- mysql事务提交过程
两段式提交
事务的提交主要分为两个主要步骤:
1.准备阶段(Prepare)
此时SQL已经成功执行,并生成xid信息及redo和undo的内存日志。然后调用prepare方法完成第一阶段,papare方法实际上什么也没做,将事务状态设为TRX_PREPARED,并将redo log刷磁盘。
2.提交阶段(Storage Engine(InnoDB)Commit Phase)
1.记录Binlog日志。
如果事务涉及的所有存储引擎的prepare都执行成功,则调用 TC_LOG_BINLOG::log_xid方法将SQL语句写到binlog。
(write()将binary log内存日志数据写入文件系统缓存,fsync()将binary log文件系 统缓存日志数据永久写入磁盘)。
此时,事务已经铁定要提交了。否则,调用ha_rollback_trans方法回滚事务,而SQL 语句实际上也不会写到binlog。
2.告诉引擎做commit。
最后,调用引擎的commit完成事务的提交。会清除undo信息,刷redo日志,将 事务设为TRX_NOT_STARTED状态。
上面是mysql的XA提交方案,但是存在不足,就是事务并发提交的话会变成串行执行fsync刷盘,大大降低了事务提交的并发度。
主从复制延迟问题:
MySQL最早的主备复制只有两个线程,IO 线程负责从主库接收 binlog 日志,并保存在本地的 relaylog 中,SQL线程负责解析和重放 relaylog 中的 event。当主库并行写入压力较大时,备库 IO 线程一般不会产生延迟,因为写 relaylog 是顺序写,但是 SQL 线程重放的速度经常跟不上主库写入的速度,会造成主备延迟。如果延迟过大,relaylog 一直在备库堆积,还可能把磁盘占满。
为了缓解这种问题,很自然的想法是提高 SQL 线程重放的并行度,引入并行复制。
- mysql5.6的Schema级别的并行复制
由此引申出了mysql5.6的Schema级别的并行复制。
场景:对于多个DB同时更新才能有比较高的并发度,但是更常见的情况是更新一个DB。
可以将Schema级别改为Table级别,可以大大提高单库多表的并行度,但是对于只有一个热点表的情况依然处理不了。
- Mysql5.7 Group Commit(组提交)并行复制
MySQL 5.7并行复制的思想简单易懂,一言以蔽之:一个组提交的事务都是可以并行回放,因为这些事务都已进入到事务的prepare阶段,则说明事务之间没有任何冲突(否则就不可能提交)。
变量:slave_parallel_type
可选值:DATABASE、(数据库级别的并行复制)
LOGICAL_CLOCK(基于Group Commit级别的并行复制)
如果binLog中last_commited 相同的事务,可以在备库并行回放。
但是也有弊端,就是如果主库的并行度本身就比较低,那么备库回放也会很慢。为此5.7引进了两个参数:
binlog_group_commit_sync_delay:
Binlog提交后等待一段时间再进行fsync,让每个group的事务更多,提高并行度
binlog_group_commit_sync_no_delay_count:
等待提交的最大事务数,如果事务数达到了但是等待时间还没到那就立即fsync。尽量缩小等待时间。
4.Mysql8.0基于WriteSet的并行复制
MySQL 8.0中引入了一种新的机制来判断事务能否并行回放,通过检测事务在运行过程中是否存在写冲突来决定从机上的回放顺序,这使得从机上的并发程度不再依赖于主机。
参数:binlog_transaction_dependency_tracking
可选值:COMMIT_ORDERE:使用 5.7 Group commit 的方式决定事务依赖
WRITESET:使用 WriteSet 的方式决定判定事务直接的冲突,发现冲突则依赖冲突 事务,否则按照 COMMIT_ORDERE 方式决定依赖
WRITESET_SESSION:在 WRITESET 方式的基础上,保证同一个 session 内的事务不 可并行