InnoDB存储引擎的主要工作都是在一个单独的后台线程master thread中完成的。
master thread主线程中主要包括以下几个循环:
主循环 loop
后台循环background loop
刷新循环flush loop
暂停循环suspend loop
(1)重做日志缓冲刷新到磁盘,即使这个事务还没有提交(这可以解释为什么再大的事务commit的时间也是很快的);
(2)判断当前1秒内IO是否<5次,是则说明当前IO压力很小,可合并插入缓冲;
(3)判断当前缓冲池中的脏页比例buf_get_modified_ratio_pct是否>innodb_max_dirty_pages_pct,超过这个阈值说明需要做磁盘同步操作,将100个脏页写入磁盘;
(4)如果当前没有用户活动,切换到background loop后台循环中。
2、每10秒一次的操作
(1)判断过去10秒之内IO是否<200,是则认为当前有足够的磁盘IO能力,将刷新100个脏页到磁盘;
(2)合并至多5个插入缓冲;
(3)将日志缓冲刷新到磁盘;
(4)执行一次full purge操作,删除无用的undo页(每次最多删除20个)->作用:对表执行update、delete这类操作,原先的行会被标记为删除,但是为了一致性读,需要保留这些行版本的信息。但是在full purge操作时,会判断当前事务系统中已被删除的行是否可以被删除(比如有时候可能还有查询操作需要读取之前版本的undo信息),如果可以,InnoDB会立即将其删除。
(5)刷新100个或10个脏页到磁盘(脏页比例>70%,则刷新100个;<70%,则刷新10个);
(6)产生一个checkpoint检查点,为fuzzy checkpoint模糊检查点。InnoDB存储引擎在checkpoint时并不会把所有缓冲池中的脏页都写入到磁盘,因为这样可能会对性能产生影响,而只是将oldest LSN最老日志序列号的页写入磁盘。
3、数据库空闲时或数据库关闭时的操作
(1)删除无用的undo页;
(2)合并20个插入缓冲;
(3)跳回到主循环;
(4)不断刷新100个脏页到磁盘,直到符合条件(可能跳转到flush loop中完成)。
master thread中潜在的问题:硬编码hard coding
(1)最多只会刷新100个脏页,合并5个插入缓冲;
问题:当密集写时,“忙不过来”,很慢;且发生宕机需要恢复时,由于很多数据还没有刷新到磁盘,可能会导致恢复需要很长的时间。
修正:参照google patch,提供磁盘IO吞吐量参数innodb_io_capacity,默认200,刷新100%脏页、合并5%插入缓冲。
(2)脏页比例innodb_max_dirty_pages_pct默认为90%
问题:该值“太大了”,如果有很大的内存或者数据库服务器的压力很大时,刷新脏页的速度反而可能会降低;同时,在数据库恢复阶段可能需要更多的时间。
master thread主线程中主要包括以下几个循环:
主循环 loop
后台循环background loop
刷新循环flush loop
暂停循环suspend loop
void master_thread() {
loop:
for (int i = 0; i < 10; i++)
{
sleep 1 second if necessary
do things once per second // 1、每秒1次的操作
if (no user activity) // 如果当前没有用户活动,切换到后台循环
goto background loop;
}
do things once per ten seconds // 2、每10秒一次的操作
goto loop;
background loop:
do something // 3、数据库空闲时或数据库关闭时的操作
if (not idle) // 非空闲切换到主循环
goto loop;
else // 空闲时切换到刷新循环
goto flush loop;
flush loop:
do buffer pool flush 100 dirty page // 刷新100个脏页到磁盘
// 缓存池中脏页的比例>某个阈值,默认为90%,不断刷新100个脏页到磁盘
if (buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct)
goto flush loop;
goto suspend loop; // 切换到暂停循环,将master thread挂起
suspend loop:
suspend_thread()
waiting event
goto loop;
}
1、每秒1次的操作
(1)重做日志缓冲刷新到磁盘,即使这个事务还没有提交(这可以解释为什么再大的事务commit的时间也是很快的);
(2)判断当前1秒内IO是否<5次,是则说明当前IO压力很小,可合并插入缓冲;
(3)判断当前缓冲池中的脏页比例buf_get_modified_ratio_pct是否>innodb_max_dirty_pages_pct,超过这个阈值说明需要做磁盘同步操作,将100个脏页写入磁盘;
(4)如果当前没有用户活动,切换到background loop后台循环中。
2、每10秒一次的操作
(1)判断过去10秒之内IO是否<200,是则认为当前有足够的磁盘IO能力,将刷新100个脏页到磁盘;
(2)合并至多5个插入缓冲;
(3)将日志缓冲刷新到磁盘;
(4)执行一次full purge操作,删除无用的undo页(每次最多删除20个)->作用:对表执行update、delete这类操作,原先的行会被标记为删除,但是为了一致性读,需要保留这些行版本的信息。但是在full purge操作时,会判断当前事务系统中已被删除的行是否可以被删除(比如有时候可能还有查询操作需要读取之前版本的undo信息),如果可以,InnoDB会立即将其删除。
(5)刷新100个或10个脏页到磁盘(脏页比例>70%,则刷新100个;<70%,则刷新10个);
(6)产生一个checkpoint检查点,为fuzzy checkpoint模糊检查点。InnoDB存储引擎在checkpoint时并不会把所有缓冲池中的脏页都写入到磁盘,因为这样可能会对性能产生影响,而只是将oldest LSN最老日志序列号的页写入磁盘。
3、数据库空闲时或数据库关闭时的操作
(1)删除无用的undo页;
(2)合并20个插入缓冲;
(3)跳回到主循环;
(4)不断刷新100个脏页到磁盘,直到符合条件(可能跳转到flush loop中完成)。
master thread中潜在的问题:硬编码hard coding
(1)最多只会刷新100个脏页,合并5个插入缓冲;
问题:当密集写时,“忙不过来”,很慢;且发生宕机需要恢复时,由于很多数据还没有刷新到磁盘,可能会导致恢复需要很长的时间。
修正:参照google patch,提供磁盘IO吞吐量参数innodb_io_capacity,默认200,刷新100%脏页、合并5%插入缓冲。
(2)脏页比例innodb_max_dirty_pages_pct默认为90%
问题:该值“太大了”,如果有很大的内存或者数据库服务器的压力很大时,刷新脏页的速度反而可能会降低;同时,在数据库恢复阶段可能需要更多的时间。
修正:innodb_max_dirty_pages_pct默认90%->默认75%,且通过innodb_adaptive_flushing自适应地刷新影响每一秒刷新脏页的数量(通过一个buf_flush_get_desired_flush_rate函数判断需要刷新脏页最合适的数量,buf_flush_get_desired_flush_rate函数通过判断产生重做日志的速度来判断最合适的刷新脏页的数量)。
参考:<MySQL技术内幕 InnoDB存储引擎>