事务的两阶段提交
未引入两阶段提交:异常情况举例说明
假设我们执行一条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
的写入拆成了两个步骤prepare
和commit
,就是两阶段提交。
图中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逻辑上是一致的。