2.4 Checkpoint 技术
https://blog.youkuaiyun.com/tanliqing2010/article/details/81509539 思维导图 可以借鉴一下
- 前面已经讲到了,缓冲池的设计目的为了协调 CPU速度与磁盘速度的鸿沟。因此页的操作首先都是在缓冲池中完成的。如果一条 DML 语句,如 Update 或 Delete 改变了页中的记录,那么此时页是脏的, 即缓冲池中的页的版本要比磁盘的新。数据库需要将新版本的页从缓冲池刷新到磁盘。
- 倘若每次一个页发生变化,就将新页的版本刷新到磁盘,那么这个开销是非常大的。若热点数据集中在某几个页中,那么数据库的性能将变得非常差。同时,如果在从缓冲池将页的新版本刷新到磁盘时发生了宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当前事务数据库系统普遍都采用了 Write Ahead Log 策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务 ACID中D(Durability 持久性)的要求。
- 思考下面的场景,如果重做日志可以无限地增大,同时缓冲池也足够大,能够缓冲所有数据库的数据,那么是不需要将缓冲池中页的新版本刷新回磁盘。因为当发生宕机时,完全可以通过重做日志来恢复整个数据库系统中的数据到宕机发生的时刻。但是这需要两个前提条件∶
1.缓冲池可以缓存数据库中所有的数据;
2.重做日志可以无限增大。
对于第一个假设对于生产环境应用中的数据库是很难得到保证的。
再来看第二个前提条件∶重做日志可以无限增大。也许是可以的,但是这对成本的要求太高,同时不便于运维。
即使上述两个条件都满足,那么还有一个情况需要考虑∶ 宕机后数据库的恢复时间。 - 因此 Checkpoint (检查点)技术的目的是解决以下几个问题∶
1.缩短数据库的恢复时间;
2.缓冲池不够用时,将脏页刷新到磁盘;
3.重做日志不可用时,刷新脏页。 - 当数据库发生宕机时,数据库不需要重做所有的日志,因为 Checkpoint 之前的页都已经刷新回磁盘。故数据库只需对 Checkpoint 后的重做日志进行恢复。这样就大大缩短了恢复的时间。
- 此外,当缓冲池不够用时,根据LRU 算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行 Checkpoint,将脏页也就是页的新版本刷回磁盘。
- 重做日志出现不可用的情况是因为重做日志的设计是循环使用的,并不是让其无限增大的。重做日志可以被重用的部分是指这些重做日志已经不再需要。若此时重做日志还需要使用,那么必须强制产生 Checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。
- 对于InnoDB 存储引擎而言,其是通过 LSN(Log Sequence Number)来标记版本的。而 LSN 是8字节的数字,其单位是字节。每个页有LSN,重做日志中也有 LSN, Checkpoint 也有LSN。可以通过命令 SHOW ENGINE INNODB STATUS 来观察;
在 InnoDB存储引擎中,Checkpoint 发生的时间、条件及脏页的选择等都非常复杂。 - 而 Checkpoint 所做的事情无外乎是将缓冲池中的脏页刷回到磁盘。在 InnoDB存储引擎内部,有两种 Checkpoint,分别为∶
- Sharp Checkpoint
- Fuzzy Checkpoint
- Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数
innodb_fast_shutdown=1。
但是若数据库在运行时也使用 Sharp Checkpoint,那么数据库的可用性就会受到很大的影响。故在 InnoDB 存储引擎内部使用 Fuzzy Checkpoint 进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。
这里笔者进行了概括,在 InnoDB存储引擎中可能发生如下几种情况的 Fuzzy Checkpoint:
- Master Thread Checkpoint
- FLUSH_LRU_LIST Checkpoint
- Async/Sync Flush Checkpoint
- Dirty Page too much Checkpoint
- 对于Master Thread中发生的 Checkpoint,差不多以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。这个过程是异步的,即此时 InnoDB 存储引擎可以进行其他的操作,用户查询线程不会阻塞。
- 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,如∶
- 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<checkpoint age<sync_water_mark 时触发 Async Flush,从 Flush列表中刷新足够的脏页回磁盘,使得刷新后满足 checkpoint age<async_water_ mark;
- checkpoint_age>sync_water_mark 这种情况一般很少发生,除非设置的重做日志文件太小,并且在进行类似LOAD DATA的 BULK INSERT操作。此时触发 Sync Flush 操作,从 Flush 列表中刷新足够的脏页回磁盘,使得刷新后满足 checkpoint_age<async_water_mark。
- 可见,Async/Sync Flush Checkpoint 是为了保证重做日志的循环使用的可用性。在 InnoDB 1.2.x 版本之前,Async Flush Checkpoint 会阻塞发现问题的用户查询线程,而 Sync Flush Checkpoint 会阻塞所有的用户查询线程,并且等待脏页刷新完成。从 InnoDB 1.2.x版本开始———也就是MySOL 5.6 版本,这部分的刷新操作同样放入到了单独的Page Cleaner Thread 中,故不会阻塞用户查询线程。
- MySQL 官方版本并不能查看刷新页是从 Flush列表中还是从LRU列表中进行 Checkpoint 的,也不知道因为重做日志而产生的 Async/Sync Flush 的次数。但是 InnoSQL版本提供了方法,可以通过命令 SHOW ENGINE INNODB STATUS 来观察,如∶
- 最后一种 Checkpoint 的情况是 Dirty Page too much,即脏页的数量太多,导致 InnoDB 存储引擎强制进行 Checkpoint。其目的总的来说还是为了保证缓冲池中有足够可用的页。其可由参数 innodb_max_dirty_pages_pct 控制∶
- innodb_max_dirty_pages_pct 值为75 表示,当缓冲池中脏页的数量占据 75%时,强制进行 Checkpoint,刷新一部分的脏页到磁盘。在 InnoDB 1.0.x版本之前,该参数默认值为 90,之后的版本都为 75。