MySQL技术:InnoDB 存储引擎(不同版本的 Master Thread 工作方式)

asdsInnoDB 存储引擎的主要工作都是在一个单独的后台线程Master Thread 中完成的,这一节将具体解释该线程的具体实现及该线程可能存在的问题。

asdsadasdasdasdsadasdasdasdsadassdasdsadasdasdsadasdsadassadasdas————《MySQL技术内幕INNODB存储引擎》


Master Thread 工作方式

lnnoDB 1.0.x 版本之前的Master Thread

sssaMaster Thread 具有 最高的线程优先级别。其内部由 多个循环(loop) 组成: 主循环( loop) 、后台循环(backgroup loop) 、刷新循环(flush loop) 、暂停循环(suspend loop)。Master Thread 会根据数据库运行的状态在这几个循环中进行切换。

ss loop循环

sssaLoop 被称为主循环,因为大多数的操作是在这个循环中,其中有两大部分的操作:每秒钟的操作和每10 秒的操作。

void master_ thread () {
loop:
for (int i= 0 ; i<lO ; i++) {
	do thing once per second
	sleep 1 second if necessary
}
do things once perten seconds
goto loop;
}

sdsssa注:loop 循环通过 thread sleep 来实现,这意味着所谓的每秒一次或每10 秒一次的操作是 不精确的。在负载很大的清况下可能会有延迟(delay), 只能说大概在这个频率下。当然, InnoDB 源代码中还通过了其他的方法来尽量保证这个频率。

sssa每秒一次的操作:

sssads①、日志缓冲刷新到磁盘,即使这个事务还没有提交(总是);

sdsssa注:即使某个事务还没有提交, InnoDB 存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这一点是必须要知道的,因为 这可以很好地解释为什么再大的事务提交(commit) 的时间也是很短的

sssads②、合并插入缓冲( 可能);

sdsssa注:合并插入缓冲(Insert Buffer) 并不是每秒都会发生的。InnoDB 存储引擎会判断当前一秒内发生的IO 次数是否小于5 次,如果小于5 次,InnoDB 认为当前的IO 压力很小,可以执行合并插入缓冲的操作。

sssads③、至多刷新100 个InnoDB 的缓冲池中的脏页到磁盘( 可能);

sdsssa注:InnoDB 存储引擎通过判断当前缓冲池中脏页的比例( buf_get_ modified ratio pct) 是否超过了配置文件中innodb_max_dirty pages_pct 这个参数(默认为90, 代表90%), 如果超过了这个阙值, InnoDB 存储引擎认为需要做磁盘同步的操作,将100 个脏页写入磁盘中。

sssads④、如果当前没有用户活动,则切换到background loop( 可能);

sssa每 10 秒一次的操作:

sssads①、将日志缓冲刷新到磁盘(总是);

sdsssa注:和每秒一次的操作一样

sssads②、合并至多5 个插入缓冲(总是);

sdsssa注:不同于每秒一次操作时可能发生的合并插入缓冲操作,这次的合并插入缓冲操作总会在这个阶段进行。

sssads③、刷新100 个脏页到磁盘(可能的情况下);

sdsssa注:InnoDB 存储引擎会先判断过去10 秒之内磁盘的IO 操作是否小于 200 次,如果是, InnoDB 存储引擎认为当前有足够的磁盘IO 操作能力,因此将100个脏页刷新到磁盘。

sssads④、删除无用的Undo 页(总是);

sdsssa注:执行full purge 操作,即删除无用的Undo页。对表进行update 、delete 这类操作时,原先的行被标记为删除,但是因为一致性读(consistent read) 的关系,需要保留这些行版本的信息但是在full purge 过程中,InnoDB 存储引擎会判断当前事务系统中已被删除的行是否可以删除,比如有时候可能还有查询操作需要读取之前版本的undo 信息,如果可以删除, InnoDB 会立即将其删除。(从源代码中可以发现,每次最多尝试回收 20undo 页。)

sssads⑤、刷新100 个或者10 个脏页到磁盘(总是) ;

sdsssa注:InnoDB 存储引擎会判断缓冲池中脏页的比例( buf_get_modified_ratio pct) ,如果有超过70% 的脏页,则刷新100 个脏页到磁盘,如果脏页的比例小于70%, 则 只需刷新10% 的脏页到磁盘。

sssa主循环( main loop ) 的伪代码:

ss background loop 循环

sssa若当前没有用户活动(数据库空闲时)或者数据库关闭(shutdown), 就会切换到这个循环。

sssabackground loop 会执行以下操作:

sssads①、删除无用的Undo 页(总是);

sssads②、合并20 个插入缓冲(总是);

sssads③、跳回到主循环(总是) ;

sssads④、不断刷新100 个页直到符合条件(可能,跳转到 flush loop刷新循环中完成) 。

sdsssa注:若flush loop 中也没有什么事情可以做了, InnoDB 存储引擎会切换到suspend_loop, 将Master Thread 挂起, 等待事件的发生。若用户启用(enable) 了InnoDB 存储引擎,却没有使用任何InnoDB 存储引擎的表,那么Master Thread 总是处于挂起的状态。

