上一篇写了 Buffer Pool,Buffer Pool 确实是能提升读写效率。不过内存中的数据一断电就消失了,并不是真的持久化了。
这时候就需要 redo log 来保证数据的持久化。也就是 ACID 中的 D 属性。
问题
redo log 是怎么保证数据持久化的呢?
为什么不直接持久化数据,而要持久化 redo log?
checkpoint
第一个问题,简单来讲就是每次 update、insert、detele 语句,在 Buffer Pool 中修改的同时,会在 redo log 中写一条记录并持久化下来。
这就很奇怪,为什么一样反正都是持久化,为什么非要持久化 redo log而不是直接持久化数据呢?
每次 update、insert、detele 的数据行都不一定在同一个扇区,可以想象一下,直接持久化数据的话,磁盘的磁针(对于机械硬盘而言)应该就是每次修改都要转来转去,也就是随机 IO,那效率是极低的。
而 InnoDB 的 redo log 在设计时就尽量保持 Redo Log 存储在一段连续的空间上,因此在系统第一次启动时就会将日志文件的空间完全分配,以顺序追加的方式记录 Redo Log,这种顺序 IO相比上面的随机 IO效率高了不少。
类似 Buffer Pool,redo log 也有一个 redo log buffer 区域,也是在内存中。那么 redo log buffer 是怎么怎么保证信息在崩溃的时候不会丢失呢?
这是由一个参数innodb_flush_log_at_trx_commit
控制的,该参数有 0,1,2 三个值可选,默认是 1。
0:事务提交会写在 redo log buffer 中,然后每秒钟写入操作系统缓存并调用fsync()
刷到磁盘。(这里有个用户态和内核态的概念,如下图,在类 unix 系统中的默认实现是调用fsync()
)
1:默认值,每次提交事务向磁盘同步 redo log。
2:每次提交事务将 redo log 写到操作系统的缓冲区中,然后每秒钟调用fsync()
刷到磁盘。
这里系统调用 fsync()也不是固定的,在不同的操作系统上都有多种不一样的解决方式,Mysql 官网上给出的就有以下几种。
那持久化之后的 redo log,那么多日志,究竟从哪里开始是没有持久化的,哪里是已经持久化的呢?
这就依赖着checkpoint
,Mysql 采用 redo log 可以理解为写数据文件是"异步"
的,写日志文件是"同步"
的。
数据库实例崩溃时,内存中的 Buffer Pool 中的修改过的数据,可能没有写入到数据块中。数据库在重新打开时,需要进行恢复 Buffer Pool 中的数据状态,并确保已经提交的数据被写入到数据块中。checkpoint 是这个过程中的重要机制,通过它来确定,恢复时哪些重做日志应该被扫描并应用于恢复。
可以理解为数据库重启时在 redo log 的checkpoint
之后开始恢复数据,而 redo log 是一个环,checkpoint
之前区域又可以被覆盖以重新利用。
才疏学浅,理解不到位、有误的地方欢迎大佬们指正!
扫码关注峡谷程序员
! [免费获取更多学习资料]