InnoDB并发控制模型

赶在2014之前,写下2013最后一篇技术博客,介绍一下InnDB的并发控制模型

背景知识

InnoDB中为什么需要并发控制?要回答这个问题,需要先聊聊context switch,当同时执行的进程/线程超过cpu核心数(超线程的数目)时,cpu以时间片轮转调度执行多个进程的时候就有context switch了,当进程A的时间片用完需要调度另外进程B时,需要将进程A的状态和参数保存在堆栈中,等再次调度到A时,再从堆栈中恢复状态和参数,这个切换的代价就是context switch,context switch的代价根据CPU性能及进程空间大小的不同,开销从几微秒(us)到几毫秒(ms)级别(参考:Quantifying The Cost of Context Switchhow-long-does-it-take-to-make-context),相对于一次内存读写操作(100ns左右),context switch代价不可小视(按照100us计算的话,相当于1000次内存访问)

innodb_thread_concurrency

这个参数就是控制进入到InnoDB层的并发线程数目,默认为0,表示不受限制,线上我们一般设置为32或者64

当同时并发(逻辑上)的进程/线程数目远远超过cpu核心数时,context switch就会非常频繁,最终导致性能问题(一轮context switch的时间也许够处理一个任务了),这时候如果限制并发运行的进程/线程数目在cpu核心数,就会极大提高性能,看效果图:

innodb_thread_concurrency_test

当innodb_thread_concurrency设置为0时,innodb并发控制不起作用,导致进入到innodb层的线程数不受控制,可以从图中观察到QPS急剧下降,当并发线程超过4096时,MySQL基本不能响应了(机器硬件:2个cpu,12核心/cpu,192G内存),然而设置innodb_thread_concurrency为32时,性能下降得没有那么厉害

InnoDB并发控制实现

算法介绍(innodb_thread_concurrency > 0):
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);
	}

至于更加具体的实现细节,感兴趣的话还需阅读代码

《InnoDB并发控制模型》上有1条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注