MySQL Group Commit 笔记

Binlog开启为何和Group Commit不兼容?

MySQL/MariaDB使用XA/2阶段提交保证事务持久性。
提交事务的三个阶段:

  1. 准备阶段,事务在引擎中持久化,但仍可以回滚;
  2. 如果准备阶段成功,事务在binlog中持久化;
  3. 提交阶段,引擎提交事务,无法再回滚。

开启binlog之前,group commit的实现:

    trx->flush_log_later = TRUE;

    innobase_commit_low(trx);

    trx->flush_log_later = FALSE;

最后所有事务通过同一个fsync()调用flush。

开启binlog之后,group commit的实现:

innobase_xa_prepare()

    write() and fsync() binary log

    innobase_commit()

在innobase_xa_prepare()阶段,锁住了prepare_commit_mutex,导致只要有一个事务在执行innobase_commit(),其他所有执行innobase_xa_prepare()的事务都会阻塞等待锁释放。
因此无法做到所有事务共同通过fsync()调用flush。

那么为什么innobase_xa_prepare()要拿着prepare_commit_mutex?为了严格保证事务提交的顺序。

当sync_binlog=1时,很明显上述的第二步会成为瓶颈,而且还是持有全局大锁,这也是为什么性能会急剧下降。
很快Mariadb就提出了一个Binlog Group Commit方案,即在准备写入Binlog时,维持一个队列,最早进入队列的是leader,后来的是follower,leader为搜集到的队列中的线程依次写Binlog文件, 并commit事务。Percona 的Group Commit实现也是Port自Mariadb。不过仍在使用Percona Server5.5的朋友需要注意,该Group Commit实现可能破坏掉Semisync的行为。

Oracle MySQL 在5.6版本开始也支持Binlog Group Commit,使用了和Mariadb类似的思路,但将Group Commit的过程拆分成了三个阶段:

  • flush stage 将各个线程的binlog从cache写到文件中;
  • sync stage 对binlog做fsync操作(如果需要的话);
  • commit stage 为各个线程做引擎层的事务commit。

每个stage同时只有一个线程在操作。

如何修复group commit?

仅在为binlog刷盘时进行fsync(), 也就是让innodb_flush_log_at_trx_commit运行在级别2甚至级别0上。

为了能按上述步骤恢复到数据一致状态, 我们需要以下两个条件:

  • 对于一个事务, 需要将其在binlog中的位置存到存储引擎中, 这样在崩溃恢复中, 我们才能知道应从binlog哪个位置起开始回放事务.
  • 这次我们真的需要保证binlog和存储引擎的commit顺序一致! 否则崩溃恢复时, 有可能的情况是: binlog中两个事务的顺序是AB, 而存储引擎中仅有B被提交, 而A没被持久化到存储引擎中. 这样我们就没法决定如何恢复一致性了.

实现

1.

void commit_ordered(handlerton *hton, THD *thd, bool all);

在commit()之前调用,且保证在各引擎上严格的提交顺序。

  1. Ticket方式
    使用 innodb的UT_LIST结构,启用force_binlog_order配置。

你可能感兴趣的:(MySQL Group Commit 笔记)