1、两阶段提交的流程
其实所谓的两阶段就是把一个事务分成两个阶段来提交。
上面为两阶段提交的时序图。
你可以粗略的观察一下上图,MySQL想要准备事务的时候会先写redolog 、 binlog分成两个阶段
两阶段提交的第一阶段(prepare阶段):写redo-log并将其标记为Prepare状态。
紧接着写binlog
两阶段提交的第二阶段(commit阶段):写bin-log并将其标记为commit状态。
2、两阶段写日志用意?
你有没有想过这样一件事,binlog默认都是不开启的状态!
也就是说,如果你根本不需要binlog带给你的特性(比如数据备份恢复、搭建MySQL主从集群),那你根本就用不着让MySQL写binlog,也用不着什么两阶段提交。
只用一个redolog就够了。无论你的数据库如何crash,redolog中记录的内容总能让你MySQL内存中的数据恢复成crash之前的状态。
所以说,两阶段提交的主要用意是:为了保证redolog和binlog数据的安全一致性。只有在这两个日志文件逻辑上高度一致了。你才能放心的使用redolog帮你将数据库中的状态恢复成crash之前的状态,使用binlog实现数据备份、恢复、以及主从复制。而两阶段提交的机制可以保证这两个日志文件的逻辑是高度一致的。没有错误、没有冲突。
当然,两阶段提交能做到足够的安全还需要你合理的设置redolog和binlog的fsync的时机,而这块知识点所涉及到的参数前几篇文章已经说过。如果不记得,可以去看下。
3、syn_binlog=1问题
这个参数控制binlog的落盘时机,并且白日梦也知道你们公司线上数据库的该参数一定被设置成了1。
Notice!!! 这个参数为1时,表示当事物提交时会将binlog落盘。
现在你用15s中的时间,思考一下,蓝色句子中说的事物提交时会将binlog落盘,这个提交时,是下图中的step1时刻呢?还是step2时刻呢?
答案是:step1时刻!
知道这个知识点很重要,下面我来描述这样一个场景。
假如要执行一条update语句,那你肯定知道,先写undolog(便于后续对Update事务的回滚)。然后你的update逻辑将BufferPool中的缓存页修改成了脏页。
当你准备提交事务时(也就是step1阶段),会写redo log,并将其标记为prepare阶段。然后再写binlog,并将binlog落盘。
然后发生了以外了,MySQL宕机了。
那我问你,当你重启MySQL后,update对BufferPool中做出的修改时会被回滚还是会被提交呢?
答案是:会根据redolog将修改后的recovery出来,然后提交。
那为什么会这样做呢?
其实总的来说,不论mysql什么时刻crash,最终是commit还是rollback完全取决于MySQL能不能判断出binlog和redolog在逻辑上是否达成了一致。只要逻辑上达成了一致就可以commit,否则只能rollback。
比如还是上面描述的场景,binlog已经写了,但是MySQL最终选择了回滚,那代表你的binlog比BufferPool(或者Disk)中的真实数据多出一条更新,日后你用这份binlog做数据恢复,是不是结果一定是错误的?