从5.6开始,如果使用了GCC Build-in的原子操作,在进入Innodb层的线程并发控制走与之前不同的逻辑,5.5也可以调用通过原子操作进行并发控制的逻辑,但需要打开只读选项innodb_thread_concurrency_timer_based来控制.
quoted code in 5.6:
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 */
srv_conc_enter_innodb_without_atomics是MySQL5.5的调用逻辑,在5.6中开始默认编译情况下,调用srv_conc_enter_innodb_with_atomics逻辑,其不同之处在于使用GCC Build-in的原子操作,来避免热点锁srv_conc_mutex的频繁加锁/释放。
与5.5不同的是,在5.6中采用了一种称为adaptive sleep的方法,来替代5.5使用的直接sleep固定时间的方式。这样Innodb可以根据系统的负载做一些自适应调整。新增一个参数innodb_adaptive_max_sleep_delay(文档见http://dev.mysql.com/doc/refman/5.6/en/innodb-parameters.html#sysvar_innodb_adaptive_max_sleep_delay ),文档的表述有误,已report bug list(http://bugs.mysql.com/bug.php?id=68594)
原先的做法是在进入Innodb层前,先查看当前在Innodb中活跃的线程数是否超过innodb_thread_concurrency; 如果超过了,则sleep一段时间,再重试,如果还是超过并发限制时,就给其分配一个slot,让其进入信号量等待。
在5.6中,当使用原子操作进行并发控制时,如果innodb_adaptive_max_sleep_delay大于0, 会对另外一个参数innodb_thread_sleep_delay的值做自适应调整.它的逻辑也很简单。
我们这里假定innodb_adaptive_max_sleep_delay的值大于0
当线程能够进入Innodb层时:
a.如果当前线程之前sleep过一次,并且当前innodb_thread_sleep_delay>20,将innodb_thread_sleep_delay减1
b.如果当前没有等待的线程,将innodb_thread_sleep_delay除以2
如果线程目前因并发控制无法进入Innodb层:
a.如果当前innodb_thread_sleep_delay>innodb_adaptive_max_sleep_delay
,将innodb_thread_sleep_delay的值设置为innodb_adaptive_max_sleep_delay
b.sleep innodb_thread_sleep_delay毫秒
c.如果该线程已经sleep了超过1次,将innodb_thread_sleep_delay++
可以看到innodb_thread_sleep_delay降低比增加的更快。这样在并发线程数很高时,当限制并发数早就达到,其他线程的每次sleep时间会缓慢拉长。而当Innodb层很空闲时,sleep时间又会快速降到非常低
调整sleep到一个优化值的目的是,过小的sleep值可能会产生太多的线程切换,但过长的sleep时间,在并发比较空闲的时候又会影响性能。新的并发控制策略有利于随着负载的变化而做自适应调整。
另外注意,在使用原子操作进行并发控制后,就再也没有使用信号量让线程进行等待了。
这种自适应调整策略的效率依然有待于评估,也不确定其对某些工作负载是否存在不利影响。合适的参数配置应当由性能测试来给出。