Sharp Checkpoint发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数innodb_fast_shutdown=1。但是若数据库在运行时也使用Sharp Checkpoint,那么数据库的可用性就会受到很大的影响。故在InnoDB存储引擎内部使用Fuzzy Checkpoint进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。
Fuzzy Checkpoint:
1、Master Thread Checkpoint;
2、FLUSH_LRU_LIST Checkpoint;
3、Async/Sync Flush Checkpoint;
4、Dirty Page too much Checkpoint
1、Master Thread Checkpoint
以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘,这个过程是异步的,此时InnoDB存储引擎可以进行其他的操作,用户查询线程不会阻塞。
2、FLUSH_LRU_LIST Checkpoint
因为InnoDB存储引擎需要保证LRU列表中需要有差不多100个空闲页可供使用。在InnoDB1.1.x版本之前,需要检查LRU列表中是否有足够的可用空间操作发生在用户查询线程中,显然这会阻塞用户的查询操作。倘若没有100个可用空闲页,那么InnoDB存储引擎会将LRU列表尾端的页移除。如果这些页中有脏页,那么需要进行Checkpoint,而这些页是来自LRU列表的,因此称为FLUSH_LRU_LIST Checkpoint。
而从MySQL 5.6版本,也就是InnoDB1.2.x版本开始,这个检查被放在了一个单独的Page Cleaner线程中进行,并且用户可以通过参数innodb_lru_scan_depth控制LRU列表中可用页的数量,该值默认为1024,如:
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_lru_scan_depth';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_lru_scan_depth | 1024 |
+-----------------------+-------+
3、Async/Sync Flush Checkpoint
指的是重做日志文件不可用的情况,这时需要强制将一些页刷新回磁盘,而此时脏页是从脏页列表中选取的。若将已经写入到重做日志的LSN记为redo_lsn,将已经刷新回磁盘最新页的LSN记为checkpoint_lsn,则可定义:
checkpoint_age(可以理解脏页,或者待刷新的脏页) = redo_lsn - checkpoint_lsn
再定义以下的变量:
async_water_mark = 75% * total_redo_log_file_size
sync_water_mark = 90% * total_redo_log_file_size
若每个重做日志文件的大小为1GB,并且定义了两个重做日志文件,则重做日志文件的总大小为2GB。那么async_water_mark=1.5GB,sync_water_mark=1.8GB。则:
当checkpoint_age<async_water_mark时,不需要刷新任何脏页到磁盘; </async_water_mark时,不需要刷新任何脏页到磁盘;<>
当async_water_mark<checkpoint_age<sync_water_mark时触发async flush,从flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age<async_water_mark; </checkpoint_age
checkpoint_age>sync_water_mark这种情况一般很少发生,除非设置的重做日志文件太小,并且在进行类似LOAD DATA的BULK INSERT操作。此时触发Sync Flush操作,从Flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age<async_water_mark。 </async_water_mark。<>
可见,Async/Sync Flush Checkpoint是为了保证重做日志的循环使用的可用性。在InnoDB 1.2.x版本之前,Async Flush Checkpoint会阻塞发现问题的用户查询线程,而Sync Flush Checkpoint会阻塞所有的用户查询线程,并且等待脏页刷新完成。从InnoDB 1.2.x版本开始——也就是MySQL 5.6版本,这部分的刷新操作同样放入到了单独的Page Cleaner Thread中,故不会阻塞用户查询线程。
解释下为什么重做日志文件不可用时,这时需要强制将一些脏页刷新回磁盘?
因为我们知道redo的作用是保证数据库的一致性,当数据库异常停机时,需要借助redo+undo进行实例恢复,redo前滚---恢复出buffer pool中的脏数据(包括已经commit还没有刷新到磁盘的,也可能包括没有commit,但是已经刷新到磁盘的,)然后借助undo完成回滚---将没有commit,但是已经刷新到磁盘的数据,回滚到之前的状态。那么为啥重做日志文件不可用时,这时需要强制将一些脏页刷新回磁盘?原因就在于,redo 是循环覆写的,当redo log 文件不可用,也就是说此时所有的redo 文件里面的redo都是实例恢复需要的,也就是不能被覆盖的redo, 那么什么是实例恢复需要的redo呢?就是buffer pool中的的脏数据,还没有刷新到磁盘,而这些脏数据相关的redo是不能被覆盖的,这些redo就是实例恢复需要的redo,所以没有可用的重做日志文件,需要强制将一些脏页刷新回磁盘,这样就会有一些redo是实例恢复不需要的了,也就可以被覆盖了。
4、Dirty Page too much