一、问题来源

这是一个朋友问我的典型案例。整个故障现象表现为,MySQL 数据库频繁的出现大量的请求不能响应。下面是一些他提供的证据:

1. show processlist从状态信息来看出现如下情况:insert 操作:状态为 update

update/delete 操作:状态为 updating

select 操作:状态为 sending data

因此可以推断应该是语句执行期间出现了问题,由于篇幅原因只给出一部分,并且我将语句部分也做了相应截断:show processlist----------------------------......11827639    root    dbmis   Execute 9   updating    UPDATE17224594    root    dbmis   Execute 8   Sending data    SELECT sum(exchange_coin) as exchange_coin FROM17224595    root    dbmis   Execute 8   update  INSERT INTO17224596    root    dg  Execute 8   update  INSERT INTO17224597    root    dbmis   Execute 8   update  INSERT INTO17224598    root    dbmis   Execute 7   update  INSERT INTO17224599    root    dbmis   Execute 7   Sending data    SELECT COUNT(*) AS tp_count FROM17224600    root    dg  Execute 7   update  INSERT INTO17224601    root    dbmis   Execute 6   update  INSERT INTO17224602    root    dbmis   Execute 6   Sending data    SELECT sum(exchange_coin) as exchange_coin FROM17224606    root    dbmis   Execute 5   update  INSERT INTO17224619    root    dbmis   Execute 2   update  INSERT INTO17224620    root    dbmis   Execute 2   update  INSERT INTO17224621    root    dbmis   Execute 2   Sending data    SELECT sum(exchange_coin) as exchange_coin17224622    root    dg  Execute 2   update  INSERT INTO17224623    root    dbmis   Execute 1   update  INSERT INTO17224624    root    dbmis   Execute 1   update  INSERT INTO17224625    root    dg  Execute 1   update  INSERT INTO17224626    root    dbmis   Execute 0   update  INSERT INTO

2. 系统 IO/CPU

从 vmstat 来看,CPU 使用不大,而 IO 也在可以接受的范围内(vmstat wa% 不高且 b 列为 0 )如下:vmstat--------------------------------------

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----

r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st

2  0 927300 3057100      0 53487316    0    0     5   192    0    0  3  1 96  0  0

iostat--------------------------------------

