为什么要两阶段提交
事务提交之后,redo log和bin log 都是需要1持久化到磁盘中,但是这两个是独立的逻辑,可能出现半成功的状态,这样就造成两份日志之间的逻辑不一致。如:
以id=1,name = ‘小明’执行
update stu set name = '小红' where id = 1
会出现如下两种情况:
- 如果在将redo log刷入到磁盘之后,Mysql突然宕机了,而bin log还没来得及写入。Mysql重启后,,通过redo log能将Buffer Pool中id = 1这行数据的name字段更新到‘小红’但是bin log由于没有写入,所以从库中的数据还是‘小明’,出现了主从不一致的情况
- 如果bin log写入了磁盘之后,,Mysql突然宕机了,而redo log还没来得及写入。由于redo log没有写入数据,重启之后主库不存在这条数据,但是从库已经同步了这条数据就会出现,主从不一致的情况
Mysql为了避免出现两份日志之间的逻辑不一致的问题,使用‘两阶段提交’来解决,两阶段提交其实是解决分布式事务一致性协议的,它可以保证多个逻辑操作,要不成功,不要全都失败,不会出现半成功半失败的情况。
两阶段提交把单个事务提交分为两个阶段,分别是“准备”和“提交”阶段
两阶段提交的过程是怎么样的
当客户端执行commit语句或者在自动提交的情况下,Mysql内部开启一个XA事务,分为两个阶段来完成XA事务的提交
事务提交的过程分为两个阶段,就是将redo log的写入拆分为两个步骤:prepare和commit,中间在穿插写入bin log,具体步骤如下:
- prepare阶段:将XID(内部XA事务的ID)写入到redo log,,同时将redo log对应的事务状态设置为prepare,然后将redo log持久化到磁盘
- commit阶段:将XID写入到bin log然后将bin log持久化到磁盘,接着提交事务,将redo log状态设置为commit,此时该状态并不需要持久化到磁盘,只需要写入到文件系统的缓存页中就可以,因为只要bin log写到磁盘中成功,就算redo log状态还是prepare也没有关系,一样会被认为事务已经执行成功
异常重启会发送什么现象呢
不管是时刻A(redo log写入磁盘,bin log没有)出现问题还是时刻B(redo log和bin log都写入到磁盘,但是还没有写入commit标识)崩溃,此时得redo log都是处于prepare的状态
在Mysql重启之后,都会按照顺序扫描redo log文件,碰到处于prepare状态的redo log,就会拿着redo log中的XID去bin log中去查找是否存在这个XID:
- 如果bin log中没有当前内部XA事务的XID,说明redo log完成了刷盘,但是bin log还没有刷盘,则回滚事务,对应时刻A
- 如果bin log中有当前内部事务的XID,说明redo log和bin log都已经完成了刷盘,则提交事务,对应时刻B
可以看到,对于处于prepare阶段的redo log,既可以提交事务,也可以回滚事务,这取决于能否在bin log中查找到与redo log相同的XID,如果有就提交事务,没有就回滚事务。这样就可以保证redo log和 bin log的一致性
两阶段提交出现的问题
两个阶段虽然保证了两个日志文件的数据一致性,但是性能不高,主要的原因是:
- 磁盘I/O次数过高:每一个事务提交都是需要两次刷盘(redo和bin)
- 锁竞争激烈:两个阶段虽然可以保证“单个事务”的两个日志一致,但是不能保证“多个事务“情况下,两者提交的顺序一致性。因此,在两个阶段提交流程的基础上,还需要加上一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志提交的顺序一致
为了解决上诉问题,引用有一个新的技术”组提交“,当多个事务提交的时候,会将多个bin log刷盘操作合并成一个,从而减少磁盘I/O的次数。如果10个事务一次排队刷盘的时间成本是10,那么将着10个事务一次性一起刷盘的时间成本近似.
引入组提交及之后,prepare阶段不变,只针对于commit阶段,将commit阶段拆分为三个过程:
- flush阶段:多个事务按照将进入的顺序将bin log从cache写入文件(不刷盘)
- sync阶段:对于bin log文件进行fsyn操作(多个事务的bin log合并有一次刷盘)
- commit:各个事务按照顺序做InnoDB的提交操作
上面的每一个阶段都有一个队列,每个阶段都有锁进行保证事务写入的顺序性,第一个进入队列的事务会成为 leader,leader领导所在队列的所有事务,全权负责整队的操作,完成后通知队内其他事务操作结束
每个阶段引入队列,锁只是针对队列进行保护,不会锁住整个提交事务的整个过程。锁的粒度减小了,这样就可以多个阶段并发执行,提升效率
有 binlog 组提交,那有 redo log 组提交吗?
这个要看 MySQL 版本,MySQL 5.6 没有 redo log 组提交,MySQL 5.7 有 redo log 组提交
在 MySQL 5.6 的组提交逻辑中,每个事务各自执行 prepare 阶段,也就是各自将 redo log 刷盘,这样就没办法对 redo log 进行组提交。
所以在 MySQL 5.7 版本中,做了个改进,在 prepare 阶段不再让事务各自执行 redo log 刷盘操作,而是推迟到组提交的 flush 阶段,也就是说 prepare 阶段融合在了 flush 阶段。
这个优化是将 redo log 的刷盘延迟到了 fush 阶段之中,sync 阶段之前。通过延迟写 redo log 的方式为 redolog 做了一次组写入,这样 binlog 和 redo log 都进行了优化。
标签:bin,事务,log,数据库,Update,阶段,提交,redo From: https://blog.csdn.net/loss_rose777/article/details/145264442