参考文章:
MySQL的崩溃恢复到底是怎么回事: developer.aliyun.com/article/919…
背景
保证持久化。简单做法,在事物提交之前把内存里的改动同步到磁盘即可,存在的问题:
- 如果只修改了一个字段,一个字节改动就把页数据(18k)刷到磁盘,太浪费
- 随机性修改,一个事物中可能涉及到多个不连续的页,要进行很多随机IO
redo log的思路,崩溃重启后可以恢复数据也相当于保证了持久性,每次修改记录会保存一条redo log
- redo log结构:type、spaceID、pageNumber、data。
- row_id例子,简单redo_log的结构增一条记录,各个树,文件header等修改,复杂redo_log的例子(保存底层函数参数)
- redo日志只是系统崩溃时用来恢复脏页的,如果脏页都已经刷盘了,那么就不需要redo日志了,redo日志可以被覆盖了。
log buffer
不会直接将redo log写入log file,先写到Buffer Pool中的 log Buffer中,再由一些机制同步到log file。
log buffer 同步到log file:
- log buffer空间不足时
- 事物提交时
- 系统线程每秒刷新到log file
- 正常关闭服务时
- checkpoint时
flush链表
- 在buffer pool的页被编辑过,不会立即同步磁盘,先记录redo log日志后放到flush链表,等待刷盘操作。
- o-m、n-m熟悉:第一次修改的lsn,最新一次修改的lsn
- 刷盘操作: a。正常关闭时 b。系统线程master thread 每秒将flush中的脏页刷新到磁盘 c。缓存页不够用时
checkpoint机制
-
checkpoint 与 刷新flush链表的线程(异步)是两回事
-
checkpoint时机:
- 固定时间线程刷新,每1/10秒
- log file空间不足时
- 缓存页空间不足时,LRU剔除最少使用的页,如果是脏页,执行checkpoint
- 脏页比例过大,75%
-
checkpoint步骤:
- 找到flush链表中,o-m最小的控制块,o-m就是最新的checkpoint_lsn
- 将checkpoint_lsn同步到log_file的管理信息中
-
好处: 重启时只恢复最新的checkpoint,可以缩短崩溃恢复时间,也就是重启时间。 对log buffer中的脏页、redo log 中的日志做瘦身
lsn
- 日志序列号,初始值8704,mtr后增加: 实际产生的日志量(一个字节代表1) + log block head + log block tail。
- lsn越小,代表日志产生越早
flush_to_disk_lsn
redo log 是先到Buffer Pool的 log buffer再到磁盘的log file,其中lsn是当前已经产生的日志量,flush_to_disk_lsn是已经刷新到log file的日志量
checkpoint_lsn
log file中小于 checkpoint_lsn_offset的都可以进行覆盖
可以使用 SHOW ENGINE INNODB STATUS; 查看lsn、flush_to_disk_lsn、checkpoint_lsn各参数值。
log file 与 lsn偏移量关系
lsn默认值8704,log file 默认开始的偏移量是2048,lsn在log file中的偏移量: 当前lsn - 8704 + 2048
崩溃恢复
崩溃恢复,就是MySQL重启时照着redo log中的最后一次Checkpoint之后的日志回放一遍
-
确认恢复起点 checkpoint_lsn之后的日志
-
确认恢复终点 log block header,记录了使用多少空间时,不足512kb就认为是终点
-
崩溃恢复优化
- 统一处理redo日志:表空间号+页号作为key,redo日志集合作为value
- 跳过已经刷新到磁盘的页: checkpoint_lsn之后的日志,后台线程不断的从flush链表中刷盘,可能有一部分已被刷盘了,没必要再执行。 如何确认的:页头FileHeader中有一个FIL_PAGE_LSN属性,记录最近一次修改页面的lsn值(控制块的n-m),如果checkpoint_lsn小于页记录的lsn则不需要刷