Checkpoint技术
缓冲池的设计目的是为了协调CPU速度与磁盘速度的鸿沟。因此页的操作首先都是在缓冲池中完成的。如果一条DML语句,比如Update或Delete改变了页中的记录,那么此时页是脏的,即缓冲池中的页要比磁盘的新,那么数据库就需要将新版本的页从缓冲池刷新到磁盘。
倘若每次一个页发生变化,都要将新页刷新到磁盘,那么这个开销是非常大的。如热点数据集在某几个页中,那么数据库的性能将变得非常差。同时,如果在从缓冲池往磁盘刷新页的时候发生的宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当事务数据库系统普遍都采用Write Ahead log策略, 即当事务提交时,先写重做日志,再修改页。当因为宕机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务ACID中持久性(Durability)的要求。
如下场景需要思考:如果重做日志可以无限的增大,同时缓冲池也足够大,能够缓冲所有数据库的数据,那么是不需要将缓冲池中页的新版本刷新到磁盘。因为当发生宕机时,完全可以通过重做日志来恢复整个数据库系统的数据到宕机发生的时刻。但是这样做是有前提的:
- 1.缓冲池可以缓存数据库中所有的数据
- 2.重做日志可以无限增大
对于第一个前提条件,有经验的用户都知道,当数据库刚开始创建时,表中没有任何数据。缓冲池的确可以缓存所有的数据库文件。然后随着市场的推广,用户的增加,产品越来越受到关注,使用量也越来越大。这时负责后台存储的数据库容量必定会不断增大。当前3TB的MySQL数据库已不少见,但是3TB的内存却非常少见。
再看第二个前提条件,重做日志可以无限增大。也许是可以的,但是这对成本的要求太高了,同时不便于运维。DBA或SA不能知道什么时候重做日志是否已经接近于磁盘可使用空间的阈值,并且要让存储设备支持可动态扩展也是需要一定的技巧和设备支持的。
当前即使以上两个条件都满足,那么还需要考虑的是,当数据库运行几个月甚至几年,如果发生宕机,那么通过重做日志恢复的话,需要多久呢?
因此CheckPoint(检查点)技术的目的是解决以下几个问题:
- 缩短数据库的恢复时间
- 缓冲池不够用时,将脏页刷新到磁盘
- 重做日志不可用时,刷新脏页
当数据库发生宕机时,数据库不需要重做所有的日志,因为checkpoint之前的页都已经刷新回磁盘。故数据库只需对checkpoint后的的重做日志进行恢复。这样就大大缩短了恢复时间。
此外,当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,如此页为脏页,那么需要强制执行checkpoint,将脏页也就是页的最新版本刷回磁盘。
重做日志出现不可用的情况是因为当前事务数据库系统对重做日志的设计是循环使用的,并不是让其无线增大,这从成本及管理上都是比较困难的。重做日志可以被重用的部分是指这些重做日志已经不再需要了,即当数据库发生宕机时,数据库恢复操作不需要这部分的重做日志,因此这部分就可以被覆盖重用。若此时重做日志还需要使用,那么必须强制产生checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。
对于InnoDB存储引擎而言,其是通过LSN(Log Sequenc Number)来标记版本的。而LSN是8字节的数字,其单位是字节。每个页有LSN,重做日志也有LSN,checkpoint也有LSN。可以通过命令show engine innodb status
来观察:
mysql> show engine innodb status\G;
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2022-11-21 10:02:07 0x7f573806e700 INNODB MONITOR OUTPUT
=====================================
......
---
LOG
---
Log sequence number 105133652
Log flushed up to 105133652
Pages flushed up to 105133652
Last checkpoint at 105133643
0 pending log flushes, 0 pending chkp writes
10 log i/o's done, 0.00 log i/o's/second
----------------------
......
在InnoDB存储引擎中,checkpoint发生的时间、条件及脏页的选择等都非常复杂。而checkpoint所做的事情无外乎是将缓冲池中的脏页刷回到磁盘。不同之处在于每个刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发checkpoint。在InnoDB存储引擎内部,有两种checkpoint,分别位:
- Sharp Checkpoint
- Fuzzy Checkpoint
Sharp CheckPoint发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作,即参数innodb_fast_shutdown=1
。
mysql> show variables like '%innodb_fast_shutdown%';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| innodb_fast_shutdown | 1 |
+----------------------+-------+
1 row in set (0.00 sec)
但是如果数据库在运行的时候也使用Sharp Checkpoint,那么数据库的可能性就会受到很大的影响。故在InnoDB存储引擎内部使用Fuzzy Checkpoint进行页的刷新,即只刷新一部分脏页,而部署刷新所有的脏页回磁盘。
在InnoDB存储引擎中可能发生如下几种情况的Fuzzy Checkpoint:
- Master Thread Checkpoint
- FLUSH_LRU_LIST Checkpoint
- Aysnc/Sync Flush Checkpoint
- Dirty Page too much Checkpoint
Master Thread Checkpoint
Master Thread中发生的Checkpoint,差不多以每秒或没十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。这个过程是异步的,即此时InnoDB存储引擎可以进行其他的操作,用户查询线程不会阻塞。
FLUSH_LRU_LIST Checkpoint
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 variables like 'innodb_lru_scan_depth'\G;
*************************** 1. row ***************************
Variable_name: innodb_lru_scan_depth
Value: 1024
1 row in set (0.00 sec)
Aysnc/Sync Flush Checkpoint
Aysnc/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。那么asynx_water_mark=1.5GB,sync_water_mark=1.8GB
。则:
- 当checkpoint_age < aync_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的BUCK INSERT操作。此时触发Sync Flush操作,从Flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age < sync_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
中,故不会阻塞用户查询线程
。
Dirty Page too much Checkpoint
Dirty Page too much Checkpoint指的是脏页的数量太多,导致InnoDB存储引擎强制进行CheckPoint。其目的总的来说还是为了保证缓冲池中有足够可用的页。其可由innodb_max_dirty_pages_pct控制:
mysql> show variables like 'innodb_max_dirty_pages_pct'\G;
*************************** 1. row ***************************
Variable_name: innodb_max_dirty_pages_pct
Value: 75.000000
1 row in set (0.01 sec)
innodb_max_dirty_pages_pct值为75表示,当缓冲池中脏页的数量占据75%时,强制进行CheckPoint,刷新一部分的脏页到磁盘。在InnoDB 1.0.x版本之前,该参数默认值为90,之后的版本都为75。