日期: August 15, 2022
关于redo log 与binlog
mysql的log有多种,redo log、undo log、binlog、slowlog,而所谓的两阶段事务提交涉及的就是redo log和binlog
binlog特点
- binlog会原封不动的记录DDL和DCL语句,只记录已经提交事务的DML(增删改查)语句,binlog属于逻辑日志
- binlog不是innodb引擎独有的
- binlog只在事务提交完成后进行一次写入
- binlog不是循环使用,在写满或者重启之后,会生成新的binlog文件
- binlog可以作为恢复数据使用,主从复制搭建
redo log特点
- redo log记录的是DML操作时,数据页的变化,redo log属于物理日志
- redo log是innodb引擎独有的
- redo log由于其记录是物理操作日志,因此每个事务对应多个日志条目,并且事务的redo log写入是并发的,并非在事务提交时写入,而是在事务进行中不断的写入,其在文件中记录的顺序并非是事务开始的顺序
- redo log默认有两个文件,每个大小默认48M,ib_logfile0和ib_logfile1
- redo log可作为异常宕机或者介质故障后的数据恢复使用
双一配置
双一配置指的是mysql的两个配置参数
sync_binlog=1 控制binlog日志的刷盘策略
默认为0,mysql不控制binlog日志的刷新,交给文件系统自己控制,此时性能最好,但不安全,当为1时表示每写1次到binlog里面就立即刷盘
innodb_flush_log_at_trx_commit redo参数 0/1/2(默认1)
1表示在每次事务提交时立即刷新redo log到磁盘,commit成功
0表示每秒刷新日志到系统内存,同步到磁盘,异常宕机时会丢失1秒内事务
2表示每次事务提交都立即刷新redo缓冲池,缓冲池到系统内存再每秒同步刷新到磁盘
两阶段事务提交
MySQL通过两阶段提交过程来完成事务的一致性的,也即redo log和binlog的一致性的,理论上是先写redo log,再写binlog,两个日志都提交成功(刷入磁盘),事务才算真正的完成
例如:
我现在要给 id = 2 这一行的 c 字段加 1,到 MySQL 层面,它是如何去做的呢?
首先,mysql会先找到这条 id = 2 的数据,然后找到 c 字段进行加 1 操作,这个时候,引擎会将这行数据更新到内存中,同时把这个更新操作记录到 redo log 里面,这个时候 redo log 处于 prepare 状态,随后执行器生成这个操作的 binlog ,并且把 binlog 写入到磁盘完成之后,执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 从 prepare 状态改成 commit 状态,这样更新操作才算完成
注意redo log 是先 prepare 状态,等 binlog 写完之后,才是 commit 状态,这种方式就叫”两阶段提交”

所以如果说没有设置双1配置,意外宕机后可能会出现redo log与binlog不一致的情况,假如redo log有,而binlog没有,可以用redo log恢复完全数据,但这时如果想用binlog备份时,备份出来的结果就会少数据
那么为什么需要redo log呢
MySQL在更新数据时,为了减少磁盘IO,并不会直接更新磁盘上的数据,而是先更新Buffer Pool中缓存页的数据,等到合适的时间点,再将这个缓存页持久化到磁盘,Buffer Pool 中所有缓存页都是处于内存当中, 一旦断电或者宕机,buffer pool中的数据就丢了,于是redo log出现了
当进行增删改操作时,MySQL 会在更新 Buffer Pool 中的缓存页数据的同时,记录一条对应操作的redo log,如果buffer pool中的数据丢失了,可以利用redo log恢复
但redo log也不是立刻刷盘的,它也是先保存到redo log的一个buffer中,等到合适的时机再落盘(每次到这我总觉得这不就是套娃么哈哈,但其实还是有道理的)
合适的时机指:mysql正常关闭/默认每隔1s落一次盘/redo log buffer写入量超过 redo log buffer 内存的一半时,即超过8MB时/事务提交时,落盘(需要参数设置)
因此要严格保证数据不丢失,必须得保证 innodb_flush_log_at_trx_commit 配置为 1,即每次事务提交就刷新redo log到磁盘
既然每次事务提交就落盘,为什么不直接舍弃redo log,当事务提交的时候直接把数据落盘呢?
因为redo log 和数据两者的落盘机制不同,在写redo log 时,我们将redo log日志追加到文件末尾,虽然也是一次磁盘 IO,但这是顺序写操作(不需要移动磁头);
而直接将数据更新到磁盘时,涉及到的操作是将 buffer pool 中缓存页写入到磁盘上的数据页上,由于涉及到寻找数据页在磁盘的哪个地方,这个操作发生的是随机写操作;
顺序写和随机写的性能差距可是非常大的,所以就选择了redo log的机制