思考一下这个场景:若是重作日志能够无限地增大,同时缓冲池也足够大,那么是不须要将缓冲池中页的新版本刷新回磁盘。由于当发生宕机时,彻底能够经过重作日志来恢复整个数据库系统中的数据到宕机发生的时刻。html
可是这须要两个前提条件:一、缓冲池能够缓存数据库中全部的数据;二、重作日志能够无限增大mysql
所以Checkpoint(检查点)技术就诞生了,目的是解决如下几个问题:一、缩短数据库的恢复时间;二、缓冲池不够用时,将脏页刷新到磁盘;三、重作日志不可用时,刷新脏页。算法
当数据库发生宕机时,数据库不须要重作全部的日志,由于Checkpoint以前的页都已经刷新回磁盘。数据库只需对Checkpoint后的重作日志进行恢复,这样就大大缩短了恢复的时间。sql
当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么须要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。数据库
当重作日志出现不可用时,由于当前事务数据库系统对重作日志的设计都是循环使用的,并非让其无限增大的,重作日志能够被重用的部分是指这些重作日志已经再也不须要,当数据库发生宕机时,数据库恢复操做不须要这部分的重作日志,所以这部分就能够被覆盖重用。若是重作日志还须要使用,那么必须强制Checkpoint,将缓冲池中的页至少刷新到当前重作日志的位置。缓存
对于InnoDB存储引擎而言,是经过LSN(Log Sequence Number)来标记版本的。bash
LSN是8字节的数字,每一个页有LSN,重作日志中也有LSN,Checkpoint也有LSN。能够经过命令SHOW ENGINE INNODB STATUS来观察:app
mysql> show engine innodb status \G --- LOG --- Log sequence number 34778380870 Log flushed up to 34778380870 Last checkpoint at 34778380870 0 pending log writes, 0 pending chkp writes 54020151 log i/o's done, 0.92 log i/o's/second
Checkpoint发生的时间、条件及脏页的选择等都很是复杂。而Checkpoint所作的事情无外乎是将缓冲池中的脏页刷回到磁盘,不一样之处在于每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发Checkpoint。dom
在InnoDB存储引擎内部,有两种Checkpoint,分别为:Sharp Checkpoint、Fuzzy Checkpoint异步
Sharp Checkpoint 发生在数据库关闭时将全部的脏页都刷新回磁盘,这是默认的工做方式,即参数innodb_fast_shutdown=1。可是若数据库在运行时也使用Sharp Checkpoint,那么数据库的可用性就会受到很大的影响。故在InnoDB存储引擎内部使用Fuzzy Checkpoint进行页的刷新,即只刷新一部分脏页,而不是刷新全部的脏页回磁盘。
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,如:
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_lru_scan_depth'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_lru_scan_depth | 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版本开始——也就是MySQL 5.6版本,这部分的刷新操做一样放入到了单独的Page Cleaner Thread中,故不会阻塞用户查询线程。
MySQL官方版本并不能查看刷新页是从Flush列表中仍是从LRU列表中进行Checkpoint的,也不知道由于重作日志而产生的Async/Sync Flush的次数。可是InnoSQL版本提供了方法,能够经过命令SHOW ENGINE INNODB STATUS来观察,如:
mysql> show engine innodb status \G BUFFER POOL AND MEMORY ---------------------- Total memory allocated 2058485760; in additional pool allocated 0 Dictionary memory allocated 913470 Buffer pool size 122879 Free buffers 79668 Database pages 41957 Old database pages 15468 Modified db pages 0 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 15032929, not young 0 0.00 youngs/s, 0.00 non-youngs/s Pages read 15075936, created 366872, written 36656423 0.00 reads/s, 0.00 creates/s, 0.90 writes/s Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 41957, unzip_LRU len: 0 I/O sum[39]:cur[0], unzip sum[0]:cur[0]
四、Dirty Page too much
即脏页的数量太多,致使InnoDB存储引擎强制进行Checkpoint。其目的总的来讲仍是为了保证缓冲池中有足够可用的页。其可由参数innodb_max_dirty_pages_pct控制:
mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_max_dirty_pages_pct' ; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | innodb_max_dirty_pages_pct | 75 | +----------------------------+-------+
innodb_max_dirty_pages_pct值为75表示,当缓冲池中脏页的数量占据75%时,强制进行Checkpoint,刷新一部分的脏页到磁盘。在InnoDB 1.0.x版本以前,该参数默认值为90,以后的版本都为75。
在Innodb事务日志中,采用了Fuzzy Checkpoint,Innodb每次取最老的modified page(last checkpoint)对应的LSN,再将此脏页的LSN做为Checkpoint点记录到日志文件,意思就是“此LSN以前的LSN对应的日志和数据都已经flush到redo log
当mysql crash的时候,Innodb扫描redo log,从last checkpoint开始apply redo log到buffer pool,直到last checkpoint对应的LSN等于Log flushed up to对应的LSN,则恢复完成
那么具体是怎么恢复的呢?
如上图所示,Innodb的一条事务日志共经历4个阶段:
建立阶段:事务建立一条日志;
日志刷盘:日志写入到磁盘上的日志文件;
数据刷盘:日志对应的脏页数据写入到磁盘上的数据文件;
写CKP:日志被看成Checkpoint写入日志文件;
对应这4个阶段,系统记录了4个日志相关的信息,用于其它各类处理使用:
Log sequence number(LSN1):当前系统LSN最大值,新的事务日志LSN将在此基础上生成(LSN1+新日志的大小);
Log flushed up to(LSN2):当前已经写入日志文件的LSN;
Oldest modified data log(LSN3):当前最旧的脏页数据对应的LSN,写Checkpoint的时候直接将此LSN写入到日志文件;
Last checkpoint at(LSN4):当前已经写入Checkpoint的LSN;
对于系统来讲,以上4个LSN是递减的,即: LSN1>=LSN2>=LSN3>=LSN4.
具体的样例以下(使用show innodb status \G命令查看,Oldest modified data log没有显示):
LOG --- Log sequence number 34822137537 Log flushed up to 34822137537 Last checkpoint at 34822133028 0 pending log writes, 0 pending chkp writes 54189288 log i/o's done, 3.00 log i/o's/second
mysql crash的时候,Innodb有日志刷盘机制,能够经过innodb_flush_log_at_trx_commit参数进行控制,这里说的是如何防止日志覆盖致使日志丢失
Innodb的checkpoint和redo log有哪些紧密关系?有几上名词须要解释一下:
Ckp age(动态移动): 最老的dirty page尚未flush到数据文件,即没有作last checkpoint的范围
Buf age(动态移动): modified page information没有写到log中,但已在log buffer
Buf async(固定点): 日志空间大小的7/8,当buf age移动到Buf async点时,强制把没有写到log中的modified page information开始写入到log中,不阻塞事务
Buf sync(固定点): 日志空间大小的15/16,当写入很大的,buf age移动很是快,一会儿到buf sync的点,阻塞事务,强制把modified page information开始写入到log中。若是不阻塞事务,未作last checkpoint的redo log存在覆盖危险
Ckp async(固定点): 日志空间大小的31/32,当ckp age到达ckp async,强制作last checkpoint,不阻塞事务
Ckp sync(固定点):日志空间大小,当ckp age到达ckp sync,强制作last checkpoint,阻塞事务,存在redo log覆盖的危险
接下分析4种状况
若是buf age在buf async和buf sync之间
若是buf age在buf sync以后(固然这种状况是不存在,mysql有保护机制)
若是ckp age在ckp async和ckp sync之间(这种状况是不存在)
若是ckp age在ckp sync以后(这种状况是不存在)
第一种状况:
当写入量巨大时,buf age移动到buf async和buf sync之间,触发写出到log中,mysql把尽可能多的log写出,若是写入量减慢,buf age又移回到“图一”状态。若是写入量大于flush log的速度,buf age最终会和buf sync重叠,这时全部的事务都被阻塞,强制将2*(Buf age-Buf async)的脏页刷盘,这时IO会比较繁忙。
第二种状况:
固然这种状况是不可能出现,由于若是出现,redo log存在覆盖的可能,数据就会丢失。buf age会越过log size,buf age的大小可能就超过log size,若是要刷buf age,那么整个log size都不够容纳全部的buf age。
第三种和第四种状况不存在分析:
ckp age始终位于buf age的后面(左边),由于ckp age是last checkpoint点,老是追赶buf age(将尽量多的modified page flush到磁盘),因此buf age确定是先到达到buf sync。
ckp async及ckp sync存在乎义?
mysql中page cache也存在high water及low water,当dirty page触到low water时,os是开始flush dirty page到磁盘,到high water时,会阻塞一切动做,os会疯狂的flush dirty page,磁盘会很忙,存在IO Storm,
本文参考:
http://blog.csdn.net/yah99_wolf/article/category/539408
http://www.cnblogs.com/bamboos/p/3532150.html
http://tech.uc.cn/?p=716
http://www.mysqlperformanceblog.com/2011/04/04/innodb-flushing-theory-and-solutions/