事务的两阶段提交

未引入两阶段提交:异常情况举例说明

假设我们执行一条SQL语句,修改name为sh : update user set name = ‘sh’ where id = 1 。

异常1

假设先写入redo log 成功,但是没来得及写入bin log ,系统崩了。在MySQL重启后,可以根据redolog把记录更新成’sh’,但是,binlog由于没写成功,所以他是没有记录下来这次变更的,那么也就意味着,主备同步的时候,是缺了一条SQL的,导致主备库之间数据不一致。

异常2

那么,如果换个顺序,先写入binlog成功,但是没来及的写入redolog,系统崩了。在MySQL重启之后,崩溃恢复的时候由于redo log还没写,所以什么都不用做,数据库记录还是旧值。但是因为binlog已经写入成功了,所以在做主备同步的时候,就会把新值同步到备库,就导致了主备库之间数据不一致。

总结

如上面的例子,如果不引入二阶段提交的话,binlog和redolog没办法保证一致性的情况下,就会导致主备库之间的数据不一致。为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。

两阶段提交

原理

redo log的写入拆成了两个步骤preparecommit,就是两阶段提交

image.png

图中2阶段提交的过程是:

Prepare 阶段

这个阶段 SQL 已经成功执行并生成 redolog,处于prepare阶段

BinLog持久化

binlog 提交,通过 write() 将 binlog 内存日志数据写入文件缓冲区;
通过fsync() 将 binlog 从文件缓冲区永久写入磁盘。

Summary

write 和 fsync 是与文件系统和磁盘IO相关的两个不同的操作。
write 操作将数据写入文件的缓冲区,这意味着 write 操作完成后,并不一定立即将数据持久化到磁盘上,而是将数据暂时存储在内存中。
fsync 用于强制将文件的修改持久化到磁盘上。它通常与 write 配合使用,以确保文件的修改在 fsync 操作完成后被写入磁盘。

Commit

在执行引擎内部执行事务操作,更新redolog,处于Commit阶段。

引入两阶段提交后异常情况分析

写binlog时发生异常

使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志或者不完整,就会回滚该事务。这样主备是一致的,就都没有执行这个事务。

写完binlog时发生异常

此时,redolog处于prepare状态,binlog已写入,binlog 中的事务存在并且完整,就会提交事务恢复数据。这样主备是一致的,就都执行了事务。

总结

以上异常情况分析可以看出,两阶段提交能够确保数据的一致性。

如何判断binlog和redolog达成一致?

当MySQL写完redolog并将它标记为prepare状态时,并且会在redolog中记录一个XID,它全局唯一的标识着这个事务。而当你设置sync_binlog=1时,做完了上面第一阶段写redolog后,mysql就会对应binlog并且会直接将其刷新到磁盘中。binlog结束的位置上也有一个XID。只要这个XID和redolog中记录的XID是一致的,MySQL就会认为binlog和redolog逻辑上是一致的。