MYSQL-01(补充) crash-safe&两阶段提交

285 阅读2分钟

crash-safe依赖redo log实现。 redo log存储的是页的物理日志。即检查每页有什么改动。 redo log可以设置为一组四个文件。上文提到的checkpoint表示刷新到磁盘上的日志序列号,即LSN。每个页上也有自己的LSN。 当mysql异常重启,系统会自动检查redo log的lsn与每个页的lsn,当redo log的lsn大于页的lsn时,则表示redo log中的数据未完全写入数据库。那么系统将从页的lsn开始,从redo log中恢复数据。

两阶段提交

我们先回顾下上文讲到的update执行流程。

graph TD
1[执行器获取行数据] --> 2[存储引擎查找内存] ---> 3[查找成功] & 4[查找失败从磁盘中读入数据] ---> 5[数据返回执行器] --> 6[执行器执行修改逻辑] --> 7[调用存储引擎写入数据] ---> 8[数据更新至内存] ---> 9[写入redo log,redo log处于prepare状态] ---> 10[执行器获取执行结果,写入binlog,调用提交事务接口] ---> 11[提交食物,处于commit状态]

从以上流程,我们可以看出执行器写入数据时拆分成prepare和commit两个状态。为什么要这么做呢?主要是为了两份log保持一致。

怎样数据库恢复到一周前的某一刻?

上面提到两阶段提交是为了让两份log保持一致,那么两份log保持一致有什么作用呢?

我们先从数据恢复的过程说起。

前面有提到过,binlog会记录所有的逻辑操作,并且是采用“追加写”的形式。如果系统保存了一段时间以内的binlog,同时系统会定期做整库备份。当需要恢复到指定时间时,我们可以找到最近一次的全量备份,然后从备份的时间点开始,依次取出备份的binlog,重放到需要恢复的那个时刻。这样我们就可以获得需恢复节点的临时库了。

那么两阶段提交,也就是日志一致性在里面发挥了什么作用呢。我们来反推一下

假设执行一条将c值由0改为1的sql。

如果先写入redo log,后写入binlog,当redo log提交后系统崩溃,此时binlog还未写完,此时系统恢复,数据从redo log中恢复出来,c值为1,但binlog没有这条记录,恢复到临时库时并未执行c值设置为1的写入。那么恢复出来的数据为0。这样恢复出来的数据就是错误的。

如果先写入binlog。再写完binlog时发生crash,由于redo log还没写完,系统恢复后该事务无效,所以c值仍然为0。但从binlog恢复时,已经写入了c改为1的日志,恢复出来的数据为1。

innodb_flush_log_at_trx_commit这个参数设置为1时,每次事务提交redo log都直接持久化到磁盘。这样可以保证mysql异常重启之后数据不丢失。

sync_binlog这个参数设置为1时,表示每次事务的binlog都持久化到磁盘,这样可以保证mysql异常重启之后binlog不丢失。