回顾: InnoDB更新逻辑
了解了binlog之后,我们看InnoDB如何执行更新语句的。
- 执行器先找到ID=2这一行,由于ID是主键,引擎可以直接用树搜索到这一行。注意如果这一行所在的数据本来就在内存中,直接返回给执行器,否则需要从磁盘中读到内存里。
- 执行器拿到数据执行操作,得到一行新的数据,然后调用引擎接口写入这行新数据。
- 引擎将这行数据更新到内存中,同时将操作记录到redo log。此时redo log处于prepare阶段,告知执行器记录完了,随时可以提交
- 而后执行器生成这个操作的bin log,把bin log 写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚写入redo log的记录改为commit提交阶段,更新完成
两阶段提交
我们注意到写入redo log 的步骤分为prepare和commit两个阶段。这就是两阶段提交,主要为了让两份日志保持逻辑一致
binlog会记录所有操作,并追加写入,系统定期做整库备份。假设我们找到的整库备份与需求记录点差一天,我们只需要从这个点开始根据binlog重做这一天所有的操作即可。
假设不使用两阶段提交,而先写某一个日志,写完了再写另一个
假设场景为,ID=2,这一行c=0;要将其加1,更新完第一个日志,之后再写第二个日志的时候发生了crash。
- 假设先写redo log ,在写binlog 时crash。由于redo log由crash-safe特性,仍可以恢复c被加了1,此时c=1.但是由于binlog没写完,如果用binlog恢复只会得到c=0。
- 假设先写bin log,后写redo log,由于还没有写redolog,会认定为这个事务无效,c还是=0。但是因为我已经写了binlog,如果用Bin log恢复就会多一个事务。
总之,如果不使用两阶段提交,就会导致用不同log恢复的状态不一样,即两份log逻辑无法保持一致。常见的做法是全量备份,再应用bin log
MySQL异常重启,怎么保证数据完整性
崩溃恢复规则:
- 如果redo log事务完整,(这个完整指已经有可commit标识),则直接提交
- 如果redo log事务只到prepare,则判断binlog,binlog完整直接提交,否则回滚
两种崩溃情形:
- 如果处于:写redo log处于prepare阶段,但在写binlog 之前,发生了崩溃:此时Binlog 还没写,即不会传到备库,redo log也没有提交,所以崩溃恢复很简单,直接回滚事务即可。
- 如果处于:binlog