MySQL的两阶段提交

Posted 生活过于潦草的学习小屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL的两阶段提交相关的知识,希望对你有一定的参考价值。

mysql的两阶段提交

解决的问题:保证了事务在多个日志间的原子性

在MySQL中存在着两个日志系统:server层的binlog和执行引擎层的redolog,如何保证两个日志的原子性,即要么都提交要么都终止?

  • 在此MySQL使用了两阶段提交:

这里我借用了丁奇大佬的示意图。

两阶段提交保证了原子性,也保证了crash safe能力,这里存在的一点疑惑是,究竟如何实现的?考量到大佬没有讲更多实现细节,我查找了一些资料总结一下。

MySQL 内部XA

MySQL采用了如下过程实现了内部XA的两阶段提交:

  • Prepare阶段:InnoDB将回滚端设置为prepare阶段,redolog落盘,这里在45讲中没有说清楚的是,redolog也是会持久化到磁盘上的,并不是只存在于内存中,否侧系统奔溃时一样失去crash safe能力,不过相较于在数据库中的随机IO,redolog落盘是顺序IO,相较有更好的性能。
  • Commit阶段:binlog写入文件,落盘,InnoDB commit

崩溃恢复的具体情况:

需要注意的是,commit标志不是事务的成功与否标志,binlog写入才是

  • prepare阶段奔溃,写入了redolog但没有写入binlog--->回滚
  • commit阶段奔溃,若没有成功写入binlog---->回滚
  • 如果成功写入binlog,commit标志不再重要,会重新写入commit标志---->提交事务

崩溃恢复过程

以binlog中的xid为准

  • 找到最后一个binlog,将redolog刷盘,将binlog中的xid添加到一个hash中,提交给InnoDB引擎
  • InnoDB引擎遍历每一个还在prepared状态的事务,确定是否要回滚
// recovery mode
if (info->commit_list
    ? info->commit_list->count(x) != 0
    : tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT) {

    // 1. 如果 XID 在 hash 里,说明 redolog 和 binlog 都已经完成了事务的刷盘,可以提交
    hton->commit_by_xid(hton, &info->list[i].id);
} else {

    // 2. 如果 XID 不在 hash 里,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,2PC 没有成功,需要回滚
    hton->rollback_by_xid(hton, &info->list[i].id);
}

可以看到的是一切以binlog中的xid为基准进行提交或回滚

参数设置

innodb_flush_log_at_trx_commit参数:

  • {0|1|2} : 指定何时将事务日志刷到磁盘,默认为1。
  • 0表示每秒将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
  • 1表示每事务提交都将"log buffer"同步到"os buffer"且从"os buffer"刷到磁盘日志文件中。
  • 2表示每事务提交都将"log buffer"同步到"os buffer"但系统决定"os buffer"刷到磁盘日志文件中的时间。

sync_binlog

  • 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失

以上是关于MySQL的两阶段提交的主要内容,如果未能解决你的问题,请参考以下文章

聊一聊 MySQL 中的数据编辑过程中涉及的两阶段提交

浅谈mysql的两阶段提交协议

浅谈mysql的两阶段提交协议

Mysql—Mysql日志的两阶段提交分布式事务以及多事务组提交

使用 Atomikos 的两阶段提交 (2PC) 配置

Mysql事务—内部XA的两阶段提交(2pc)