Linux 3.10.0-693.el7.x86_64 (fang-data1)     09/23/2019  _x86_64_    (32 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle

2.72    0.00    0.52    0.45    0.00   96.31

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util

sdb               9.73    11.28    3.93  264.54   415.23  2624.20    22.64     0.25    0.93    3.25    0.90   0.80  21.61

sda              10.13    11.59    6.34  264.22   450.68  2624.20    22.73     0.01    0.05    2.55    1.00   0.93  25.19

sdc              11.60    11.36    5.03  263.12   453.02  2592.44    22.71     0.17    0.62    5.08    0.53   0.81  21.60

sde               0.01     0.10    0.11  160.45     6.69   920.23    11.55     0.16    1.01    1.80    1.01   0.83  13.32

sdd              11.26    11.30    2.23  263.18   412.90  2592.44    22.65     0.17    0.65   10.37    0.56   0.82  21.78

md126             0.00     0.00   11.30  468.80   164.79  5216.64    22.42     0.00    0.00    0.00    0.00   0.00   0.00

dm-0              0.00     0.00    0.11   58.80     6.69   920.23    31.47     0.15    2.56    1.96    2.56   2.16  12.74

dm-1              0.00     0.00    0.06    0.08     0.24     0.31     8.00     0.01   41.80    1.20   72.78   0.83   0.01

dm-2              0.00     0.00   11.24  408.66   164.55  5216.33    25.63     0.14    0.32    1.02    0.30   0.46  19.29

这就比较奇怪了,一般来说数据库不能及时响应请求很大可能是由于系统负载过高。如果说 DML 还可能是 Innodb 锁造成的堵塞,但是大量 sending data 状态下的 select 操作一般可能都和系统负载过高有联系,但是这里系统负载还在可以接受的范围内。

二、pstack 分析借助 pstack 查看线程的栈帧,查看 pstack 发现如下(由于篇幅限制只给出部分说明问题的部分):

1. insert 线程:Thread 85 (Thread 0x7fbb0d42b700 (LWP 20174)):#0  0x00007fbfae164c73 in select () from /lib64/libc.so.6#1  0x0000000000987c0f in os_thread_sleep (tm=) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/os/os0thread.cc:287#2  0x00000000009e4dea in srv_conc_enter_innodb_with_atomics (trx=trx@entry=0x7fba4802f9c8) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/srv/srv0conc.cc:276#3  srv_conc_enter_innodb (trx=trx@entry=0x7fba4802f9c8) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/srv/srv0conc.cc:511#4  0x000000000093b948 in innobase_srv_conc_enter_innodb (trx=0x7fba4802f9c8) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/handler/ha_innodb.cc:1280#5  ha_innobase::write_row (this=0x7fb8440ab260, record=0x7fb8440ab650 "") at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/handler/ha_innodb.cc:6793#6  0x00000000005b440f in handler::ha_write_row (this=0x7fb8440ab260, buf=0x7fb8440ab650 "") at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:7351#7  0x00000000006dd3a8 in write_record (thd=thd@entry=0x1d396c90, table=table@entry=0x7fb8440aa970, info=info@entry=0x7fbb0d429400, update=update@entry=0x7fbb0d429480) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_insert.cc:1667#8  0x00000000006e2541 in mysql_insert (thd=thd@entry=0x1d396c90, table_list=, fields=..., values_list=..., update_fields=..., update_values=..., duplic=DUP_REPLACE, ignore=false) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_insert.cc:1072#9  0x00000000006fa90a in mysql_execute_command (thd=thd@entry=0x1d396c90) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_parse.cc:3500

2. update 线程Thread 81 (Thread 0x7fbb24b67700 (LWP 27490)):#0  0x00007fbfae164c73 in select () from /lib64/libc.so.6#1  0x0000000000987c0f in os_thread_sleep (tm=) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/os/os0thread.cc:287#2  0x00000000009e4dea in srv_conc_enter_innodb_with_atomics (trx=trx@entry=0x7fb94003c608) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/srv/srv0conc.cc:276#3  srv_conc_enter_innodb (trx=trx@entry=0x7fb94003c608) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/srv/srv0conc.cc:511#4  0x000000000093ae4e in innobase_srv_conc_enter_innodb (trx=0x7fb94003c608) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/handler/ha_innodb.cc:1280#5  ha_innobase::index_read (this=0x7fb95c05b540, buf=0x7fb95c2ae4f0 "\377\377\377", key_ptr=, key_len=, find_flag=) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/handler/ha_innodb.cc:7675#6  0x00000000005ab6e0 in ha_index_read_map (find_flag=HA_READ_KEY_EXACT, keypart_map=3, key=0x7fb940017048 "7\307\017e\257h", buf=, this=0x7fb95c05b540) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:2753#7  handler::read_range_first (this=0x7fb95c05b540, start_key=, end_key=, eq_range_arg=, sorted=) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:6717#8  0x00000000005aa206 in handler::multi_range_read_next (this=0x7fb95c05b540, range_info=0x7fbb24b65240) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:5871#9  0x0000000000804acb in QUICK_RANGE_SELECT::get_next (this=0x7fb94000f720) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/opt_range.cc:10644#10 0x000000000082ae2d in rr_quick (info=0x7fbb24b65410) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/records.cc:369#11 0x0000000000766e1b in mysql_update (thd=thd@entry=0x1d1f2250, table_list=, fields=..., values=..., conds=0x7fb9400009c8, order_num=, order=, limit=18446744073709551615, handle_duplicates=DUP_ERROR, ignore=false, found_return=found_return@entry=0x7fbb24b65800, updated_return=updated_return@entry=0x7fbb24b65d60) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_update.cc:744

3. select 线程Thread 66 (Thread 0x7fbb3c355700 (LWP 16028)):#0  0x00007fbfae164c73 in select () from /lib64/libc.so.6#1  0x0000000000987c0f in os_thread_sleep (tm=) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/os/os0thread.cc:287#2  0x00000000009e4dea in srv_conc_enter_innodb_with_atomics (trx=trx@entry=0x7fb988354858) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/srv/srv0conc.cc:276#3  srv_conc_enter_innodb (trx=trx@entry=0x7fb988354858) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/srv/srv0conc.cc:511#4  0x000000000093ae4e in innobase_srv_conc_enter_innodb (trx=0x7fb988354858) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/handler/ha_innodb.cc:1280#5  ha_innobase::index_read (this=0x7fb9880e33a0, buf=0x7fb988351b50 "\377\377\377\377", key_ptr=, key_len=, find_flag=) at /home/install/lnmp1.5/src/mysql-5.6.40/storage/innobase/handler/ha_innodb.cc:7675#6  0x00000000005ab6e0 in ha_index_read_map (find_flag=HA_READ_AFTER_KEY, keypart_map=7, key=0x7fb988134a48 "", buf=, this=0x7fb9880e33a0) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:2753#7  handler::read_range_first (this=0x7fb9880e33a0, start_key=, end_key=, eq_range_arg=, sorted=) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:6717#8  0x00000000005aa206 in handler::multi_range_read_next (this=0x7fb9880e33a0, range_info=0x7fbb3c353400) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/handler.cc:5871#9  0x0000000000804acb in QUICK_RANGE_SELECT::get_next (this=0x7fb988002050) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/opt_range.cc:10644#10 0x000000000082ae2d in rr_quick (info=0x7fb98809c210) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/records.cc:369#11 0x00000000006d44fd in sub_select (join=0x7fb98809a728, join_tab=0x7fb98809c180, end_of_records=) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_executor.cc:1259#12 0x00000000006d2823 in do_select (join=0x7fb98809a728) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_executor.cc:936#13 JOIN::exec (this=0x7fb98809a728) at /home/install/lnmp1.5/src/mysql-5.6.40/sql/sql_executor.cc:194

好了有了这些栈帧视乎发现一些共同点他们都处于 innobase_srv_conc_enter_innodb 函数下,本函数正是下面参数实现的方式:innodb_thread_concurrency

innodb_concurrency_tickets

所以我随即告诉他检查这两个参数,如果设置了可以尝试取消。过后数据库故障得到解决。

三、参数和相关说明实际上涉及到的参数主要是 innodb_thread_concurrency 和 innodb_concurrency_tickets。将高压力下线程之间抢占 CPU 而造成线程上下文切换的情况尽量阻塞在 Innodb 层之外,这就需要 innodb_thread_concurrency 参数了。同时又要保证对于那些(长时间处理线程)不会长时间的堵塞(短时间处理线程),比如某些 select 操作需要查询很久,而某些 select 操作查询量很小,如果等待(长时间的 select 操作)结束后(短时间 select 操作)才执行,那么显然会出现(短时间 select 操作)饥饿问题,换句话说对(短时间 select 操作)是不公平的, 因此就引入了 innodb_concurrency_tickets 参数。

1. innodb_thread_concurrency

同一时刻能够进入 Innodb 层的会话(线程)数。如果在 Innodb 层干活的会话(线程)数量超过这个参数的设置,新会话(线程)将不能从 MySQL 层进入到 Innodb 层,它们将进入一个短暂的睡眠状态。休眠多久则通过参数 innodb_thread_sleep_delay 参数指定,如果还设置了参数 innodb_adaptive_max_sleep_delay 那么 Innodb 将会自动调整休眠时间,具体的算法实际上就在 srv_conc_enter_innodb_with_atomics 函数中,感兴趣的可以执行查看。其次这种休眠实际上是一个定时醒来的时钟,通过 ::nanosleep 或者 select(多路 IO 转接函数)进行实现,定时唤醒后会话(线程)重新判断是否可以进入 Innodb 层。函数 os_thread_sleep 部分如下:#elif defined(HAVE_NANOSLEEP)

struct timespec t;

t.tv_sec = tm / 1000000;

t.tv_nsec = (tm % 1000000) * 1000;

::nanosleep(&t, NULL);

#else

struct timeval  t;

t.tv_sec = tm / 1000000;

t.tv_usec = tm % 1000000;

select(0, NULL, NULL, NULL, &t);

关于到底如何设置这个值,官方文档有如下建议:Use the following guidelines to help find and maintain an appropriate setting:- If the number of concurrent user threads for a workload is less than 64, setinnodb_thread_concurrency=0.- If your workload is consistently heavy or occasionally spikes, start by settinginnodb_thread_concurrency=128 and then lowering the value to 96, 80, 64, and so on, untilyou find the number of threads that provides the best performance. For example, suppose yoursystem typically has 40 to 50 users, but periodically the number increases to 60, 70, or even 200.You find that performance is stable at 80 concurrent users but starts to show a regression abovethis number. In this case, you would set innodb_thread_concurrency=80 to avoid impactingperformance.- If you do not want InnoDB to use more than a certain number of virtual CPUs for user threads(20 virtual CPUs, for example), set innodb_thread_concurrency to this number (or possiblylower, depending on performance results). If your goal is to isolate MySQL from other applications,you may consider binding the mysqld process exclusively to the virtual CPUs. Be aware,however, that exclusive binding could result in non-optimal hardware usage if the mysqld processis not consistently busy. In this case, you might bind the mysqld process to the virtual CPUs butalso allow other applications to use some or all of the virtual CPUs.- innodb_thread_concurrency values that are too high can cause performance regression dueto increased contention on system internals and resources.- In some cases, the optimal innodb_thread_concurrency setting can be smaller than thenumber of virtual CPUs.- Monitor and analyze your system regularly. Changes to workload, number of users, or computingenvironment may require that you adjust the innodb_thread_concurrency setting

可以发现要合理的设置这个值并不那么容易并且要求较高。

2. innodb_concurrency_tickets实际上这里的 tickets 可以理解为 MySQL 层和 Innodb 层交互的次数,比如一个 select 一条数据就是需要 Innodb 层返回一条数据然后 MySQL 层进行 where 条件的过滤然后返回给客户端,抛开 where 条件过滤的情况,如果我们一条语句需要查询 100 条数据,那么实际上需要进入 Innodb 层 100 次,那么实际上消耗的 tickets 就是 100。当然对于 insert select 这种操作,需要的 tickets 是普通 select 的两倍,因为查询需要进入 Innodb 层一次,insert 需要再次进入 Innodb 层一次,后面我们就使用 insert select 的方式来模拟堵塞的情况,最后还会给出说明。这样我们也就理解为什么 innodb_concurrency_tickets 可以避免(长时间处理线程)长时间堵塞(短时间处理线程)的原因了。假设 innodb_concurrency_tickets 为 5000(默认值),有一个需要查询 100W 行数据的大 select 操作和一个需要查询 100 行数据的小 select 操作,大 select 操作先进行,但是当查询了 5000 行数据后将丢失CPU使用权,小 select 操作将会进行并且一次性完成。最后关于这里涉及的参数可以继续参考官方文档中的说明,我们线上并没有设置这些参数,因为感觉很难设置合适,如果设置不当反而会遇到问题,就如本案例一样。

3. 事务操作状态

实际上如果是处于这种堵塞情况,我们完全可以在 information_schema.innodb_trx 和 show engine innodb status 中看到如下:---TRANSACTION 162307, ACTIVE 133 sec sleeping before entering InnoDB (这里)

mysql tables in use 2, locked 2

767 lock struct(s), heap size 106968, 212591 row lock(s), undo log entries 15451

MySQL thread id 14, OS thread handle 140736751912704, query id 1077 localhost root Sending data

insert into testui select * from testui

---TRANSACTION 162302, ACTIVE 320 sec, thread declared inside InnoDB 1

mysql tables in use 2, locked 2

2477 lock struct(s), heap size 336344, 609049 row lock(s), undo log entries 83582

MySQL thread id 13, OS thread handle 140737153779456, query id 1050 localhost root Sending data

insert into testti3 select * from testti3

mysql> select trx_id,trx_state,trx_query,trx_operation_state,trx_concurrency_tickets from information_schema.innodb_trx \G

*************************** 1. row ***************************

trx_id: 84325

trx_state: RUNNING

trx_query: insert into  baguait4 select * from testgp

trx_operation_state: sleeping before entering InnoDB(这里)

trx_concurrency_tickets: 0

*************************** 2. row ***************************

trx_id: 84319

trx_state: RUNNING

trx_query: insert into  baguait3 select * from testgp

trx_operation_state: sleeping before entering InnoDB

trx_concurrency_tickets: 0

我们可以看到事务操作状态被标记为sleeping before entering InnoDB。但是需要注意一点的是对于只读事务比如 select 操作而言,show engine innodb status 可能看不到。但是遗憾的是案例中朋友并没有采集 trx_operation_state 的值。

四、模拟测试这里我们简单模拟,我们一共启用3个事务,其中两个 insert select 操作,一个单纯的 select 操作,当然这里的都是耗时操作,涉及的表每个表都有大概 100W 的数据。

同时为了方便观察我们需要设置参数:innodb_thread_concurrency=1

innodb_concurrency_tickets=10

操作步骤如下:

如果多观察几次你可以看到如下的现象:mysql> select trx_id,trx_state,trx_query,trx_operation_state,trx_concurrency_tickets from information_schema.innodb_trx \G show processlist;

*************************** 1. row ***************************

trx_id: 84529

trx_state: RUNNING

trx_query: insert into  baguait4 select * from testgp

trx_operation_state: sleeping before entering InnoDB

trx_concurrency_tickets: 0

*************************** 2. row ***************************

trx_id: 84524

trx_state: RUNNING

trx_query: insert into  baguait3 select * from testgp

trx_operation_state: inserting

trx_concurrency_tickets: 1

*************************** 3. row ***************************

trx_id: 422211785606640

trx_state: RUNNING

trx_query: select * from baguait1

trx_operation_state: sleeping before entering InnoDB

trx_concurrency_tickets: 0

3 rows in set (0.00 sec)

+----+-----------------+-----------+---------+---------+------+------------------------+--------------------------------------------+-----------+---------------+

| Id | User            | Host      | db      | Command | Time | State                  | Info                                       | Rows_sent | Rows_examined |

+----+-----------------+-----------+---------+---------+------+------------------------+--------------------------------------------+-----------+---------------+

|  1 | event_scheduler | localhost | NULL    | Daemon  | 3173 | Waiting on empty queue | NULL                                       |         0 |             0 |

|  6 | root            | localhost | testmts | Query   |   70 | Sending data           | insert into  baguait3 select * from testgp |         0 |             0 |

|  7 | root            | localhost | testmts | Query   |   68 | Sending data           | insert into  baguait4 select * from testgp |         0 |             0 |

|  8 | root            | localhost | testmts | Query   |   66 | Sending data           | select * from baguait1                     |    120835 |             0 |

|  9 | root            | localhost | NULL    | Query   |    0 | starting               | show processlist                           |         0 |             0 |

+----+-----------------+-----------+---------+---------+------+------------------------+--------------------------------------------+-----------+---------------+

5 rows in set (0.00 sec)

mysql>

mysql>

mysql>

mysql>

mysql> select trx_id,trx_state,trx_query,trx_operation_state,trx_concurrency_tickets from information_schema.innodb_trx \G show processlist;

*************************** 1. row ***************************

trx_id: 84529

trx_state: RUNNING

trx_query: insert into  baguait4 select * from testgp

trx_operation_state: sleeping before entering InnoDB

trx_concurrency_tickets: 0

*************************** 2. row ***************************

trx_id: 84524

trx_state: RUNNING

trx_query: insert into  baguait3 select * from testgp

trx_operation_state: sleeping before entering InnoDB

trx_concurrency_tickets: 0

*************************** 3. row ***************************

trx_id: 422211785606640

trx_state: RUNNING

trx_query: select * from baguait1

trx_operation_state: fetching rows

trx_concurrency_tickets: 3

3 rows in set (0.00 sec)

+----+-----------------+-----------+---------+---------+------+------------------------+--------------------------------------------+-----------+---------------+

| Id | User            | Host      | db      | Command | Time | State                  | Info                                       | Rows_sent | Rows_examined |

+----+-----------------+-----------+---------+---------+------+------------------------+--------------------------------------------+-----------+---------------+

|  1 | event_scheduler | localhost | NULL    | Daemon  | 3177 | Waiting on empty queue | NULL                                       |         0 |             0 |

|  6 | root            | localhost | testmts | Query   |   74 | Sending data           | insert into  baguait3 select * from testgp |         0 |             0 |

|  7 | root            | localhost | testmts | Query   |   72 | Sending data           | insert into  baguait4 select * from testgp |         0 |             0 |

|  8 | root            | localhost | testmts | Query   |   70 | Sending data           | select * from baguait1                     |    128718 |             0 |

|  9 | root            | localhost | NULL    | Query   |    0 | starting               | show processlist                           |         0 |             0 |

+----+-----------------+-----------+---------+---------+------+------------------------+--------------------------------------------+-----------+---------------+

5 rows in set (0.00 sec)

我们可以观察到 trx_operation_state 的状态 3 个操作都在交替的变化,但是总有 2 个处于sleeping before entering InnoDB状态。并且我们可以观察到 trx_concurrency_tickets 总是不会大于 10 的。因此我们有理由相信在同一时刻只有一个操作进入了 Innodb 层。但是需要注意的是在 show engine innodb status 中观察不到 select 的操作如下:------------TRANSACTIONS------------Trx id counter 84538Purge done for trx's n:o = 84529, sees = 84524, sees

但是我们还需要注意 show engine innodb status 有如下输出第一行说明了有 2 个会话(线程)堵塞在 Innodb 层以外。--------------ROW OPERATIONS--------------1 queries inside InnoDB, 2 queries in queue3 read views open inside InnoDB2 RW transactions active inside InnoDB

五、实现方法前面我们已经描述了每次 MySQL 层和 Innodb 层的交互都会进行一次这样的判断,它用来决定会话(线程)是否能够进入 Innodb 层,下面就是大概的逻辑,由函数 innobase_srv_conc_enter_innodb 调入。->是否设置了参数innodb_thread_concurrency  ->是     ->是否tickets大于0        ->是、直接进入Innodb层并且tickets减1        ->否、调入函数srv_conc_enter_innodb           ->调入函数srv_conc_enter_innodb_with_atomics              ->开启死循环                 ->是否活跃线程数小于innodb_thread_concurrency设置                    ->是、增加活跃线程数,并且自动调整delay参数,退出死循环,满tickets进入Innodb层                    ->否、自动调整delay参数后设置事务操作状态为"sleeping before entering InnoDB",然后进入休眠状态直到时间达到后重新醒来继续循环  ->否、直接进入Innodb层

我们可以看到这个实现方式,在 Inndob 以外的会话(线程)会一直等待直到 Inndob 层内活跃的线程数小于 innodb_thread_concurrency 为止,并且每次进入 Innodb 层都会将 tickets 减 1。

其他:关于 insert select 操作消耗 tickets 的说明这里额外说明一下,因为我在测试的时候看了一下,对于一行数据而言首先需要 select 查询出来然后再 insert 插入到表中,这里实际上一行数据涉及到进入 Innodb 层两次,那么就需要消耗 2 个 tickets,下面留下两个栈帧供自己后面参考:

1. insert select 查询数据进入 Innodb 层#0  innobase_srv_conc_enter_innodb (prebuilt=0x7ffedcb98d10) at /mysqldata/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:1740#1  0x0000000001a53f7c in ha_innobase::general_fetch (this=0x7ffedcb9d760, buf=0x7ffedc9469b0 "\375\n", direction=1, match_mode=0)    at /mysqldata/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:9846#2  0x0000000001a545ee in ha_innobase::rnd_next (this=0x7ffedcb9d760, buf=0x7ffedc9469b0 "\375\n")    at /mysqldata/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:10083#3  0x0000000000f836d6 in handler::ha_rnd_next (this=0x7ffedcb9d760, buf=0x7ffedc9469b0 "\375\n") at /mysqldata/percona-server-locks-detail-5.7.22/sql/handler.cc:3146#4  0x00000000014e2a55 in rr_sequential (info=0x7ffedcb4f120) at /mysqldata/percona-server-locks-detail-5.7.22/sql/records.cc:521#5  0x0000000001581277 in sub_select (join=0x7ffedcb4ea20, qep_tab=0x7ffedcb4f0d0, end_of_records=false)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1280#6  0x0000000001580be6 in do_select (join=0x7ffedcb4ea20) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:950#7  0x000000000157eaa2 in JOIN::exec (this=0x7ffedcb4ea20) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:199#8  0x0000000001620327 in handle_query (thd=0x7ffedc012960, lex=0x7ffedc014f90, result=0x7ffedcc46680, added_options=1342177280, removed_options=0)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_select.cc:185#9  0x000000000180466d in Sql_cmd_insert_select::execute (this=0x7ffedcc46608, thd=0x7ffedc012960)

2. insert select 插入数据进入 Innodb 层#0  innobase_srv_conc_enter_innodb (prebuilt=0x7ffedcb9c6f0) at /mysqldata/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:1740#1  0x0000000001a50587 in ha_innobase::write_row (this=0x7ffedc946470, record=0x7ffedcb78d00 "\375\n")    at /mysqldata/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:8341#2  0x0000000000f9041d in handler::ha_write_row (this=0x7ffedc946470, buf=0x7ffedcb78d00 "\375\n") at /mysqldata/percona-server-locks-detail-5.7.22/sql/handler.cc:8466#3  0x00000000018004b9 in write_record (thd=0x7ffedc012960, table=0x7ffedcb8f940, info=0x7ffedcc466c8, update=0x7ffedcc46740)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_insert.cc:1881#4  0x00000000018019b9 in Query_result_insert::send_data (this=0x7ffedcc46680, values=...) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_insert.cc:2279#5  0x00000000015853a8 in end_send (join=0x7ffedcb4ea20, qep_tab=0x7ffedcb4f248, end_of_records=false)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:2925#6  0x0000000001581f71 in evaluate_join_record (join=0x7ffedcb4ea20, qep_tab=0x7ffedcb4f0d0) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1645#7  0x0000000001581372 in sub_select (join=0x7ffedcb4ea20, qep_tab=0x7ffedcb4f0d0, end_of_records=false)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1297#8  0x0000000001580be6 in do_select (join=0x7ffedcb4ea20) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:950#9  0x000000000157eaa2 in JOIN::exec (this=0x7ffedcb4ea20) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:199#10 0x0000000001620327 in handle_query (thd=0x7ffedc012960, lex=0x7ffedc014f90, result=0x7ffedcc46680, added_options=1342177280, removed_options=0)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_select.cc:185#11 0x000000000180466d in Sql_cmd_insert_select::execute (this=0x7ffedcc46608, thd=0x7ffedc012960)

实际上插入数据正是在查询完数据后调用函数 evaluate_join_record 的时候,通过回调了函数 Query_result_insert::send_data 来实现,这点和单纯的 select 不一样单纯的 select 这里调入是函数 Query_result_send::send_data 如下:#0  Query_result_send::send_data (this=0x7ffedcc465f8, items=...) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_class.cc:2915#1  0x00000000015853a8 in end_send (join=0x7ffedcb4e930, qep_tab=0x7ffedcb4f4b0, end_of_records=false)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:2925#2  0x0000000001581f71 in evaluate_join_record (join=0x7ffedcb4e930, qep_tab=0x7ffedcb4f338) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1645#3  0x0000000001581372 in sub_select (join=0x7ffedcb4e930, qep_tab=0x7ffedcb4f338, end_of_records=false)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:1297#4  0x0000000001580be6 in do_select (join=0x7ffedcb4e930) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:950#5  0x000000000157eaa2 in JOIN::exec (this=0x7ffedcb4e930) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_executor.cc:199#6  0x0000000001620327 in handle_query (thd=0x7ffedc012960, lex=0x7ffedc014f90, result=0x7ffedcc465f8, added_options=0, removed_options=0)    at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_select.cc:185#7  0x00000000015d1f77 in execute_sqlcom_select (thd=0x7ffedc012960, all_tables=0x7ffedcc45cf0) at /mysqldata/percona-server-locks-detail-5.7.22/sql/sql_parse.cc:5445

最后推荐高鹏的专栏《深入理解 MySQL 主从原理 32 讲》,想要透彻了解学习 MySQL 主从原理的朋友不容错过。

mysql 1280_技术分享 | MySQL 一次奇怪的故障分析相关推荐

  1. mysql relay_技术分享 | MySQL:产生大量小 relay log 的故障一例

    作者:高鹏 文章末尾有他著作的<深入理解 MySQL 主从原理 32 讲>,深入透彻理解 MySQL 主从,GTID 相关技术知识. 源码版本:5.7.22 水平有限,如有误请谅解 本文建 ...

  2. MySQL read-c_技术分享 | MySQL C API 参数 MYSQL_OPT_READ_TIMEOUT 的一些行为分析

    作者:戴岳兵 MYSQL_OPT_READ_TIMEOUT 是 MySQL c api 客户端中用来设置读取超时时间的参数.在 MySQL 的官方文档中,该参数的描述是这样的: MYSQL_OPT_R ...

  3. mysql dbuild_config_技术分享 | MySQL Test 初探

    什么是 MySQL Test? MySQL Test 是 MySQL 发行版本中集成 all-in-one 测试框架,用于做 mysql 服务的单元,回归和一致性测试,并提供了运行单元测试和创建新单元 ...

  4. mysql TMM_技术分享 | MySQL:timestamp 时区转换导致 CPU %sy 高的问题

    这个问题是一个朋友遇到的@风云,并且这位朋友已经得出了近乎正确的判断,下面进行一些描述. 一.问题展示下面是问题当时的系统负载如下: 我们可以看到 40.4%sy 正是系统调用负载较高的表现,随即朋友 ...

  5. mysql优化说出九条_技术分享 | MySQL 优化:为什么 SQL 走索引还那么慢?

    原标题:技术分享 | MySQL 优化:为什么 SQL 走索引还那么慢? 背景 2019-01-11 9:00-10:00 一个 MySQL 数据库把 CPU 打满了. 硬件配置:256G 内存,48 ...

  6. mysql数据库j电子课件,MYSQL数据库技术分享PPT演示课件

    <MYSQL数据库技术分享PPT演示课件>由会员分享,可在线阅读,更多相关<MYSQL数据库技术分享PPT演示课件(29页珍藏版)>请在人人文库网上搜索. 1.数据库技术分享, ...

  7. 技术分享 | MySQL中MGR中SECONDARY节点磁盘满,导致mysqld进程被OOM Killed

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 在MGR测试中,人为制造磁盘满问题后,节点被oom killed 问题描述 在对 ...

  8. 技术分享|MySQL caching_sha2_password认证异常问题分析

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 0. 导读 相同的账号.密码,手动客户端连接可以成功,通过MySQL Conne ...

  9. mysql 的connect 设置 无法点next_技术分享 | MySQL 使用 MariaDB 审计插件

    作者:姚远 专注于 Oracle.MySQL 数据库多年,Oracle 10G 和 12C OCM,MySQL 5.6 ,5.7,8.0 OCP.现在鼎甲科技任顾问,为同事和客户提供数据库培训和技术支 ...

最新文章

  1. 边城高级中学2021届高考成绩查询,湖南省花垣县边城高级中学2021届高三第二学期学科素养测试英语试题及答案.doc...
  2. foreach_and_函数
  3. 小程序修改数组中对象的某个值或者修改对象值
  4. 疲劳驾驶样本集_谷歌AI最新3D数据集,1.5万张动图,让AR主宰你的生活
  5. 仿小说蓝色小说网站导航引流网站源码 带手机版
  6. backbonejs mvc框架的增删查改实例
  7. C语言 · 9-1九宫格
  8. Delphi调用C++写的dll示例
  9. 想问问大家,使用qt开发的wps安装包是如何做到32位64位系统兼容的
  10. 层次分析法、网络层次分析法、模煳层次分析法、双基点法
  11. 2016清华集训滚粗记
  12. “数据源“SqlServer ”包含无法用于处理操作的 ImpersonationMode。不能与名称为“SqlServer ”的数据源进行连接。”
  13. 微信蓝牙设备开发教程之获取蓝牙设备deviceid和设备二维码(2)
  14. 【HTMLCSS】运维、后端你该会的前端基本内容
  15. UI设计师为什么要学习字体设计?
  16. python中科学计数法怎么表示_python科学计数法转换
  17. Python分析股票行情代码
  18. JavaScript声明和使用变量
  19. 51智能小车黑线寻迹(防出线)
  20. iMX6ULL RTL8723BU WiFi蓝牙模块

热门文章

  1. 如何获取字符串中某个具体的数值--通过json.load转化成字典形式获取
  2. 苹果开发账号过期不续费会怎样?
  3. WEB接口测试之Jmeter接口测试自动化 (二)(数据分离)
  4. 《Effective C#》读书笔记——条目19:保证0为值类型的有效状态.NET资源管理
  5. 1595 hdu find the longest of the shortest
  6. java 输出ab_一道在知乎很火的 Java 题——如何输出 ab【转】
  7. mysql官网二进制包_mysql二进制包安装与配置实战记录
  8. [网络安全自学篇] 五十三.Windows系统安全之Metasploit实现栈溢出攻击及反弹shell原理解析
  9. 【数据结构与算法】之深入解析“根据身高重建队列”的求解思路与算法示例
  10. 2017年第八届蓝桥杯 - 省赛 - C/C++大学A组 - G. 正则问题