mysql层事物提交流程
假设参数设置:
binlog_group_commit_sync_delay 0
binlog_group_commit_sync_no_delay_count 0
binlog_order commits on
sync_binlog 1
binlog_transaction_dependency_tracking commit_order
sync_binlog=0 binary log不sync 刷盘,依赖OS刷盘机制,同时会在flush阶段后通知DUMP线程发送event
sync_binlog=1 binary log每次sync队列形成后都进行sync刷盘,约等于每次group commit进行刷盘,同时会在sync阶段后通知dump线程发送event。注意sync_binlog非1的设置可能导致从库比主库多事物
sync_binlog>1 binary log将在制定次sync队列形成后进行sync刷盘,约等于制定次groupcommit后刷盘,同时会在flush阶段后通知dump线程发送event
流程详解解释:
步骤解析第一阶段
备注:在1之前会有一个获取MDL_key::commit 锁的操作,因此ftwrl将会赌赛commit操作,堵塞i状态为waiting for commit lock。
1.binlog 准备,将上一次commit队列中最大的seq number写入到本次事物的last commit中,可参考binlog_prepare函数
2.innodb准备,更改事物的状态为准备并且将事物的状态和XID写入到undo,参考TRX_PREPARE函数
3.XID_EVENT生成并且写道binlog cache中
步骤解析第二阶段:
4.形成flush列队。这一步正在不断的有事物加入到这个flush队列。第一个进入flush队列的为本阶段的leader,非leader线程将会堵塞,直到commit阶段后由leader线程唤醒
5.获取LOCK log锁
6.这一步就是将flush阶段的队列取出来准备进行处理。也就是在这个时候本flush队列就不能在更改了。可参考stage_manager.fetch_queue_for的函数
7.这里事物会进行innodb层的redo持久化,并且会帮助其他事物进行redo的持久化。可以参考MySQL_bin_log::process_flush_stage_queue函数
8.生成GTID和SEQ number,并且连接前面的last commit生成gtid_event,然后直接写入到binary log中。--------没有写入binary log cache
对于seqnumber和last commit的取值来说,有三种取值,收到参数binlog_transaction_dependency_tracker来决定.
对于commit_order特点:
8.1 每次事物提交seq number增加1
8.2 last commit 在前面的binlog准备阶段就赋予值给了每个事物
8.3 last commit 是前一个commit队列的最大seq number
其次seq number和last commit这2个值类型都为logical_clock,其中维护了一个叫做offsets偏移量的值,用来记录每次binary log切换时sequence_number的相对偏移量,因此seq number 和last commit 在每个binary log总是重新计算。
9.将会binlog cache里面的所有event写入到我们的binary log中,包含以下事件(query_event,map_event,dml_event,xid_event)
*******重复7-9 把flush队列中所有的事物做同样的处理
注意:如果sync_binlog !=1 这里将会唤醒DUMP线程进行event的发送
10.这一步还会判断binary log是否需要切换,并且设置一个切换标记。依据就是整个队列每个事物写入的event总量加上现有的binary log大小是否超过max_binlog_size。注意:将所有的event写入binary log然后再进行判断;因此对于大事物来讲event 可定都包含再同一个binary log中。
步骤解析第三阶段:
11.flush 列队加入到sync队列。第一个进入flush列队的leader为本阶段的leader。其他flush列队加入sync队列,且其他flush队列的leader会被lock sync堵塞,直到commit阶段后由leader线程唤醒。
12.释放lock log
13.获取lock sync
14.这里根据参数delay的设置来决定是否等待一段时间,如果delay时间越长那么加入sync队列的时间就会越长,也就可能由更多的flush列队加入进来,那么这个sync队列的事物就越多。不仅会提高sync效率,并且增大了group commit组成员的数量,从而提高MTS的并行效率。
15.这一步就是将sync阶段的队列取出来准备进行并行处理,也就是这个时候sync队列就不能再更改了,这个队列和flush队列不一样,事物的顺序一样但是数量可能不一样。
16.根据sync_binlog的设置决定是否刷盘
-----------------这里sync阶段就结束了,---------如果sync_binlog=1这里将会唤醒DUMP线程进行event的发送
解析第四步
17.sync队列加入到commit队列,第一个进入的sync队列的leader为本阶段的leader。其他sync队列加入commit队列,且其他sync队列的leader会被lock commit堵塞,直到commit阶段后由leader线程的唤醒
18.lock sync
19.lock commit
20.根据参数binlog_order_commits的设置来决定是否按照队列的顺序进行innodb层的提交,如果binlog_order_commits=1 则按照队列顺序提交事物的可见和提交顺序一致。如果binlog_order_commits=0 则下面21到23步将不会进行,事物的顺序一样,数量可能不一样
21.这一步就是将commit阶段的队列取出来进行处理,也就是这个时候commit队列不能再改变了,这个队列和flush队列和sync队列不一样,事物的顺序一样,数量可能不一样
重要:如果rpl_semi_sync_master_wait_point参数设置为after_sync 这里将会进行ACK的确认,可以看到实际innodb层提交操作还没有进行
等待期间状态为‘waiting for semi-sync ACK form slave'
22.再innodb层提交之前必须要更改last_commit。commit队列中每个事物都会去更新它,如果大于则更改,小于则不变
23.commit队列中每个事物按照顺寻进行innodb层的提交。可以参考innobase_commit函数
这一步innodb层会做很多动作:readview的更新 undo的状态的更新,innodb锁资源的释放
完成这一步,实际上再innodb层事物就可以看见了。
循环22-23直到commit列队处理完成。
24.释放lock commit
如果rpl_semi_sync_master_wait_point参数设置为after_commit ,这里将会进行ACK的确认,
可以看到实际innodb层提交操作已经完成了,等待期间状态为‘waiting for semi-sync ACK form slave'
到这里commit阶段结束了。
25.这里leader线程唤醒所有组内的成员,各自进行各自的操作
26.每个事物成员进行binlog cache的重置,清空cache释放临时文件
27.如果binlog_order_commit设置为0,commit队列中的每个事物就各自进行innodb层面的提交
28.根据10设置的切换标记,决定是否进行binary log的切换
29.如果切换了binary log,则还需要根据expire_logs_days的设置判断是否进行binlog的清理