概念
1 什么是脏页?
对于数据库中页的修改操作,则首先修改在缓冲区中的页,缓冲区中的页与磁盘中的页数据不一致,所以称缓冲区中的页为脏页。
2 脏页什么时候写入磁盘?
脏页以一定的频率将脏页刷新到磁盘上。页从缓冲区刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为CheckPoint的机制刷新回磁盘。
3 什么是CheckPoint?
Checkpoint要做的事情是将缓冲池中的脏页数据刷到磁盘上。CheckPoint决定了脏页落盘的时机、条件及脏页的选择,不同的CheckPoint做法并不相同。
保证数据的安全性
落盘的流程图:
脏页产生了肯定是有一个时间要进行落盘,那么怎么保证修改内存到落盘整个过程中不发生任何的问题呢?
InnoDB采用了Write Ahead Log(WAL)策略和Force Log at Commit机制实现事务级别下数据的持久性。
Force Log at Commit机制
:当事务提交时,所有事务产生的日志都必须刷到磁盘。如果日志刷新成功后,缓冲池中的数据刷新到磁盘前数据库发生了宕机,那么重启时,数据库可以从日志中恢复数据,这样可以保证数据的安全性,这个是mysql redo log 落盘的默认行为,innodb_flush_log_at_trx_commit 可以通过这个参数去改变。
Write Ahead Log(WAL)策略
:要求数据的变更写入到磁盘前,首先必须将内存中的日志写入到磁盘;InnoDB 的 WAL(Write Ahead Log)技术的产物就是 redo log,对于写操作,永远都是日志先行,先写入 redo log 然后在合适的时间应用redo log进行刷盘。
redo log日志只记录更新操作和行信息,大小相对较小。同时日志的写入是顺序的,就是继续往后写,这样通过日志先行就把随机的写入转换为了顺序写入从而提升了性能。再有日志的刷盘和事务是有关联的,事务提交后刷盘策略可以通过innodb_flush_log_at_trx_commit 来控制,日志记录的是事务中执行的一系列操作,不是单条就会触发更新。
Redo Log 刷盘的时机
1)Mysql服务关闭时
2)当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,会触发落盘;
3)InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。
4)每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘,这里可以调整策略。
为了确保每次日志都写入到redo日志文件,在每次将redo日志缓冲写入redo日志后,调用一次fsync操作(从系统的缓存真正刷新到磁盘),将缓冲文件从文件系统缓存中真正写入磁盘。
innodb_flush_log_at_trx_commit 这个参数相信也不陌生了:
-
0时:事务提交时,不会立即把 log buffer里的数据写入到redo log日志文件的。而是等待主线程每秒写入一次。
特点:
如果MySQL崩溃或者服务器宕机,此时内存里的数据会全部丢失,最多会丢失1秒的事务。
写入效率最高,但是数据安全最低; -
1时:每次事务提交时,会将数据将从log buffer写入redo日志文件与文件系统缓存,并同时
fsync刷新到磁盘中。
特点:
系统默认配置为1,MySQL崩溃已经提交的事务不会丢失,要完全符合ACID必须使用默认设置1。
写入效率最低,但是数据安全最高; -
2时:事务提交时,也会将数据写入redo日志文件与文件系统缓存,但是不会调用fsync,而是让
操作系统自己去判断何时将缓存写入磁盘。
特点:
事务提交都会将数据刷新到操作系统缓冲区,可以认为是已经持久化到磁盘,但没有真正意义
上持久化到磁盘。
如果MySQL崩溃已经提交的事务不会丢失。但是如果服务器宕机或者意外断电,操作系统缓存内的数据会丢失,所以最多丢失1秒的事务。
检查点机制
有了上面的准备工作,真正决定数据什么时候落盘的时机是检查点机制,下面我们来看看检查点是怎样工作的,解决了什么问题?
1 从这个流程来看,首先它可以避免Redo log日志的堆积。因为我们当前检查点执行以后,数据已经落盘了,那么之前的Redo log就没有作用了可以清理掉不可能再使用到的日志。同时如果数据库发了宕机,这个时候也只需要执行上一个检查点到现在的Redo Log就可以恢复数据。
2 可以解决缓冲池不够用问题,缓冲池不够用时,将脏页刷新到磁盘当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行Checkpoint,将脏页也就是页的新版本刷回磁盘。
3 redo日志不可用时,刷新脏页当redo日志出现不可用时,Checkpoint将缓冲池中的页至少刷新到当前redo日志的位置。这样就算RedoLog不可用也可以保证不丢失更新。
那么具体的检查点又有所不同
1 可以分为两类
sharp checkpoint:在关闭数据库的时候,将buffer pool中的脏页全部刷新到磁盘中。
fuzzy checkpoint:数据库正常运行时,在不同的时机,将部分脏页写入磁盘。仅刷新部分脏页到磁盘,也是为了避免一次刷新全部的脏页造成的性能问题。
Fuzzy Checkpoint:默认方式,只刷新一部分脏页,不是刷新所有脏页;
主要有以下几种情况:
- Master Thread Checkpoint :在Master Thread中,会以每秒或者每10秒一次的频率,将部分脏页从内存中刷新到磁盘,这个过程是异步的。正常的用户线程对数据的操作不会被阻塞。
- FLUSH_LRU_LIST Checkpoint:缓冲池不够用时,根据LRU算法会淘汰掉最近最少使用的页,如果该页是脏页的话,会强制执行CheckPoint,将该脏页刷回磁盘(由Page Cleaner Thread完成);
- Async/Sync Flush Checkpoint:重做日志不可用的情况,需要强制从脏页列表中选取一些脏页刷盘(由Page Cleaner Thread完成)。由于磁盘是一种相对较慢的存储设备,内存与磁盘的交互是一个相对较慢的过程。innodb_log_file_size定义的是一个相对较大的值,正常情况下,由前面两种checkpoint刷新脏页到磁盘,在前面两种checkpoint刷新脏页到磁盘之后,脏页对应的redo log空间随即释放,一般不会发生Async/Sync Flush checkpoint。
- Dirty Page too much:即脏页数量太多,导致强制进行Checkpoint。由参数innodb_max_dirty_pages_pt 来控制,默认75(即75%)。当脏页数量占据75%缓冲池时,刷新一部分脏页到磁盘。(由Page Cleaner Thread完成)
在检查点落盘的过程中也可能会发生异常,这个时候就需要Double Write双写来保证不写失效
所谓的写失效就就比如我们一页的数据为16K,但是我们这个页只写了一半数据库就发生了异常,这个时候页就被损坏了。
这个时候我们不能通过Redo log来恢复,重做日志中记录的是对页的物理操作,而不是页面的全量记录,而如果发生partial page write(部分页写入)问题时,出现问题的是未修改过的数据,此时重做日志(Redo Log)无能为力。因此引入了双写机制:
Double Write分两个部分:
内存中的Doublewrite buffer,大小为2MB
磁盘上的Doublewrite buffer,大小为2MB,连续的128个页,相当于两个extent
Double write脏页刷新流程:
1 首先复制:脏页刷新时不直接写磁盘,而是先将脏页复制到内存的Doublewrite buffer。
2 再顺序写:内存的Doublewrite buffer分两次,每次1MB顺序地写入共享表空间的物理磁盘上,会立即调用fsync函数同步OS缓存到磁盘中,顺序写性能好。
3 最后离散写:内存的Doublewrite buffer最后将页写入各自表空间文件中,离散写较顺序写入差一些。
如果操作系统在将页写入磁盘的过程中发生了崩溃,其恢复过程如下:
1 首先InnoDB存储引擎从系统表空间中的Double write中找到该页的一个副本
2 然后将其复制到独立表空间
3 再应用重做日志。
相关配置
innodb_doublewrite:Doublewrite Buffer是否启用开关,默认是开启状态,InnoDB将所有数据存储两次,首先到双写缓冲区,然后到实际数据文件。
Innodb_dblwr_pages_written:记录写入到DWB中的页数量。
Innodb_dblwr_writes:记录DWB写操作的次数。