MySQL innodb Checkpoint的设计和Oracle checkpoint类似,主要是为了缩短数据库恢复时间,buffer pool不够用时刷脏页到datafile,保证redo log file的循环使用而不会一直增大。
MySQL innodb引擎通过LSN(Log Sequence Number)来标记版本,通过show engine innodb status \G输出做解释:
---
LOG
---
Log sequence number 2722769
#LSN1,当前数据库系统LSN,最新事务的LSN
Log flushed up to 2722769
#LSN2,当前已写入redo log file的LSN
Pages flushed up to 2722769
#LSN3,当前最旧脏页数据对应的LSN
Last checkpoint at 2722760
#LSN4,当前已写入redo log file checkpoint的LSN
0 pending log flushes, 0 pending chkp writes
129 log i/o's done, 0.00 log i/o's/second
----------------------
LSN1>=LSN2>=LSN3>=LSN4
检查点分为:
sharp checkpoint全量检查点 在数据库关闭时将脏页刷新到datafile ,通过参数innodb_fast_shutdown=1 或 0可以实现
Innodb_fast_shutdown告诉innodb在它关闭的时候该做什么工作。有三个值可以选择:
0表示在innodb关闭的时候,需要purge all, merge insert buffer,flush dirty pages。这是最慢的一种关闭方式,但是restart的时候也是最快的。
1表示在innodb关闭的时候,它不需要purge all,merge insert buffer,只需要flush dirty page。1是默认值。
2表示在innodb关闭的时候,它不需要purge all,merge insert buffer,也不进行flush dirty page,只将log buffer里面的日志flush到log files。因此等下进行恢复的时候它是最耗时的。
purge all,merge insert buffer的含义:
Purge all
这个操作主要是删除那些无用的undo页。对于delete操作,innodb是通过先将要删除的那一行标记为删除,而不是马上清除这一行,因为innodb实现了MVCC,这些undo段用来实现MVCC机制。MVCC也就是常说的多版本控制,锁不阻塞读,读也不阻塞写,这样大大提高了并发性。那么在一致性读的时候,怎么才能找到和事务开始的那个版本呢?对于主键索引,每个行都有一个事务ID和一个undo ID,这个undo ID指向了这行的先前版本的位置。对于非主键索引,也就是常说的secondary index,是通过先找主键索引再找到undo段。而对于update操作,则是先标记删除,然后insert一个新的行,接下来如果有一致性读,那么查找old version的行的原理和delete操作是一样的,详情见[1]。现在接着说purge all操作,随着DML的操作越来越多,那么回滚段必然也会越来越多导致占用了许多磁盘空间,那么innodb就会定期删除一些无用的undo页,首先,innodb重启的时候必然undo页都会无效所以会进行purge all操作,另外,随着时间的推移必然一些事务已经完成,它们已不再需要某些undo页,那么这些undo在mysqld running的时候也会定期的进行清除。
Merge insert buffer
Insert buffer是innodb的一个特性之一,在非聚簇、且不是唯一索引(即非主键索引、非唯一索引)的情况下,如果插入的索引行所属的页在buffer pool中就直接更新这个页,否则它会将这个索引行插入到insert buffer中,然后定期对这个insert buffer进行合并(合并的本质工作就是将insert buffer中的信息更新到真正的索引文件中去)。因为innodb的secondary index是非聚簇的,那么插入很有可能带来大量的随机I/O,而如果利用insert buffer对一些属于相同页的行进行合并,那么就会减少随机IO从而提高性能。但是这里需要注意的是,insert buffer和doublewrite buffer是类似的概念,他实际上属于system tablespace中的一部分[2],正由于它也是持久化存储,那么在服务器宕机或是重启之后这些信息不会丢失,所以也就有了在前面介绍innodb_fast_shutdown时所说:在innodb重启时,可能需要进行merge insert buffer。那么在什么情况下需要对insert buffer进行merge操作呢?
a> 在innodb restart的时候
b> master thread会定期的进行merge操作
c> 每次读取secondary index page时,如果所需页不在buffer pool,而这些页在insert buffer中的时候,这时需要先对insert buffer进行合并,然后才能被读取。为什么这样呢?因为所有插入的索引行所属的页如果不在buffer pool中,而又在insert buffer中,那么它一定代表了页的最新状态(不理解?因为每次插入索引行的时候,如果所需页不在
buffer pool中就直接插入到insert buffer中,而一旦insert buffer merge后相关的行也就不在insert buffer更新secondary index page了)。这时或许你会问那么为什么不直接读取insert buffer中的页然后继续操作而一定要合并(更新到索引文件)呢?因为在innodb中是数据文件(也就是主键索引)和索引文件缓存的,在insert buffer中读取了需要的页后,那么必然就会在buffer pool中缓存了这个页,而如果这个页还留在insert buffer中却不更新到secondary index page去,那么,第一,这将不能保证索引文件得到更新;第二,insert buffer的空间会被占用。而如果这一步将insert buffer 合并后,不但减小了insert buffer的使用空间,而且将这merge操作完成了一部分,减小了以后merge的负担(不是有句话叫做今日事今日毕么),不过这也减慢了读的操作,因为读操作必须等待这个页的合并。
fuzzy checkpoint 部分页刷新检查点,主要4类:
1.master thread checkpoint
master thread周期性从cache pool的脏页列表种刷新一定比例的页到datafile,相关参数:innodb_io_capacity
2.FLUSH_LRU_LIST checkpoint
page cleaner thread通过innodb_lru_scan_depth数量刷新脏页
3.async/sync flush checkpoint
redo log file不可用时,强制将一些脏页刷新到datafile。sync位置时redo log的7/8处,async是redo log的3/4处
4.dirty page too much
buffer log脏页太多,达到innodb_max_dirty_pages_pct时强制进行checkpoint
参考:
MySQL DBA工作笔记