阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

MySQL innodb_thread_concurrency相关参数理解

210次阅读
没有评论

共计 10364 个字符,预计需要花费 26 分钟才能阅读完成。

虽然这几个参数我以前也有学习过,但是一直没有在源码级别进行证明,所以一直也没有写,但是今天群里有朋友问到,所以先按照官方手册的加上我自己的理解进行一下解释,以后一定要在源码级别进行下补充

使用 MySQL 版本:5.7.14

OS 平台:CentOS release 6.5 (Final) 64bit

一、理论基础

首先要理解几个参数我们必须要先知道下面的内容,注意下面内容并不深入,而且只是我自己的理解

1、什么是多线程

实际上 MySQL 中的多线程就是 POSIX 那一套,比如也就是我们说的 pthread 族函数比如 pthread_create、pthread_join、pthread_mutex_lock 等等,相信有多线程编程基础的朋友不会陌生,线程也叫轻量级进程 (LWP) 那么多线程有什么好处,相对于进程而言多线程共享了很多东西比如

1. 文件描述符表

2. 每种信号的处理方式

3. 当前工作目录

4. 用户 ID 和组 ID

5. 除栈以外的内存空间

其实我们在编程的时候多线程通信都是通过非栈以外的内存进程的,比如堆空间,既然线程能够共享这麽多资源,不管是线程的创建、上下文切换、线程间通信都变得方便了(注意共享是方便了但是对临界区的管理需要使用类似 mutex rwlock 之类的锁来实现)。接下来我们就要来讲讲线程间上下文切换

同时要记住一点线程是 CPU 调度的最小单位、进程是资源分配的最小单位。配上一张图

MySQL innodb_thread_concurrency 相关参数理解

2、线程的上下文切换
 我们知道 LINUX 是一个多批道多用户分时操作系统,它允许多个任务同时进入内存 CPU 通过时间轮片的方式进行调度,我们举例如果我有 2 核的 CPU,但是我当前有 4 个同等优先级的 MYSQL 线程进入了就绪队列,那么我们同一时刻能够并行(注意用词的准确性不是并发是并行) 执行的 MYSQL 线程其实是 2 个,另外 2 个呢?当然就处于就绪队列,等待获得 CPU 时间来完成工作,等到正在执行的 2 个线程时间轮片用完以后这个时候需要保留处理器现场,其实就是保存寄存器的值到内存,然后放弃 CPU,进入就绪态,这个时候在就绪队列的 2 个线程可以进入 CPU 进行工作了,这种 4 个线程并发执行但是只有 2 个线程获得时间轮片并行执行 (获得 CPU 轮片) 在这种不断需要获得 CPU 轮片 –>> 工作 –>> 保存寄存器值到内存 –>> 放弃 CPU 轮片的方式中我们将保存寄存器值到内存这种动作叫做线程上下文切换,这是有一定代价的,当然 我的理解也许很片面因为我毕竟不是搞 LINUX 内核的。如果同时需要并发执行的线程越多这种上下文切换的频率就越大,这也是为什么我们在 LINUX 负载高的时候能够观察到更多上下文切换的原因 (vmstat 就可以看到),那么我们说如果限制同一时刻并发执行的线程数上下文切换将会减少,某种意义说就是长痛不如短痛,与其让你不断的进行上文切换还不如把你处于睡眠态放弃 CPU 使用权, 这里简单说一下线程的缺点:
 线程不稳定(库函数实现)
 线程调试比较困难(gdb 支持不好)
 信号使用非常困难
3、小事物线程饥饿问题
 如果有过多线程编程使用过 MUTEX,这种抢占试锁的朋友,一定不会忘记在某个线程释放 MUTEX 后,其他线程会以抢占的方式来获得,某些线程可能运气不好老是抢不到,如果换成 同优先级线程之间,OS 在调度的时候如果不均衡,那么某些可能任务量小的线程老是得不到 CPU 轮片,而大任务线程老是获得 CPU 轮片,这依赖于 OS 的线程调度策略,这样就可能形成小任务线程饥饿问题,与其依赖 OS 的调度策略不如自己设置一种规则,让用到了一定时间轮片的线程先处于睡眠态放弃 CPU 的使用。
 二、参数解释
 好了有了上面的理论知识可以进行这几个参数的解释了
 其实这三个参数就是来解决上面的问题
