- ">1. 在两阶段提交的不同瞬间,MySQL异常重启,是怎么保证数据完整性的?

- 2. 拓展追问
- 追问1:MySQL怎么知道binlog是完整的?
- 追问2:redo log 和 binlog是怎么关联起来的?
- 追问3:处于prepare阶段的redo log加上完整binlog,重启就能恢复,MySQL为什么要这么设计?
- 追问4:如果这样的话,为什么还要两阶段提交呢?干脆先redo log写完,再写binlog。崩溃恢复时,必须得两个日志都完整才可以。是不是一样的逻辑?
- 追问5:不引入两个日志,也就没有两阶段提交的必要了。只用binlog来支持崩溃恢复,又能支持归档,不就可以了?
- 追问6:那能不能反过来,只用redo log,不要binlog?
- 追问7:redo log一般设置多大?
- 追问8:正常运行中的实例,数据写入后的最终落盘,是从redo log更新过来的还是从buffer pool更新过来的呢?
- 追问9:redo log buffer是什么?是先修改内存,还是先写redo log文件?
- 业务设计问题
- 思考
1. 在两阶段提交的不同瞬间,MySQL异常重启,是怎么保证数据完整性的?

图1 两阶段提交示意图
现在我们来分析一下在两阶段提交的不同时刻,MySQL异常重启会出现什么现象?
- 如果在时刻A(写入redo log 处于prepare阶段之后、写binlog之前),发生了崩溃(crash),由于此时binlog还没写,redo log也还没提交,所以崩溃恢复的时候,这个事务会回滚。这时候,binlog还没写,所以也不会传到备库。到这里,大家都可以理解。
问题在时刻B(binlog写完,redo log还没commit前发生crash),那崩溃恢复时MySQL怎么处理?
我们先来看一下崩溃恢复时的判断规则。
- 如果redo log里面的事务是完整的,也就是已经有了commit标识,则直接提交;
- 如果redo log里面的事务只有完整的prepare,则判断对应的事务binlog是否存在并完整:
a. 如果是,则提交事务;
b. 否则,回滚事务。
这里,时刻B发生crash对应的就是2(a)的情况,崩溃恢复过程中事务会被提交。
2. 拓展追问
追问1:MySQL怎么知道binlog是完整的?
追问2:redo log 和 binlog是怎么关联起来的?
追问3:处于prepare阶段的redo log加上完整binlog,重启就能恢复,MySQL为什么要这么设计?
追问4:如果这样的话,为什么还要两阶段提交呢?干脆先redo log写完,再写binlog。崩溃恢复时,必须得两个日志都完整才可以。是不是一样的逻辑?
回答:其实,两阶段提交是经典的分布式系统问题,并不是MySQL独有的。
如果必须要举一个场景,来说明这么做的必要性的话,那就是事务的持久性问题。
- 对于InnoDB引擎来说,如果redo log提交完成了,事务就不能回滚(如果这还允许回滚,就可能覆盖掉别的事务的更新)。
- 而如果redo log直接提交,然后binlog写入的时候失败,InnoDB又回滚不了,数据和binlog日志又不一致了。
两阶段提交就是为了给所有人一个机会,当每个人都说“我ok”的时候,再一起提交。
追问5:不引入两个日志,也就没有两阶段提交的必要了。只用binlog来支持崩溃恢复,又能支持归档,不就可以了?
追问6:那能不能反过来,只用redo log,不要binlog?
追问7:redo log一般设置多大?
追问8:正常运行中的实例,数据写入后的最终落盘,是从redo log更新过来的还是从buffer pool更新过来的呢?
追问9:redo log buffer是什么?是先修改内存,还是先写redo log文件?
业务设计问题
业务上有这样的需求,A、B两个用户,如果互相关注,则成为好友。设计上是有两张表,一个是like表,一个是friend表,like表有user_id、liker_id两个字段,我设置为复合唯一索引即uk_user_id_liker_id。语句执行逻辑是这样的:
以A关注B为例:
第一步,先查询对方有没有关注自己(B有没有关注A)
select * from like where user_id = B and liker_id = A;如果有,则成为好友
insert into friend;没有,则只是单向关注关系
insert into like;但是如果A、B同时关注对方,会出现不会成为好友的情况。因为上面第1步,双方都没关注对方。第1步即使使用了排他锁也不行,因为记录不存在,行锁无法生效。请问这种情况,在MySQL锁层面有没有办法处理?
思考
当MySQL去更新一行,但要修改的值跟原来的值相同,这时MySQL会真的去执行一次修改吗?
- InnoDB认真执行了“把这个值修改成(1,2)”这个操作,该加锁的加锁,该更新的更新。
