这种策略是指,当事务提交时,要先重做日志,然后再修改页,如果由于宕机发生修改页失败,那么可以通过日志来完成数据的恢复,这也是事务的ACID里面的D属性(Durability 持久性)。
假设重做日志可以无限增大,而且缓冲池足够大,可以缓冲所有数据库的数据,那么就不必将缓冲池中的页刷新回磁盘了,只需要操作缓冲池就行,当发生宕机后,可以通过重做日志来恢复整个数据库系统到宕机发生的时刻。但这两个条件都很苛刻。
即使满足前面这两个条件,但如果对于重做日志很大,宕机后数据库的恢复时间要很久,此时恢复的代价也是非常大的,所以此时就有了checkpoint技术(检查点)
checkpoint技术主要优化如下
缩短数据库的恢复时间
缓冲池不够用时,将脏页刷新到磁盘
重做日志不可用时,刷新脏页
当数据库发生宕机时,数据库不需要重新去重做所有的日志,因为checkpoint之前的页都是已经刷新到磁盘上了,数据库只需要对checkpoint后的重做日志进行恢复,这样就大大缩短了数据库恢复时间。
除此之外,如果缓冲池不够用,LRU列表会溢出old区里面的页(最少使用的页),如果此页是脏页,就会发生数据丢失,所以,发生缓冲池不够用时,若脏页要被溢出,就会被强制执行checkpoint,将脏页刷新到磁盘。
重做日志并不是可以无限增大的,只不过可以重用那些不需要的空间(是指那些不再需要的重做日志),当数据库发生宕机时,如果数据库根据日志来恢复数据时,发现重做日志的某部分不需要使用,那么这部分可能就会被释放,来覆盖重用(这些数据一般都是已经被写入磁盘永久保存了,不需要日志来恢复),若该重做日志还要继续使用,那么必须强制执行checkpoint,将缓冲池的页刷新到当前重做日志的位置。(即重做日志重用了某些不需要使用的部分,此时需要重做日志缓冲池对重做日志进行刷新,来覆盖那些不需要的部分日志)。
InnoDB通过LSN(log Sequence Number->日志序列号,表示事务写入到日志的字节总量)来标记版本的,LSN是一个8个字节的数字,每个页都有LSN,重做日志中也有LSN,CheckPoint也有LSN。
//查看InnoDB的LSN
SHOW ENGINE INNODB STATUS;
CheckPoint所做的事情无外乎是将缓冲池的脏页刷新回磁盘中,不过脏页的选择、CheckPoint发生的时间是比较负责的。重点在于,每次要刷新多少页到磁盘,又是从哪里获取脏页,以及什么时间触发CheckPoint。
InnoDB存储引擎内部,有两种Checkpoint,分别为:
Sharp Checkpoint
Fuzzy Checkpoint
Sharp Checkpoint是发生在数据库关闭时将所有的脏页都刷新回磁盘中,这也是默认的工作方式,即参数innodb_fast_shutdown=1。
在InnoDB存储引擎内部是使用Fuzzy Checkpoint进行页的刷新,即在数据库运行时,使用的是Fuzzy Checkpoint,即只会刷新部分脏页,而不是刷新所有的脏页回磁盘
Fuzzy Checkpoint大概在以下几种情况会发生
Master Thread Checkpoint
FLUSH_LRU_LIST Checkpoint
Async/Sync Flush Checkpoint
Dirty Page too much Checkpoint
Master Thread Checkpoint是在Master Thread中发生的,Master Thread会以差不多每秒或每十秒从缓冲池中刷新一定比例的页会磁盘,这个过程是异步的,即不会阻塞其他操作,比如用户查询线程。
FLUSH_LRU_LIST Checkpoint是用来保证缓冲池中的LRU列表中需要差不多100个空闲页可供使用。
在InnoDB1.1.X版本之前,需要检查LRU列表中是否有足够的可用空间,这个操作是发生在用户查询线程中的,这显然会对用户查询线程造成阻塞影响,降低了效率,倘若发现LRU列表不足100个空闲页时,那么InnoDB存储引擎会将LRU列表尾端的页移除(old区),如果发现移除的页中存在脏页,那么就要进行checkpoint,将脏页刷新回磁盘中,因为这些页是LRU列表中的,所以称为FLUSH_LRU_LIST Checkpoint。
但从InnoDB1.2.X版本开始(即MySQL5.6版本),这个检查就被单独放在Page Cleaner线程中进行,用户可以通过控制参数innodb_lru_scan_depth控制LRU列表里面可用页的数量,现在默认为1024。
SHOW VARIABLES LIKE ‘innodb_lru_scan_depth’;
Async/Sync Flush Checkpoint指的是重做日志文件不可用的情况,这是要像使用回不可用的重做日志,就要强制将一些页刷新回磁盘,此时脏页是从脏页列表中选取的
若将已经重做日志中已经有的LSN记为redo_lsn,将要刷新回磁盘的页的总LSN记为checkpoint_lsn,定义一个check_point_age,定义如下
check_point_age = redo_lsn - checkpoint_lsn
这里可以将redo_lsn可以理解成当前磁盘上的重做日志的日志数量,checkpoint_lsn理解成释放掉不要的部分的重做日志的日志数量,那么两者相减,就得到了释放掉不要的部分重做日志的日志数量。
然后再定义以下的变量
async_water_mark = 75% * total_redo_log_file_size
sync_water_mark = 90% * total_redo_log_file_size