前言
MySQL通过两阶段提交的机制,保证了redo log和bin log的逻辑一致性,进而保证了数据的不丢失以及主从库的数据一致。
而说起两阶段提交,就不得不先介绍一下redo log和bin log。
redo log
redo log即重做日志,是InnoDB引擎特有的一种日志(有的面试官经常问到这一点)。
redo log主要做什么呢?
以更新数据为例,我们知道,MySQL的数据是存储在磁盘上的,如果每一次更新数据,都去磁盘寻址找到要更新的数据,进行更新操作的话,这个IO成本是非常高的。
如果是固态硬盘还好,如果是机械硬盘,那么MySQL的更新性能根本无法满足我们的业务需要。
所以,MySQL采用了一种叫做WAL的技术,Write-Ahead Logging。
当更新数据时,将更新操作(即某个数据页上做了什么修改)先写到redo log里面,然后更新内存,这个更新操作就算完成了。MySQL会在服务器空闲的时候,把redo log的操作记录刷新到磁盘里,以保持数据的一致性。
需要注意的是,redo log虽然也是磁盘上的一个文件,但是由于操作是顺序写,所以性能是非常高的。
当然了,redo log也是有大小上限的,不可能无限制的写入。
以上图为例,配置了4个redo log,write pos就是代表当前记录写到什么位置了,而check point表示一个推进点,它会不断的前移,做擦除数据的操作,以保证redo log可以不断的写入。
当然,擦除数据之前,会把redo log的记录刷新到磁盘。
通过redo log,可以保证即使MySQL发生异常重启,数据也不会丢失(因为redo log是物理日志,可以进行重放),这个特性就叫做crash-safe。
bin log
bin log是MySQL Server提供的一种日志,叫做归档日志,所有引擎都可以使用bin log。
那bin log和redo log的区别是什么呢?
1,这两种日志的提供者不同:bin log是由MySQL Server提供的,redo log是InnoDB引擎特有的。
2,redo log主要记录的是某个数据页做了什么修改,bin log记录的是语句的原始逻辑,比如更新了某一行的某个字段。
3,redo log是循环写的,数据会被覆盖。bin log是追加写,一个文件写满,就写下一个文件。
两阶段提交
介绍完了redo log和bin log,我们再看一下他们两者是如何配合完成两阶段提交的。
上图就是一个更新数据的流程,可以看到,在更新一条数据之前,MySQL会先将数据加载到内存,然后更新内存,开始写redo log。
此时,redo log处于prepare状态,等到bin log写完之后,再提交事务,这一条记录的更新操作就算完成了。
redo log prepare -> 写bin log -> redo log commit,这个流程就叫做两阶段提交。
下面我们分析一下,采用两阶段提交的好处。
情景一,redo log处于prepare状态时,如果写bin log失败了,那么更新失败,此时redo log没有commit,bin log也没有记录,两者的状态是一致的,没有问题。
情景二,redo log处于prepare状态时,写bin log成功,但是宕机导致commit失败了。此时bin log产生了记录,redo log没有写入成功,数据暂时不一致。
但是不用担心,当MySQL重启时,会检查redo log中处于prepare状态的记录。在redo log中,记录了一个叫做XID的字段,这个字段在bin log中也有记录,MySQL会通过这个XID,如果在bin log中找到了,那么就commit这个redo log,如果没有找到,说明bin log其实没有写成功,就放弃提交。
通过这样的机制,保证了redo log和bin log的一致性。
总结
之所以MySQL中既存在redo log,又存在bin log,这是因为bin log是MySQL Server提供的一种归档日志,其本身并不具备crash-safe能力。而redo log本身不具备归档能力,他是一种循环写的日志。
MySQL通过将这两种日志整合起来,并通过两阶段提交的机制,保证了数据的一致性。