MYSQL INNODB innodb_thread_concurrency相关参数理解

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 原创水平有限请谅解 虽然这几个参数我以前也有学习过,但是一直没有在源码级别进行证明,所以一直也没有写,但是今天群里有 朋友问到,所以先按照官方手册的加上我自己的理解进行一下解释,以后一定要在源码级别进行下补充 使用MYSQL版本:5.

原创水平有限请谅解

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

作者微信:

               
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7天前
|
存储 关系型数据库 MySQL
Mysql索引:深入理解InnoDb聚集索引与MyisAm非聚集索引
通过本文的介绍,希望您能深入理解InnoDB聚集索引与MyISAM非聚集索引的概念、结构和应用场景,从而在实际工作中灵活运用这些知识,优化数据库性能。
46 7
|
1月前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
本文介绍了MySQL InnoDB存储引擎中的数据文件和重做日志文件。数据文件包括`.ibd`和`ibdata`文件,用于存放InnoDB数据和索引。重做日志文件(redo log)确保数据的可靠性和事务的持久性,其大小和路径可由相关参数配置。文章还提供了视频讲解和示例代码。
135 11
【赵渝强老师】MySQL InnoDB的数据文件与重做日志文件
|
13天前
|
存储 关系型数据库 MySQL
MySQL引擎InnoDB和MyISAM的区别?
InnoDB是MySQL默认的事务型存储引擎,支持事务、行级锁、MVCC、在线热备份等特性,主索引为聚簇索引,适用于高并发、高可靠性的场景。MyISAM设计简单,支持压缩表、空间索引,但不支持事务和行级锁,适合读多写少、不要求事务的场景。
42 9
|
14天前
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
26天前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的参数文件
MySQL启动时会读取配置文件my.cnf来确定数据库文件位置及初始化参数。该文件分为Server和Client两部分,包含动态与静态参数。动态参数可在运行中通过命令修改,而静态参数需修改my.cnf并重启服务生效。文中还提供了相关代码示例和视频教程。
|
1月前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的表空间
InnoDB是MySQL默认的存储引擎,主要由存储结构、内存结构和线程结构组成。其存储结构分为逻辑和物理两部分,逻辑存储结构包括表空间、段、区和页。表空间是InnoDB逻辑结构的最高层,所有数据都存放在其中。默认情况下,InnoDB有一个共享表空间ibdata1,用于存放撤销信息、系统事务信息等。启用参数`innodb_file_per_table`后,每张表的数据可以单独存放在一个表空间内,但撤销信息等仍存放在共享表空间中。
|
1月前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL InnoDB的段、区和页
MySQL的InnoDB存储引擎逻辑存储结构与Oracle相似,包括表空间、段、区和页。表空间由段和页组成,段包括数据段、索引段等。区是1MB的连续空间,页是16KB的最小物理存储单位。InnoDB是面向行的存储引擎,每个页最多可存放7992行记录。
|
1月前
|
存储 Oracle 关系型数据库
【赵渝强老师】MySQL的InnoDB存储引擎
InnoDB是MySQL的默认存储引擎,广泛应用于互联网公司。它支持事务、行级锁、外键和高效处理大量数据。InnoDB的主要特性包括解决不可重复读和幻读问题、高并发度、B+树索引等。其存储结构分为逻辑和物理两部分,内存结构类似Oracle的SGA和PGA,线程结构包括主线程、I/O线程和其他辅助线程。
【赵渝强老师】MySQL的InnoDB存储引擎
|
1月前
|
缓存 监控 关系型数据库
如何根据监控结果调整 MySQL 数据库的参数以提高性能?
【10月更文挑战第28天】根据MySQL数据库的监控结果来调整参数以提高性能,需要综合考虑多个方面的因素
78 1
|
3月前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
605 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)