innodb_thread_concurrency算法介绍:
1、server层到innodb层读写数据是一条一条记录进行的,每次读写都会进/出一次InnoDB层(row_search_for_mysql),进入InnoDB层的时候会检查当前并发线程数目,当超过innodb_thread_concurrency时,线程将尝试spin和sleep并再次检查,如果并发数还是超过innodb_thread_concurrency,线程将进入到一个FIFO中等待被唤醒,读写记录结束后退出InnoDB层时会将当前并发线程数减1,并检查其是否低于innodb_thread_concurrency,如果是的话,从FIFO中唤醒一个等待的线程,保证并发线程不会超过innodb_thread_concurrency参数。
2、当线程进入InnoDB层后,但在获取数据时由于锁请求无法得到满足而需要挂起时,线程将强制退出InnoDB层,当锁请求满足后,线程继续运行并强制进入到InnoDB层,这会导致实际并发线程数不是严格控制在innodb_thread_concurrency之内
代码调用逻辑
主要函数:
innodb_srv_conc_enter_innodb
innodb_srv_conc_exit_innodb
srv_conc_force_enter_innodb
srv_conc_force_exit_innodb
调用逻辑
进入/退出InnoDB层
ha_innobase::index_read
ha_innobase::general_fetch
row_check_index_for_mysql
...
innodb_srv_conc_enter_innodb(prebuilt->trx);
ret = row_search_for_mysql((byte*) buf, mode,
prebuilt, match_mode, 0);
innodb_srv_conc_exit_innodb(prebuilt->trx);
...
锁等待逻辑:
srv_suspend_mysql_thread
...
if (trx->declared_to_be_inside_innodb)
{
was_declared_inside_innodb =
TRUE;
/* We must declare this OS
thread to exit InnoDB, since a
possible other thread holding
a lock which this thread waits
for must be allowed to enter,
sooner or later */
srv_conc_force_exit_innodb(trx);
}
/* Suspend this thread and wait for the
event. */
thd_wait_begin(trx->mysql_thd,
THD_WAIT_ROW_LOCK);
os_event_wait(event);
thd_wait_end(trx->mysql_thd);
if (was_declared_inside_innodb) {
/* Return back inside InnoDB
*/
srv_conc_force_enter_innodb(trx);
}
5.6的改进
从5.6开始,默认编译会使用GCC atomic,可避免热点锁srv_conc_mutex的频繁加锁/释放。
Pre-5.5如果超过并发限制时,就给其线程分配一个slot,让其进入信号量等待,原子操作则无需使用信号量。
function srv_conc_enter_innodb:
ifdef HAVE_ATOMIC_BUILTINS
srv_conc_enter_innodb_with_atomics(trx);
#else
srv_conc_enter_innodb_without_atomics(trx);
#endif /* HAVE_ATOMIC_BUILTINS */
5.6引入adaptive sleep,允许innodb根据系统负载进行自适应调整,当innodb_adaptive_max_sleep_delay>0,innodb_thread_sleep_delay则会动态调整(以前者为上限)。
ada还ptive 的算法是:
如果sleep了当前值以后还是不能进入,就把sleep时间+1;
如果还有线程在sleep时,已经有了空闲线程,就把当前值的sleep 时间减半。
innodb_thread_sleep_delay降低比增加的更快,这样在并发线程数很高时,当限制并发数早就达到,其他线程的每次sleep时间会缓慢拉长。而当Innodb层很空闲时,sleep时间又会快速降到非常低
调整sleep到一个优化值的目的是,过小的sleep值可能会产生太多的线程切换,但过长的sleep时间,在并发比较空闲的时候又会影响性能。新的并发控制策略有利于随着负载的变化而做自适应调整。
Innodb_thread_concurrency不足
1线程因为锁等待而退出innodb层,当获取锁时可以直接重入innodb(跳过此参数检查),因此系统并发执行的线程数可能大于此参数值。
2代码路径靠后,此时线程已经开始执行命令,进入到ha_innobase,应该在mysql_start_query前限制即thread_running。
为此好多技术高手开发了相应patch,从server层控制并发执行的线程数,下文将做描述。
参考文章