两阶段提交

日志相关问题(binglog(归档日志)、redo log(重做日志)) - 图1

在两阶段提交的不同时刻,MySQL异常重启会出现什么现象?

  • 当在时刻A的地方,也就是写入了redo log处于prepare阶段,写binlog之前,发生了crash,此时由于还没有写binlog,redo log也还没有commit,因此,崩溃恢复的时候这个事务会回滚。这个时候binlog还没有写,也不会同步到备份库中。
  • 当在时刻B,也就是写完binlog之后系统发生了crash,崩溃恢复时分两种情况

    • 如果redo log里面的事务是完整的,也就是有了commit标识,则直接提交。
    • 如果redo log里面的事务只有完整的prepare,则判断对应的binlog的事务是否存在并完整
      • 如果binlog不完整,则回滚事务
      • 如果binlog完整,则提交事务

        MySQL是如何知道binlog是否完整的?

        一个事务的binlog是有完整格式的;
  • statement格式的binlog,最后会有COMMIT;

  • row格式的binlog,最后会有XID event

MySQL5.6.2版本之后,引入了binlog-checksum参数,用来验证binlog内容的正确性。对于binlog日志由于磁盘的原因,可能会在存储日志的过程中出错,MySQL可以通过checksum的结果来发现。所以MySQL还是有办法验证事务binlog的完整性的。

binlog和redo log是怎么关联起来的?

binlog和redo log有一个共同的字段XID。崩溃恢复的时候,会按顺序扫描redo log

  • 如果碰到既有prepare,又有commit的redo log就直接提交
  • 如果碰到只有prepare的redo log,就拿着XID去binlog找对应的事务

    为何还需要两阶段提交?

    两阶段提交是经典的分布式问题,并不是MySQL独有的。
    双重保障。

    为什么binlog不支持崩溃恢复?

    binlog没有能力恢复数据页,一个事务的binlog如果回放,就是重做这个事务,一个事务更新的可能不止一个page。
    比如一个事务更新了page ABC
    然后崩溃回复了,B坏了,AC没问题,而且AC还落盘了。
    这样如果重做事务,B好了,AC又坏了。

    能不能只用redo log,不用binlog?

    binlog有着redo log无法替代的功能,如主从复制,就是通过binlog实现的。

    数据最终写入磁盘,是从redo log更新的吗?

    不是,因为redo log的大小是固定的,并没有记录完整的数据页的数据,所以它没有能力去更新磁盘数据。
  1. 正常运行的实例,数据页被修改之后,内存中的数据页与磁盘中的不一致,就称为脏页。最终数据落盘,就是把内存中的数据页写入磁盘。这个过程甚至与redo log毫无关系。
  2. 崩溃恢复的场景,InnoDB如果判断到一个数据页可能在崩溃恢复的时候丢失了更新,就会把它读到内存中,然后同redo log更新内存的内容,就又变成了脏页,成为上边的情况。

    redo log buffer

    一个事务中可能需要多次写入redo log,InnoDB引擎在更新完内存之后,先将更新记录到redo log buffer中,也是内存,在事务提交的时候,将日志写入到redo log中持久化。