Redo log
WAL技术
WAL,即Write-Ahead Logging,是一种用于保证数据库事务持久性和一致性的日志记录机制。它的基本思想是在对数据库进行实际修改之前,先将修改操作记录到日志中。以确保即使在系统崩溃或故障的情况下,也能通过日志恢复数据。简单说就是先写日志,再写磁盘。
在MySQL的InnoDB存储引擎中,当我们向MySQL更新一条记录的时候,存储引擎会把记录顺序的写到redo log中,然后更新InnoDB中的buffer pool,而并不真正的更新磁盘中的数据页。等之后InnoDB存储引擎有空的时候,再把redo log中的记录刷到磁盘上,完成数据最终的持久化。
由于磁盘的顺序写入效率要远高于随机写入,并且还有组提交等优化,WAL这种方式可以大大提高更新的效率。
实现机制
Redo log是Innodb存储引擎提供的用于优化更新操作的日志。它的结构可以看成是个环形的磁盘区域。
如上图的redo log由四个文件组成,当向redo log中进行顺序写时,就是依次的写这四个文件,当写满时,就再从第一个文件开始覆盖写入。write_pos就是当前磁头的位置,一边写一边后移,checkpoint则是当前擦除的位置点,他们之间就是可以写的区域。
Redo log大小是固定的,默认是4GB(通过参数innodb_log_file_size和innodb_log_files_in_group进行配置)。Redo log不仅提高了数据更新时的效率,还让InnoDB拥有了Crash-Safe的能力,即不会因为宕机而丢数据。
和binlog的区别
redo log是InnoDB特有的,是存储引擎层的,用于crash-safe;binlog是Server层的,所有引擎都可以使用,主要用于数据归档。
redo log是物理逻辑日志,会记录数据页的位置,数据页内部则是逻辑日志,而binlog是单纯的逻辑日志,记录的就是每行的数据。
redo log有固定大小,写满了时需要进行擦除,擦除就要强制刷盘,binlog是没有大小限制的,在写到一定大小时,可以开启一个新文件接着写入。
什么是物理日志、逻辑日志、物理逻辑日志:
物理日志记录的是具体的对磁盘中数据页的操作,比如在哪个位置,做了哪个修改,只对本机有效。而逻辑日志,可以理解为SQL语句,人类是可读的。物理逻辑日志如redo log,则是物理日志和逻辑日志的结合,记录页位置时是物理的,记录数据内容时则是逻辑的。
两阶段提交
由于原本的MySQL只有MyISAM引擎,它不支持crash-safe,所以binlog也没有crash-safe的能力(因为它没有checkpoint,存储引擎不知道该从哪恢复)。而InnoDB存储引擎为了支持crash-safe,它又不能改服务器层的binlog,就增加了Redo log作为保证crash-safe的工具。而这两个日志同时存在,并且都会记录数据的更新操作,所以就需要保证两个日志文件上的记录是一致的。两阶段提交本质解决的就是redo log和binlog的数据一致性的问题。
过程
如下图是两阶段提交的过程:
InnoDB接到了Server层执行器的更新数据请求,会先将这行数据写入undolog(用于事务回滚的日志)。
然后写入InnoDB的buffer pool中。
然后将这个操作记录到redo log中,此时redo log处于Prepare状态,然后InnoDB会告知Server层的执行器,事务已经执行完,随时可以提交。
MySQL执行器在收到通知后,就会写入binlog,并调用InnoDB的接口进行提交。
最后InnoDB将刚刚写入redo log的语句置为Commit状态,本次事务就结束了。
两阶段提交的重要性
为什么给redo log设置了两个状态,而不是直接直接写redo log和binlog?
假如只是先写redo log然后再写binlog:
假如redo log写完,binlog没写完时MySQL挂了,后续恢复时,可以通过redo log恢复这次的数据,但是binlog中却没有记录,就会导致binlog中和实际数据库中存储的数据不一致。后续如果要使用这个binlog来恢复临时库的话,就会出现数据不一致。
假如只是先写binlog然后再写redo log
假如binlog写完,redolog没写时Mysql挂了,就会导致重启后无法恢复(因为redo log没有),但是binlog中却记录了这次操作,后续使用binlog恢复临时库时,就会比源库多一次操作出来,同样导致数据不一致。
异常情况的处理
首先我们观察一下redo log在整个两阶段提交中的状态变化,然后分析一下任意一步时,MySQL异常该如何处理:
写入redo log(Prepare)
写入binlog
写入redo log(Commit)
如果在redo log的Prepare阶段到写入binlog之间MySQL宕机,此时redo log处于prepare阶段,而binlog未写入。此时MySQL重启时,发现redo log处于prepare阶段,就会检查binlog,观察到binlog中没有这条事务的记录,则会忽视这条redo log。redo log和bin log之间可以通过事务ID对应(事务ID即transaction_id,在每个事务开始时分配,递增且全局唯一)。
如果在写入binlog和redolog的Commit阶段之间MySQL宕机,则redo log仍是处于prepare阶段,但是binlog已经写入了,此时MySQL重启时,发现redo log处于Prepare阶段,就会检查binlog,观察到binlog是完整的,则会自动将这条redo log提交,并恢复数据。
总结
InnoDB引擎通过redo log实现了MySQL的Crash-Safe,再通过两阶段提交保证redo log和binlog的一致性。
两阶段提交实际上是也分布式事务的XA规范的应用,XA规范也是分布式系统维持数据逻辑一致性时的常用方案。
参考资料
[1] 极客时间《MySQL实战45讲》
评论区