前言
提到两阶段提交,我们都知道是关于MySQL中其日志系统中的redo log、bin log 来实现的。那么他们是干嘛的?为什么要采取两阶段提交?
两大日志
redo log
你肯定见识过以下场景:记账的时候,我们会有一个记账本,记录着关于谁谁谁的账单,当忙碌起来的时候,如果还想找到关于xxx的账单,每次都得翻阅记账本,找到相应处再记录,这效率是不是很低?如果我们能增加一个黑板,那么我们只需要在上面记录关于账单的人名和金额,等待空闲的时候再填入进账单本,效率是不是就大大提高了。所以,MySQL就是类似这个思路来设计redo log的。我们可以把黑板类比为redo log,当有数据更新的时候,先写入到redo log中,再更新内存,等到空闲的时候再刷入磁盘。那么你可能会疑惑?如果内存满了呢?那么只能先把现在的redo log内容刷入磁盘,再继续在内存中写入。
在大致理解了redo log是什么后,那么它可以干嘛?这么说吧,redo log让MySQL有了崩溃恢复的能力。
例如上图,redo log是循环写的,check point是当前要刷入磁盘的位置,write pos是写入的位置,每次写入,write pos都会向下移动,刷入磁盘时,check point也会向下移动,当write pos追到check point表明(曲线表明还可写入redo log的数据),redo log满了,此时会刷数据进入磁盘,然后推进check point。
bin logo
bin logo存在于服务层(Server)。如果有这么一种需求:因为误操作,我想将数据库恢复到误操作之前某一个时刻,那么bin logo就派上用场了。同时,它也用于数据备份、主备、主从、主主,保证数据一致性。bin logo会记录每一条数据更新的逻辑。
它具有以下几种模式:
- statement:这个模式下,会记录更新操作逻辑。例如会记录update tableName set id = xx
- row:会记录行的内容,例如更新前后的数据。
写入时机
redo log在事务执行过程中,是可以不断写入的。而bin logo是等待事务提交后再写入的。
ps:innodb_flush_log_at_trx_commit这个参数设置成1的时候,表示每次事务的redo log都直接持久化到磁盘。
sync_binlog这个参数设置成1的时候,表示每次事务的binlog都持久化到磁盘。
为什么需要两种日志
MySQL一开始是没有innoDB这一引擎的,自带的引擎为MyISAM,但是其并没有处理重启、宕机这一突发情况的能力,而bin log只能用于备份恢复、归档。而InnDB是以插件(plugin)的形式引入的,为了能够处理这种特殊情况,所以就有了redo log。
为什么需要两阶段提交
两阶段提交指的是:redo log写入时机具有prepare以及commit状态。
例如执行update tableName age = 10 where id = 3(假设未更新前age为5);执行流程大致如下:
mysql更新的时候,如果数据不在内存,会先更新在内存中,之后等待合适的时机再更新到磁盘中去。
想象一下:
如果在redo log的prepare阶段,mysql突然重启、宕机情况下,会发生什么:redo log已经写入age为10,但是bin logo并未写入,重启后,age为10,但是在之后用bin logo进行恢复的话,会丢失age更新为10这条语句,导致数据不一致。
反过来,先写bin logo,突然重启or宕机,bin logo成功写入,但是redo log未写入,在进行重启时,age为5;但是待之后恢复时,却出现了age为10的情况,导致数据不一致。
引入两阶段提交后:
- 当redo log处于prepare阶段,突然重启or宕机,还未写入bin log,MySQL会丢失此次更新,回滚。备份恢复和重启恢复数据一致。
- 当redo log处于prepare状态,且bin log写入成功,突然重启or宕机;MySQL承认此次更新,重启后会自动commite。备份恢复和重启恢复数据一致。
如果有误,请务必告诉我,愿我们一起努力进步!