2.4 CheckPoint,讲到了CheckPoint解决的问题,其中第三点是“重做日志不可用时,刷新脏页”。详细的描述是这样的:
重做日志可以被重用的部分是指这些重做日志已经不再需要,即当数据库发生宕机时,数据库恢复操作不需要这部分的重做日志,因此这部分就可以被覆盖重用。若此时重做日志还需要使用,那么必须强制产生CheckPoint,将缓冲池中的页至少刷新到当前重做日志的位置。
读到这一段的时候,尤其是加粗部分的内容,我不禁产生疑惑:缓冲池中的页不是本来就和redolog状态相同吗?为什么还需要将缓冲池中的页刷新?
第二天回顾的时候,我思考了一下。(以下为是个人解读,如有错误欢迎批评指正)
首先,由于InnoDB采用WAL搭配redolog保证crash safe,也就是更新数据的时候,先写redolog,再写buffer pool中的数据页。又由于写redolog时需要先写redolog buffer,而这个过程由于不需要doublewrite,应该是比较快的。所以,这就可能导致在数据量大、并发写多的情况下,很多操作都被写到了redolog中,但还没有写到buffer pool中的数据页中,如图:
而这时,由于redolog是循环写,在空间不足时(写不下了),就产生了书上说的“不可用”状态。
因此此时,必须强制先将buffer pool中的数据页写入一部分(刷回盘),和redolog保持一致,从而腾出空间。具体的解决措施,即是属于Fuzzy CheckPoint的Async/Sync Flush CheckPoint,根据checkpoint_age,分别和async_water_mark以及sync_water_mark比较,选择相应的Flush操作。具体判断过程如下:
- 当checkpoint_age < async_water_mark时,无需刷页
- 当async_water_mark < checkpoint_age < sync_water_mark时,触发Async Flush
- 当sync_water_mark < checkpoint_age时,触发Sync Flush(一般很少发生)
其中,water_mark的计算公式为:
async_water_mark = 75% * total_redo_log_file_size
sync_water_mark = 90% * total_redo_log_file_size
而checkpoint_age的计算公式为:
checkpoint_age = redo_lsn - checkpoint_lsn
LSN即Log Sequence Number,我的理解里,其实就和kafka里的offset作用差不多。
经过Flush操作之后,确保checkpoint_age < async_water_mark。
回到刚才的问题,所以,文中的“不可用”,指的应该是redolog写空间不足。