为什么需要Checkpoint技术
InnoDB存储引擎中的缓存池的目的是协调CPU速度和磁盘速度的差别。数据库对数据页的操作首先都是在缓冲池中完成的。如果一条DML语句,如update和delete改变了页中的记录,那么该页变为了脏页,也就是说缓冲池中的页的内容比磁盘中的页要新。数据库需要将新版本的页从缓冲池刷新到磁盘。
但是如果一个页每次发生了变化之后,就将新页的版本刷新到磁盘,那么这个开销是非常大的。如果在从缓冲池中将页的新版本刷新到磁盘时发生了宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当前事务数据库系统普遍采用了Write Ahead Log策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务ACID中的D(持久性)的要求。
如果重做日志可以无限地增大,同时缓冲池也足够大,能够缓冲所有数据库的数据,那么是不需要将缓冲池中页的新版本刷新回磁盘的。因为当发生宕机时,完全可以通过重做日志来恢复整个数据库系统中的数据。但是需要满足两个条件:重做日志可以无限地增大,同时缓冲池也足够大,也就是说内存足够大,磁盘足够大存放重做日志。
不过如果数据库的内容特别大的话,如果通过重做日志来恢复的话可能需要很长时间。
Checkpoint技术
Checkpoint技术的目的是解决以下几个问题:
1)缩短数据库的恢复时间;
2)缓冲池不够用时,将脏页刷新到磁盘;
3)重做日志不可用时,刷新脏页;
当数据库发生宕机时,数据库不需要重做所有的日志,因为Checkpoint之前的页都已经刷新回磁盘了。所以,数据库只需要对Checkpoint后的重做日志进行恢复。这样就缩短了恢复的时间。
当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。
重做日志是可以循环使用的,重做日志可以被重用的部分是指这些重做日志已经不需要了,也就是说,即使数据库宕机了,数据库恢复操作不需要这部分的重做日志,所以这部分日志可以被覆盖重复使用。
如果重做日志没有可以被重复使用的并且重做日志已经满了,那么必须强制产生Checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。
对于InnoDB存储引擎,是通过LSN(Log Sequence Number)来标记版本的。而LSN是8字节的数字,其单位是字节。每个页有LSN,重做日志也有LSN,Checkpoint也有LSN。
mysql> show engine innodb status\G
下面是部分输出:
---
LOG
---
Log sequence number 2448358
Log flushed up to 2448358
Last checkpoint at 2448358
0 pending log writes, 0 pending chkp writes
8 log i/o's done, 0.00 log i/o's/second
Checkpoint所做的事情就是将缓冲池中的脏页刷回到磁盘。比较关心的问题是每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发Checkpoint。
- 在InnoDB存储引擎内部,有两种Checkpoint,分别为:
1)Sharp Checkpoint
2)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进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页到磁盘。
- 下面是几种发生Fuzzy Checkpoint的情况:
1)Mater Thread Checkpoint
2)FLUSH_LRU_LIST Checkpoint
3)Async/Sync Flush Checkpoint
4)Dirty Page too much Checkpoint
- Mater Thread Checkpoint
对于Mater 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。
从MySql5.6版本,也就是InnoDB1.2.x版本开始,这个检查被放在了一个单独的Page Cleaner线程中进行,并且用户可以通过参数innodb_lru_scan_depth控制LRU列表中可用页的数量,该值默认是1024;
mysql> show variables like 'innodb_lru_scan_depth';
该参数是MySql5.6版本开始新加入的,所以如果版本太低的话是查不到该参数的:
mysql> show variables like 'version';
+---------------+-------------------------+
| Variable_name | Value |
+---------------+-------------------------+
| version | 5.5.49-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)
mysql> show variables like 'innodb_lru_scan_depth';
Empty set (0.00 sec)
- Async/Sync Flush Checkpoint
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
那么async_water_mark=1.5GB,sync_water_mark=1.8GB。则:
1)当checkpoint_age < async_water_mark时,不需要刷新任何脏页到磁盘;
2)当async_water_mark < checkpoint_age < sync_water_mark时触发Async Flush,从Flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age < async_water_mark;
3)checkpoint_age > sync_water_mark,这种情况一般很少发生,除非设置的重做日志文件太小,并且在进行类似LOAD DATA的BLUK INSERT操作。此时触发Sync Flush操作,从Flush列表中刷新足够的脏页回磁盘,使得刷新后满足checkpoint_age < async_water_mark;
可以看出Async/Sync Flush Checkpoint是为了保证重做日志的循环使用的可用性。在InnoDB1.2.x版本之前,Async Flush Checkpoint会阻塞发现问题的用户查询线程,而Sync Flush Checkpoint会阻塞所有的用户查询线程,并且等待脏页刷新完成。从InnoDB1.2.x版本开始,也就是MySql5.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';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| innodb_max_dirty_pages_pct | 75 |
+----------------------------+-------+
1 row in set (0.00 sec)
innodb_max_dirty_pages_pct值为75,表示当缓冲池中脏页的数量占据75%时,强制进行Checkpoint,刷新一部分的脏页到磁盘。