mysql控制台命令大全(MySQL内部的XA及组提交浅谈)
mysql控制台命令大全(MySQL内部的XA及组提交浅谈)XA 使用两阶段提交协议的主要优点是原理简介清晰、实现方便。优缺点事务协调器 C 将一条 prepare 消息发送到执行 T 的所有节点上。当各个节点的事务管理器收到 prepare 消息时,确定是否愿意提交事务 T 中自己的部分:如果可以提交,就将所有与 T 相关的日志记录强制刷盘,并记录事务 T 的状态为 prepared,然后事务管理器返回 ready 作为应答;如果无法提交,就发送 abort 消息。commit 阶段当事务协调器 C 收到所有节点对 prepare 消息的回应后进入 commit 阶段,C 可以决定是将事务 T 进行提交还是中止,如果所有参与的节点都返回了 ready 应答,则事务 T 可以提交,否则,事务 T 需要中止。之后,协调器向所有节点发送 commit 或 abort 消息,各节点收到这个消息后,将事务最终的状态更改为 commit 或 abort,并写
一 背景在分享MySQL内部的两阶段提交和组提交之前.想必有一些人会纳闷.为什么MySQL会有2pc.因为2pc是分布式才会涉及到的呀.MySQL只是一个关系型数据库.怎么会有2pc.先给大家解释一下.因为MySQL是分为server层和存储引擎层.那么就要保证一个事务在server层和存储引擎层的一致性.所以使用到了2pc.接下来我们一起了解一下MySQL内部的2pc吧.
二 2pc介绍在分布式事务处理中,全局事务(global transaction)会访问和更新多个局部数据库中的数据,如果要保证全局事务的原子性,执行全局事务 T 的所有节点必须在执行的最终结果上取得一致。X/Open 组织针对分布式事务处理而提出了 XA 规范,使用两阶段提交协议(two-phase commit protocol,2PC)来保证一个全局事务 T 要么在所有节点都提交(commit),要么在所有节点都中止。
考虑一个全局事务 T 的事务协调器(transaction coordinator)是 C,当执行 T 的所有事务管理器(transaction manager)都通知 C 已经完成了执行,C 开始启动两阶段提交协议,分为 prepare 和 commit 两个阶段:
prepare 阶段
事务协调器 C 将一条 prepare 消息发送到执行 T 的所有节点上。当各个节点的事务管理器收到 prepare 消息时,确定是否愿意提交事务 T 中自己的部分:如果可以提交,就将所有与 T 相关的日志记录强制刷盘,并记录事务 T 的状态为 prepared,然后事务管理器返回 ready 作为应答;如果无法提交,就发送 abort 消息。
commit 阶段
当事务协调器 C 收到所有节点对 prepare 消息的回应后进入 commit 阶段,C 可以决定是将事务 T 进行提交还是中止,如果所有参与的节点都返回了 ready 应答,则事务 T 可以提交,否则,事务 T 需要中止。之后,协调器向所有节点发送 commit 或 abort 消息,各节点收到这个消息后,将事务最终的状态更改为 commit 或 abort,并写入日志。
优缺点
XA 使用两阶段提交协议的主要优点是原理简介清晰、实现方便。
主要缺点是各个节点需要阻塞等待事务协调器来决定提交或中止。如果事务协调器出现故障,那全局事务就无法获得最终的状态,各个节点可能需要持有锁并等待事务协调器的恢复,这种情况称为阻塞问题,因为事务 T 需要等待协调器恢复而被阻塞。
三 MySQL内部的2pc在MySQL中server层的binlog作为2pc的协调器.以innodb存储引擎为例.redo log为执行器
也就构成了MySQL内部的2pc.
如果没有开启binlog的情况下.mysql内部会选择TC_LOG_MMAP类作为协调者
具体代码如下:
tc_log = &tc_log_dummy;
...
if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log)) {
if (opt_bin_log)
tc_log = &mysql_bin_log;
else
tc_log = &tc_log_mmap;
}
我们主要分析开启binlog的情况下.MySQL如何保证binlog和redo log的一致性
两阶段提交过程
MySQL 采用了如下的过程实现内部 XA 的两阶段提交:
- Prepare 阶段:InnoDB 将回滚段设置为 prepare 状态;将 redolog 写文件并刷盘;
- Commit 阶段:Binlog 写入文件;binlog 刷盘;InnoDB commit;
两阶段提交保证了事务在多个引擎和 binlog 之间的原子性,以 binlog 写入成功作为事务提交的标志,而 InnoDB 的 commit 标志并不是事务成功与否的标志。
在崩溃恢复中,是以 binlog 中的 xid 和 redolog 中的 xid 进行比较,xid 在 binlog 里存在则提交,不存在则回滚。我们来看崩溃恢复时具体的情况:
- 在 prepare 阶段崩溃,即已经写入 redolog,在写入 binlog 之前崩溃,则会回滚;
- 在 commit 阶段,当没有成功写入 binlog 时崩溃,也会回滚;
- 如果已经写入 binlog,在写入 InnoDB commit 标志时崩溃,则重新写入 commit 标志,完成提交。
MySQL 的内部 XA 机制保证了单个事务在 binlog 和 InnoDB 之间的原子性,接下来我们需要考虑,在多个事务并发执行的情况下,怎么保证在 binlog 和 redolog 中的顺序一致?
早期解决方法
在 MySQL 5.6 版本之前,使用 prepare_commit_mutex 对整个 2PC 过程进行加锁,只有当上一个事务 commit 后释放锁,下个事务才可以进行 prepare 操作,这样完全串行化的执行保证了顺序一致。
存在的问题是,prepare_commit_mutex 的锁机制会严重影响高并发时的性能,在每个事务执行过程中, 都会至少调用 3 次刷盘操作(写 redolog,写 binlog,写 commit),多个小 IO 是非常低效的方式。
组提交
为了提高并发性能,肯定要细化锁粒度。MySQL 5.6 引入了 binlog 的组提交(group commit)功能,prepare 阶段不变,只针对 commit 阶段,将 commit 阶段拆分为三个过程:
- flush stage:多个线程按进入的顺序将 binlog 从 cache 写入文件(不刷盘);
- sync stage:对 binlog 文件做 fsync 操作(多个线程的 binlog 合并一次刷盘);
- commit stage:各个线程按顺序做 InnoDB commit 操作。
其中,每个阶段有 lock 进行保护,因此保证了事务写入的顺序。
实现方法是,在每个 stage 设置一个队列,第一个进入该队列的线程会成为 leader,后续进入的线程会阻塞直至完成提交。leader 线程会领导队列中的所有线程执行该 stage 的任务,并带领所有 follower 进入到下一个 stage 去执行,当遇到下一个 stage 为非空队列时,leader 会变成 follower 注册到此队列中。
这种组提交的优势在于锁的粒度减小,三个阶段可以并发执行,从而提升效率。
5.7 组提交优化:
延迟写 redo 到 group commit 阶段
MySQL 5.6 的组提交逻辑中,每个事务各自做 prepare 并写 redo log,只有到了 commit 阶段才进入组提交,因此每个事务的 redolog sync 操作成为性能瓶颈。
在 5.7 版本中,修改了组提交的 flush 阶段,在 prepare 阶段不再让线程各自执行 flush redolog 操作,而是推迟到组提交的 flush 阶段,flush stage 修改成如下逻辑:
- 收集组提交队列,得到 leader 线程,其余 follower 线程进入阻塞;
- leader 调用 ha_flush_logs 做一次 redo write/sync,即,一次将所有线程的 redolog 刷盘;
- 将队列中 thd 的所有 binlog cache 写到 binlog 文件中。
这个优化是将 redolog 的刷盘延迟到了 binlog group commit 的 flush stage 之中,sync binlog 之前。通过延迟写 redolog 的方式,为 redolog 做了一次组写入,这样 binlog 和 redolog 都进行了优化。
引用图解MySQL的一张图
五 总结本篇文章主要介绍了MySQL内部的2pc机制以及组提交机制.
借鉴:
MySQL · 源码分析 · 内部 XA 和组提交
[原理解析] MySQL组提交(group commit)