前面两篇帖子分别总结了innodb_thread_concurrency和thread pool的原理:
前者是在存储引擎层面限制并发运行的线程数,代码路径过于靠后,此时query已在server层完成解析;
后者则是在server层创建多组常驻线程,用于接收客户端连接发送的query并代为执行,而不是为每个连接单独创建一个线程。
除了这两种解决方案,还可以在server层进行running thread数量判断,如果达到阈值则直接报错或sleep。
thread_running的意义
thread_running状态变量记录了当前并发执行stmt/command的数量,执行前加1执行后减1;
代码逻辑
do_command
-->dispatch_command
...
inc_thread_running
...
mysql_execute_command or execute_some_command
...
dec_thread_running
...
Thread_running突然飙高的诱因:
1客户端连接暴增;
2系统性能瓶颈,如CPU,IO或者mem swap;
3异常sql;
往往在这种情况下,MySQL server会表现出hang住的假象。
解决方案
暂时禁止新sql执行,为此引入两个阈值low_watermark和high_watermark,以及变量threads_running_ctl_mode(selects或者all );
执行query前,检查thread_running,
1若其已达high_watermark阈值则直接拒绝执行并返回错误:mysql
server is too busy
2若其位于low和high之间,则sleep 5ms,然后继续尝试,累计等待100ms后则执行
3对于已经开启事务和super用户,不做限制
4
threads_running_ctl_mode控制query类型:SELECTS/ALL,默认为SELECTS,表示只影响SELECT语句
Patch部分源码见注1
进一步改进
将低水位限流从sleep-retry优化为基于FIFO的cond-wait/signal(实现8个FIFO);
1高水位限流(这点保持不变);
2低水位优化;其他解决方案:mariadb开发thread pool,percona在其上实现了优先队列;
本patch优势:思路与thread pool一致,但代码更简洁(不到1000行);而且增加了特定query的过滤;
Patch部分代码见注2
低水位优化细节
1新增thread_active记录并发线程数,位于mysql_execute_command(sql解析之后),高水位则在query解析之前判断;
Thread_active只统计select/DML,而commit/rollback则放过。
2采用FIFO,当thread_active >=
thread_running_low_watermark时进程进入FIFO等待,其他线程执行完sql后唤醒FIFO;
保证并发线程控制在thread_running_low_watermark内,同时引入threads_running_wait_timeout控制线程在FIFO最大等待时间,超时则直接报错返回。
3引入8个FIFO,降低了进出FIFO的锁竞争,线程采用RR分配到不同fifo,每个队列限制并发运行线程为threads_running_low_watermark/8。
已经通过高水位验证的thread,开始执行query,[解析后进行低水位判断,若通过则执行],执行当前sql完毕后,thread可能发起新query,则重复[]过程。
新增系统变量
threads_running_wait_timeout:进入FIFO排队最长时间,等待超时后sql被拒,默认100,单位为毫秒ms。
新增状态变量
threads_active:当前并发SELECT/INSERT/UPDATE/DELETE执行的线程数目;
threads_wait:当前进入到FIFO中等待的线程数目;
测试效果
./sysbench
--test=tests/db/select.lua --max-requests=0 --mysql-host=myxxxx.cm3
--mysql-user=test --mysql-table-engine=innodb --oltp-table-size=5000000
--oltp-tables-count=32
normal
mysql-0 :未打补丁版本,设置innodb_thread_concurrency=0
normal
mysql-1 :未打补丁版本,innodb_thread_concurrency=32
patched
mysql :低水位限流补丁版本(活跃线程数不超过64)
注1
+static my_bool thread_running_control(THD *thd, ulong tr)
+{
+ int slept_cnt= 0;
+ ulong tr_low, tr_high;
+ DBUG_ENTER("thread_running_control");
+
+ /*
+ Super user/slave thre