MYSQL INNODB innodb_thread_concurrency相关参数理解

原创水平有限请谅解

虽然这几个参数我以前也有学习过,但是一直没有在源码级别进行证明,所以一直也没有写,但是今天群里有
朋友问到,所以先按照官方手册的加上我自己的理解进行一下解释,以后一定要在源码级别进行下补充
使用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调度的最小单位、进程是资源分配的最小单位。配上一张图

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

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

下面是官方对于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

作者微信:

               

时间: 2024-10-07 18:02:16

MYSQL INNODB innodb_thread_concurrency相关参数理解的相关文章

MySQL InnoDB 常用优化参数

内存相关 innodb_buffer_pool_size 缓冲池,会缓冲索引页.数据页.undo页.插入缓冲.自适应哈希索引.innodb存储的锁信息.数字字典信息等 innodb_buffer_pool_instances 允许多个缓冲池实例,每页根据哈希平均分配到不同缓冲池实例中,减少数据库内部资源竞争,增加数据库并发处理能力 innodb_old_blocks_pct 确定modpoint位置,默认37,modpoint指新读取到的页放入LRU(最近最少使用算法)列表中的位置,modpoi

MySQL InnoDB 共享表空间和独立表空间

MySQL  InnoDB 共享表空间和独立表空间 官网:https://dev.mysql.com/doc/refman/5.6/en/innodb-multiple-tablespaces.html 前言:学习mysql的时候总是习惯性的和oracle数据库进行比较.在学习mysql InnoDB的存储结构的时候也免不了跟oracle进行比较.Oracle的数据存储有表空间.段.区.块.数据文件:mysql InnoDB的存储管理也类似,但是mysql增加了一个共享表空间和独立表空间的概念:

巧用MySQL InnoDB引擎锁机制解决死锁问题

最近,在项目开发过程中,碰到了数据库死锁问题,在解决问题的过程中,笔者对MySQL InnoDB引擎锁机制的理解逐步加深. 案例如下: 在使用Show innodb status检查引擎状态时,发现了死锁问题: *** (1) TRANSACTION: TRANSACTION 0 677833455, ACTIVE 0 sec, process no 11393, OS thread id 278546 starting index read mysql tables in use 1, loc

mysql中innodb_thread_concurrency的作用与改良

innodb_thread_concurrency如果参数设置大于0,则表示检查机制开启,允许进入的线程数就是参数的值. 在新的MySQL线程调用Innodb接口前,Innodb会检查已经接受的请求线程数: 如已经超过innodb_thread_concurrency设置的限制,则该请求线程会等待innodb_thread_sleep_delay微秒后尝试重新请求,如果第二次请求还是无法获得,则该线程会进入FIFO队列休眠.重试两次的机制是为了减少CPU的上下文切换的次数,以降低CPU消耗. 如

MySQL配置文件my.ini参数注释说明

mysqld程序–目录和文件 basedir = path 使用给定目录作为根目录(安装目录). character-sets-dir = path 给出存放着字符集的目录. datadir = path 从给定目录读取数据库文件. pid-file = filename 为mysqld程序指定一个存放进程ID的文件(仅适用于UNIX/Linux系统); Init-V脚本需要使用这个文件里的进程ID结束mysqld进程. socket = filename 为MySQL客户程序与服务器之间的本地

RDS for MySQL InnoDB 行锁等待和锁等待超时的处理

RDS for MySQL InnoDB 行锁等待和锁等待超时的处理   1. InnoDB 引擎表行锁等待和等待超时发生的场景 2.InnoDB 引擎行锁等待情况的处理 2.1 InnoDB 行锁等待超时参数 innodb_lock_wait_timeout 2.2 大量行锁等待和行锁等待超时的处理 1. InnoDB 引擎表行锁等待和等待超时发生的场景 当一个 RDS for MySQL 连接会话等待另外一个会话持有的互斥行锁时,会发生 InnoDB 引擎表行锁等待情况. 通常情况下,持有该

MySQL Innodb日志机制深入分析

                            MySQL Innodb日志机制深入分析   1.1. Log & Checkpoint Innodb的事务日志是指Redo log,简称Log,保存在日志文件ib_logfile*里面.Innodb还有另外一个日志Undo log,但Undo log是存放在共享表空间里面的(ibdata*文件).   由于Log和Checkpoint紧密相关,因此将这两部分合在一起分析. 名词解释:LSN,日志序列号,Innodb的日志序列号是一个64位

Mysql数据库主从复制相关介绍

公司使用master-slave架构,就具体学习了下,这里记录下相关内容. Replication的好处 读写分离:主数据库负责写和update数据,从数据库负责读操作 数据安全:由于数据复制到从库,并且可以对从库的复制进程进行停止操作,所以可以在从库上进行备份服务而不需要对主服务器进行中断操作 可分析:在线数据可以在master数据库上创建,然后在slave数据库上进行分析,且不会对master数据库造成性能影响 长数据:如果一个分支机构需要一份主要数据的copy,可以使用复制来创建一份本地备

mysql配置模板(my-*.cnf)参数详细说明_Mysql

mysql安装成功后有几个默认的配置模板,列表如下:my-huge.cnf : 用于高端产品服务器,包括1到2GB RAM,主要运行mysqlmy-innodb-heavy-4G.ini : 用于只有innodb的安装,最多有4GB RAM,支持大的查询和低流量my-large.cnf : 用于中等规模的产品服务器,包括大约512M RAMmy-medium.cnf : 用于低端产品服务器,包括很少内存(少于128M)my-small.cnf : 用于最低设备的服务器,只有一点内存(少于512M