1、innodb_thread_concurrency
同一时刻能够进入 innodb 层次并发执行的线程数 (注意是并发不是并行),如果超过 CPU 核数,某些线程可能处于就绪态而没有获得 CPU 时间轮片,如果 SERVER 层的线程大于这个值,对不起多余的线程将会被放到一个叫做 wait queue 的队列中,而不能进入 INNODB 层次,进不到 innodb 层当然也就不能干活了,谈不上获得 CPU。既然是一个队列那么它必然满足先进入先出的原则。这也是前面说的长痛不如短痛,与其让你不断的进行上文切换还不如把你处于睡眠态放弃 CPU 使用权,默认这个值是 0,代表不限制。
2、innodb_concurrency_tickets
这个参数设置为一种 tickets, 默认是 5000,我也不清楚到底它代表多久,从官方文档来看它和事物处理的行数有关,大事物需要处理的行数自然更多,小事物当然也就越少至少我们可以想成获得 CPU 的时间,干活期间他会不断减少,如果减少到 0,这个线程将被提出 innodb 层次,进入前面说的等待队列,当然也就在队尾部了,这里假设有一个小的事物正在排队进入 innodb 层,又或者它已经进入了 innodb 层没有获得 CPU 时间轮片,突然一个大的事物 tickets 耗尽被提出了 innodb 层,那么这个小事物就自然而然能够获得 CPU 轮片干活,而小事物执行非常快,执行完成后
 另外的事物又能尽快的获得 CPU 干活,不会由于 OS 线程调度不均匀的问题而造成的小事物饥饿问题,这很好理解。也就是前面我说的与其依赖 OS 的调度策略不如自己设置一种规则,让用到了一定时间轮片的线程先处于睡眠态放弃 CPU 的使用。
3、innodb_thread_sleep_delay
这个参数从官方手册来看,是代表当事物被踢出 innodb 层次后自己睡眠的时间,等待睡眠完成后再次进入 wait que 队列 5.6.3 以后可以设置 innodb_adaptive_max_sleep_delay,来自动调整 innodb_thread_sleep_delay,这就更为方便,因为这个值很难讲多少合适,其单位是 microseconds,从某种意义上来讲这个值加剧了大事物执行的时间,小事物也就更加容易进入 INNODB 层次获得 CPU 时间来干活。

 关于这几个值如果一旦 innodb_thread_concurrency 设置为 0,其他值的设置均没有效果,这很好理解,设置为 0 后表示不限制,如果不限制也就谈不上等待队列,没有等待队列睡眠多久进入等待队列自然没有意义。
 如果设置为 0 后 show engine status 的下面值始终为 0
 0 queries inside InnoDB, 0 queries in queue

这里配上一张自己根据理解画的图:

MySQL innodb_thread_concurrency 相关参数理解

 下面是官方对于 innodb_thread_concurrency 的一个建议设置值:
? If the number of concurrent user threads for a workload is less than 64, set
 innodb_thread_concurrency=0.
 ? If your workload is consistently heavy or occasionally spikes, start by setting
 innodb_thread_concurrency=128, and lowering the value to 96, 80, 64, and so on, until you
 find the number of threads that provides the best performance. For example, suppose your system
 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 above this number. In
 this case, you would set innodb_thread_concurrency=80 to avoid impacting performance.
 ? If you do not want InnoDBto use more than a certain number of vCPUs for user threads (20 vCPUs
 for example), set innodb_thread_concurrency to this number (or possibly lower, depending
 on performance results). If your goal is to isolate MySQL from other applications, you may consider
 binding the mysqldprocess exclusively to the vCPUs. Be aware, however, that exclusive binding
 could result in non-optimal hardware usage if the mysqldprocess is not consistently busy. In this
 case, you might bind the mysqldprocess to the vCPUs but also allow other applications to use some
 or all of the vCPUs.

至少我们知道如果要设置 innodb_thread_concurrency 不应该高于 CPU 核数很多,比如我们可以设置 1.5 倍 *CPU 核数。
 关于这一块也可以参考 MYSQL 官方手册
Section 15.4.6,“Configuring Thread Concurrency for InnoDB”.

三、如何观察
 现在知道的观察方式主要是 show engine innodb status 和 innodb_trx,其事物状态会为
sleeping before entering InnoDB

为了更好的观察我这里设置如下:
+—————————+——-+
 | Variable_name            | Value |
 +—————————+——-+
 | innodb_thread_concurrency | 1    |
 +—————————+——-+
 +—————————-+——-+
 | Variable_name              | Value |
 +—————————-+——-+
 | innodb_concurrency_tickets | 10    |
 +—————————-+——-+
言外之意我将同一时刻能够进入 innodb 干活的线程数设置了 1,同时 tickets 设置为了 10 来尽可能的观察到这种不断进入 innodb
层次,然后 tickets 到被提出 innodb 层次的现象,随后我做了 2 个大事物,好了我在 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
 ——–
注意这里的 sleeping before entering InnoDB
然后可以观察到
1 queries inside InnoDB, 1 queries in queue
这里也明显的说了 1 个线程在 innodb 里面另外一个在等待队列

 在 innodb_trx 中能够观察到:

时间 A:
mysql> select trx_id,trx_state,trx_query,trx_operation_state,trx_concurrency_tickets from information_schema.innodb_trx \G
 *************************** 1. row ***************************
                  trx_id: 162612
              trx_state: RUNNING
              trx_query: insert into testti3 select * from testti3
    trx_operation_state: sleeping before entering InnoDB             
 trx_concurrency_tickets: 0
 *************************** 2. row ***************************
                  trx_id: 422212176322720
              trx_state: RUNNING
              trx_query: insert into testui select * from testui
    trx_operation_state: fetching rows
 trx_concurrency_tickets: 2
 2 rows in set (0.01 sec)