ss Master Thread 完整的伪代码如下(主循环、后台循环、刷新循环、暂停循环):
void master_thread() {
goto loop ;
loop:												主循环💖💖💦💦
for (int i = 0 ; i < 10; i++) {
	thread_sleep(l) // sleep 1 second
	do log buffer flush to disk      // 将日志缓冲刷新到磁盘
	if (last one second ios < 5 )
		do merge at most 5 insert buffer  // 如果在过去的一秒少于5次io操作,则合并5个插入缓冲
	if (buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
		do buffer pool flush 100 dirty page  // 如果超过了配置文件的阈值,至多刷新100个InnoDB 的缓冲池中的脏页到磁盘
	if ( no user activity)
		goto backgroud loop   // 如果当前没有用户活动,则切换到 background loop
}
if (last_ten_second_ios < 200)   // 如果在过去10秒的io操作小于200,则刷新100个脏页到磁盘
	do buffer pool flush 100 dirty page
do merge at most 5 insert buffer  // 合并5个插入缓冲
do log buffer flush to disk			// 将日志缓冲刷新到磁盘
do full purge					// 删除无用的 Undo 页
if (buf_get_modified_ratio_pct > 70%) 
	do buffer pool flush 100 dirty page
else						//如果有超过70% 的脏页,则刷新100 个脏页到磁盘,否则刷新10%的脏页
	buffer pool flush 10 dirty page
goto loop

background loop:									 后台循环💖💖💦💦
do full purge				//删除无用的 Undo 页
do merge 20 insert buffer		// 合并20 个插入缓冲
if not idle :
	goto loop				//跳回到主循环
else :
	goto flush loop
	
flush loop:											  刷新循环💖💖💦💦
do buffer pool flush 100 dirty page //刷新100个脏页
if(buf_get_modified_ ratio_pct > innodb_max_dirty_pages_pct)
	goto flush loop      //主要是用来刷新100个脏页直到满足条件
goto suspend loop

suspend loop :										  暂停循环💖💖💦💦
suspend_thread()
waiting event
goto loop;
}
lnnoDB1 .2.x 版本之前的Master Thread

sssa1.0.x 版本之前的Master Thread的实现方式使 InnoDB 存储引擎对于 IO 其实是有限制的,在缓冲池向磁盘刷新时其实都做了一定的 硬编码(hard coding),这种规定在 很大程度上限制了 InnoDB 存储引擎对磁盘IO 的性能,尤其是写入性能。

sdsssa问题①:从前面的伪代码来看,无论何时,InnoDB 存储引擎最大只会刷新100 个脏页到磁盘,合并20 个插入缓冲。如果是在写入密集的应用程序中,每秒可能会产生 >100 个的脏页,如果是产生大于20 个插入缓冲的情况, Master Thread 似乎会“忙不过来"'或者说它总是做得很慢。即使磁盘能在1 秒内处理多于100 个页的写入和20 个插入缓冲的合并,但是由于 hard coding, Master Thread 也只会选择刷新100 个脏页和合并20个插入缓冲。同时, 当发生岩机需要恢复时,由于很多数据还没有刷新回磁盘,会导致恢复的时间可能需要很久,尤其是对于insert buffer 来说。

sssa解决方法: 提供了参数 innodb_ io capacity, 用来表示磁盘 IO 的吞吐量,默认值为200 对于刷新到磁盘页的数量,会按照 innodb_io capacity百分比来进行控制。规则如下:

sssdsa①:在 合并插入缓冲时,合并插入缓冲的数量为 innodb_io_capacity 值的 5%

sssdsa②、在从 缓冲区刷新脏页时,刷新脏页的数量为 innodb_io_capacity

sdsssa注:若用户使用了SSD 类的磁盘,或者将几块磁盘做了RAID, 当存储设备拥有更高的 IO 速度时,完全可以将innodb_io capacity 的值调得再高点, 直到符合磁盘IO 的吞吐量为止。

sdsssa问题②:参数 innodb_max_ dirty_pages_pct 默认值的问题,在InnoDB 1.0.x版本之前,该值的默认为90, 意味着脏页占缓冲池的90% 。但是该值“太大”了,因为InnoDB 存储引擎在每秒刷新缓冲池和flush loop 时会判断这个值,如果有很大的内存,或者数据库服务器的压力很大,这时刷新脏页的速度反而会降低。同样,在数据库的恢复阶段可能需要更多的时间。

sssa解决方法: 从 InnoDB 1.0.x 版本开始,innodb_max_ dirty_pages_pct 默认值变为了 75, 和 Google 测试的 80 比较接近。这样既可以加快刷新脏页的频率,又能保证了磁盘 IO 的负载。

sdsssa问题③:对于每秒刷新脏页数量的改进。原来的刷新规则:脏页在缓冲池所占的比例< innodb max_ dirty_pages_pct 时,不刷新脏页;>innodb_max_dirty_pages pct 时,刷新100 个脏页。

sssa解决方法: InnoDB 1.0.x 版本带来的另一个参数是 innodb_adaptive_flushing (自适应地刷新),该值影响每秒刷新脏页的数量。随着innodb_adaptive_ flushing 参数的引入, InnoDB 存储引擎会通过一个名为 buf_flush_get_desired_ flush_rate 的函数来判断需要刷新脏页最合适的数量。粗略地翻阅源代码后发现 buf_ flush get_ desired_ flush_rate 通过判断产生重做日志(redo log) 的速度来决定最合适的刷新脏页数量因此,当脏页的比例小于innodb_max_dirty_pages_pct 时,也会刷新一定量的脏页。

sdsssa问题④:之前每次进行full purge 操作时,最多回收20 个Undo 页。

sssa解决方法: 从InnoDB 1.0.x 版本开始引入了参数innodb_purge_ batch_size, 该参数可以控制每次full purge 回收的Undo 页的数最。该参数的默认值为20, 并可以动态地对其进行修改

ss Master Thread 的伪代码更新如下:
void master_thread () {
goto loop ;
loop:
for (int i = O; i<lO ; i++) {
	thread_sleep(1) // sleep 1 second
	do log buffer flush to disk   // 将日志缓冲刷新到磁盘
	if(last_one_second_ ios < 5% innodb_io_capacity)	//innodb_io_capacity就是1.0引入的💦💦
		do merge 5% innodb_io_capacity insert buffer  // 如果在过去的一秒少于5次io操作,则合并5个插入缓冲
	if( buf_get_modified_ ratio_pct > innodb_max_dirty_pages_pct)
			 // 如果超过了配置文件的阈值,至多刷新100个InnoDB 的缓冲池中的脏页到磁盘
		do buffer pool flush 100% innodb_ io_capacity dirty page
	else if enable adaptive flush				// innodb_adaptive_flushing (自适应地刷新)💦💦
		do buffer pool flush desired amount dirty page
	if ( no user activity)		 // 如果当前没有用户活动,则切换到 background loop
		goto backgroud loop
}
if(last_ten_second_ios < innodb_io_capacity)       //每10秒根据IO的吞吐量更新脏页💦💦
	do buffer pool flush 100% innodb_io_capacity dirty page
do merge 5% innodb_io_capacity insert buffer	//每10秒合并插入缓存为总插入缓存的百分之5💦💦
do log buffer flush to disk
do full purge
if(buf_get_modified_ratio_pct > 70%)
	do buffer pool flush 100% innodb_io_ capacity dirty page   //根据脏页总量来刷新,低于一定值也能刷新💦💦
else
	dobuffer pool flush 10% innodb_io_capaciy dirty page
goto loop

background loop :
do full purge
do merge 100% innodb_io_ capacity insert buffer     //与上面一样💦💦
if not idle :
goto loop:
else :
	goto flush loop
	
flush loop :
do buffer pool flush 100% innodb_io_ capacity dirty page    //与上面一样💦💦
if(buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
	go to flush loop
	goto suspend loop
	
suspend loop :
suspend_ thread ()
waiting event
goto loop ;
}

sdsssa注:InnoDB 1.0.x 版本在性能方面取得了极大的提高,其实这和前面提到的Master Thread 的改动是密不可分的,因为InnoDB 存储引擎的核心操作大部分都集中在Master Thread 后台线程中。

sssa实际应用:命令SHOW ENGINE INNODB STATUS 查看当前 MasterThread 的状态信息:

mysql> show engine innodb status\G;
*************************** 1. row***************************
Type: InnoDB
Name:
Status:
091009 10:14:34 INNODB MONITOR OUTPUT
Per second averages calculated from the last 42 seconds
BACKGROUND THREAD
srv_master_ thread l oops: 2188 1_second, 1537 sleeps, 218 10_second, 2 background, 2 flush
srv_master_thread log flush and writes: 1777 log writes only: 5816
......

sdsssa分析:当前主循环运行了2188 次,但是循环中的每秒挂起(sleep) 的操作只运行了1537 次(这是因为InnoDB 对其内部进行了一些优化,当压力大时并不总是等待 1 秒。因此,并不能认为1_second 和 sleeps 的值总是相等的。在某些情况下,可以通过两者之间差值的比较来反映当前数据库的负载压力,差值越大说明数据库的负载压力越大),10 秒一次的活动进行了218 次,符合1 : 10 。background loop 进行了2 次,flush loop 也进行了2次。

lnnoDB 1.2.x 版本的Master Thread

sssaInnoDB 1.2.x 版本中再次对 Master Thread 进行了优化,由此也可以看出 MasterThread 对性能所起到的关键作用。在 InnoDB 1.2.x 版本中, Master Thread 的伪代码如下:

if InnoDB is idle
	srv master do idle tasks( );
else
	srv master do active tasks();

sssa其中 srv_master_ do_ idle_ tasks() 就是之前版本中每10 秒的操作, srv_master_ do_active_ tasks() 处理的是之前每秒中的操作。同时对于刷新脏页的操作,从Master Thread线程分离到一个单独的Page Cleaner Thread, 从而减轻了Master Thread 的工作,同时进一步提高了系统的并发性

💖感谢各位的暴击三连~💖

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值