时间 B:

mysql> select trx_id,trx_state,trx_query,trx_operation_state,trx_concurrency_tickets from information_schema.innodb_trx \G
 *************************** 1. row ***************************
                  trx_id: 162612
              trx_state: RUNNING
              trx_query: insert into testti3 select * from testti3
    trx_operation_state: NULL
 trx_concurrency_tickets: 10
 *************************** 2. row ***************************
                  trx_id: 422212176322720
              trx_state: RUNNING
              trx_query: insert into testui select * from testui
    trx_operation_state: sleeping before entering InnoDB
 trx_concurrency_tickets: 0
 2 rows in set (0.32 sec)
从 trx_operation_state 中可以看到他们不断的在进行轮换的进入的 innodb 层次,同时我们还能看到活跃事物 trx_concurrency_tickets 这个 tickets 不断的减少,而处于 sleeping before entering InnoDB 的事物其 trx_concurrency_tickets 为 0。

 四、事物等待进入 innodb 层堆栈
 虽然没有研究源码但是还是将堆栈打出来,方便以后研究
#0  0x0000003ca620ef3d in nanosleep () from /lib64/libpthread.so.0
 #1  0x0000000001a80c73 in os_thread_sleep (tm=1026) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/os/os0thread.cc:278
 #2  0x0000000001b74e81 in srv_conc_enter_innodb_with_atomics (trx=0x7fffeeca15d0) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/srv/srv0conc.cc:214
 #3  0x0000000001b74fcb in srv_conc_enter_innodb (prebuilt=0x7fffb41b7110) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/srv/srv0conc.cc:259
 #4  0x000000000199c8c8 in innobase_srv_conc_enter_innodb (prebuilt=0x7fffb41b7110)
    at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:1671
 #5  0x00000000019a856d in ha_innobase::write_row (this=0x7fffb41b6b60, record=0x7fffb41af0d0 “\375\001”)
    at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:7920
 #6  0x0000000000f72e73 in handler::ha_write_row (this=0x7fffb41b6b60, buf=0x7fffb41af0d0 “\375\001”) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/handler.cc:8228
 #7  0x00000000017d0c10 in write_record (thd=0x7fffb402eb20, table=0x7fffb41b61b0, info=0x7fffb40283f0, update=0x7fffb4028468)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_insert.cc:1864
 #8  0x00000000017d2117 in Query_result_insert::send_data (this=0x7fffb40283a8, values=…) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_insert.cc:2262
 #9  0x000000000155f954 in end_send (join=0x7fffb40286d0, qep_tab=0x7fffb41e4948, end_of_records=false)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:2919
 #10 0x000000000155c515 in evaluate_join_record (join=0x7fffb40286d0, qep_tab=0x7fffb41e47d0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:1639
 #11 0x00000000015646b7 in QEP_tmp_table::end_send (this=0x7fffb4028ad0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:4710
 #12 0x000000000155b508 in sub_select_op (join=0x7fffb40286d0, qep_tab=0x7fffb41e47d0, end_of_records=true)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:1063
 #13 0x000000000155b640 in sub_select (join=0x7fffb40286d0, qep_tab=0x7fffb41e4658, end_of_records=true)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:1220
 #14 0x000000000155b1ba in do_select (join=0x7fffb40286d0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:946
 #15 0x0000000001559060 in JOIN::exec (this=0x7fffb40286d0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:199
 #16 0x00000000015f932a in handle_query (thd=0x7fffb402eb20, lex=0x7fffb4031100, result=0x7fffb40283a8, added_options=1342177280, removed_options=0)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_select.cc:184
 #17 0x00000000017d4d5f in Sql_cmd_insert_select::execute (this=0x7fffb4028330, thd=0x7fffb402eb20) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_insert.cc:3199
 #18 0x00000000015a6bea in mysql_execute_command (thd=0x7fffb402eb20, first_level=true) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:3719
 #19 0x00000000015ad15a in mysql_parse (thd=0x7fffb402eb20, parser_state=0x7fffec12c600) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:5836
 #20 0x00000000015a1019 in dispatch_command (thd=0x7fffb402eb20, com_data=0x7fffec12cd70, command=COM_QUERY)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:1447
 #21 0x000000000159fe4a in do_command (thd=0x7fffb402eb20) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_parse.cc:1010
 #22 0x00000000016e1d9c in handle_connection (arg=0x3a06b60) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/conn_handler/connection_handler_per_thread.cc:312
 #23 0x0000000001d72180 in pfs_spawn_thread (arg=0x413d3d0) at /root/mysql5.7.14/percona-server-5.7.14-7/storage/perfschema/pfs.cc:2188
 #24 0x0000003ca62079d1 in start_thread () from /lib64/libpthread.so.0
 #25 0x0000003ca5ee8b6d in clone () from /lib64/libc.so.6

本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-06/144814.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计10364字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中