文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

MySQL层事务提交的流程

2024-04-02 19:55

关注

本篇内容主要讲解“MySQL层事务提交的流程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL层事务提交的流程”吧!

本节将来解释一下MySQL层详细的提交流程,但是由于能力有限,这里不可能包含全部的步骤,只是包含了一些重要的并且我学习过的步骤。我们首先需要来假设参数设置,因为某些参数的设置会直接影响到提交流程,我们也会逐一解释这些参数的含义。本节介绍的大部分内容都集中在函数MYSQL_BIN_LOG::prepare和MYSQL_BIN_LOG::ordered_commit之中。

一、参数设置

本部分假定参数设置为:

关于参数binlog_transaction_dependency_tracking需要重点说明一下。我们知道Innodb的行锁是在语句运行期间就已经获取,因此如果多个事务同时进入了提交流程(prepare阶段),在Innodb层提交释放Innodb行锁资源之前各个事务之间肯定是没有行冲突的,因此可以在从库端并行执行。在基于COMMIT_ORDER 的并行复制中,last commit和seq number正是基于这种思想生成的,如果last commit相同则视为可以在从库并行回放,在19节我们将解释从库判定并行回放的规则。而在基于WRITESET的并行复制中,last commit将会在WRITESET的影响下继续降低,来使从库获得更好的并行回放效果,但是它也是COMMIT_ORDER为基础的,这个下一节将讨论。我们这节只讨论基于COMMIT_ORDER 的并行复制中last commit和seq number的生成方式。

而sync_binlog参数则有两个功能:

二、总体流程图

这里我们先展示整个流程,如下(图15-1,高清原图包含在文末原图中):

MySQL层事务提交的流程


三、步骤解析第一阶段(图中蓝色部分)

注意:在第1步之前会有一个获取MDL_key::COMMIT锁的操作,因此FTWRL将会堵塞‘commit’操作,堵塞状态为‘Waiting for commit lock’,这个可以参考FTWRL调用的函数make_global_read_lock_block_commit。

(1.)  binlog准备。将上一次COMMIT队列中最大的seq number写入到本次事务的last_commit中。可参考binlog_prepare函数。

(2.)  Innodb准备。更改事务的状态为准备并且将事务的状态和XID写入到Undo中。可参考trx_prepare函数。

(3.)  XID_EVENT生成并且写到binlog cache中。在第10节中我们说过实际上XID来自于query_id,早就生成了,这里只是生成Event而已。可参考MYSQL_BIN_LOG::commit函数。


四、步骤解析第二阶段(图中粉色部分)

(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函数。下面是注释和一小段代码:

  
  ha_flush_logs(NULL, true);//做innodb redo持久化

(8.) 生成GTID和seq number,并且连同前面的last commit生成GTID_EVENT,然后直接写入到binary log中。我们注意到这里直接写入到了binary log而没有写入到binlog cache,因此GTID_EVENT是事务的第一个Event。参考函数binlog_cache_data::flush中下面一段:

trn_ctx->sequence_number= mysql_bin_log.m_dependency_tracker.step(); 
//int64 state +1
...
    if (!error)
      if ((error= mysql_bin_log.write_gtid(thd, this, &writer)))
//生成GTID 写入binary log文件
        thd->commit_error= THD::CE_FLUSH_ERROR;
    if (!error)
      error= mysql_bin_log.write_cache(thd, this, &writer);
//将其他Event写入到binary log文件

而对于seq number和last commit的取值来讲,实际上在MySQL内部维护着一个全局的结构Transaction_dependency_tracker。其中包含三种可能取值方式,如下 :

到底使用哪一种取值方式,由参数binlog_transaction_dependency_tracking来决定的。
这里我们先研究参数设置为COMMIT_ORDER 的取值方式,对于WRITESET取值的方式下一节专门讨论。

对于设置为COMMIT_ORDER会使用Commit_order_trx_dependency_tracker的取值方式,有如下特点:

特点
每次事务提交seq number将会加1。
last commit在前面的binlog准备阶段就赋值给了每个事务。这个前面已经描述了。
last commit是前一个COMMIT队列的最大seq number。这个我们后面能看到。

其次seq number和last commit这两个值类型都为Logical_clock,其中维护了一个叫做offsets偏移量的值,用来记录每次binary log切换时sequence_number的相对偏移量。因此seq number和last commit在每个binary log总是重新计数,下面是offset的源码注释:

  
  int64 offset;

下面是我们计算seq number的方式,可以参考Commit_order_trx_dependency_tracker::get_dependency函数。

  sequence_number=
    trn_ctx->sequence_number - m_max_committed_transaction.get_offset(); 
//这里获取seq number

我们清楚的看到这里有一个减去offset的操作,这也是为什么我们的seq number和last commit在每个binary log总是重新计数的原因。

(9.) 这一步就会将我们的binlog cache里面的所有Event写入到我们的binary log中了。对于一个事务来讲,我们这里应该很清楚这里包含的Event有:

注意GTID_EVENT前面已经写入到的binary logfile。这里我说的写入是调用的Linux的write函数,正常情况下它会进入图中的OS CACHE中。实际上这个时候可能还没有真正写入到磁盘介质中。

重复 7 ~ 9步 把FLUSH队列中所有的事务做同样的处理。

注意:如果sync_binlog != 1 这里将会唤醒DUMP线程进行Event的发送。

(10.) 这一步还会判断binary log是否需要切换,并且设置一个切换标记。依据就是整个队列每个事务写入的Event总量加上现有的binary log大小是否超过了max_binlog_size。可参考MYSQL_BIN_LOG::process_flush_stage_queue函数,如下部分:

 if (total_bytes > 0 && my_b_tell(&log_file) >= (my_off_t) max_size)
    *rotate_var= true; //标记需要切换

但是注意这里是先将所有的Event写入binary log,然后才进行的判断。因此对于大事务来讲其Event肯定都包含在同一个binary log中。

到这里FLUSH阶段就结束了。


五、步骤解析第三阶段(图中紫色部分)

(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组成员的数量(因为last commit还没有更改,时间拖得越长那么一组事务中事务数量就越多),从而提高了从库MTS的并行效率。但是缺点也很明显可能导致简单的DML语句时间拖长,因此不能设置过大,下面是我简书中的一个案列就是因为delay参数设置不当引起的,如下:
https://www.jianshu.com/p/bfd4a88307f2

参数delay一共包含两个参数如下:

(15.) 这一步就是将SYNC阶段的队列取出来准备进行处理。也就是这个时候SYNC队列就不能再更改了。这个队列和FLUSH队列并不一样,事务的顺序一样但是数量可能不一样。

(16.) 根据sync_binlog的设置决定是否刷盘。可以参考函数MYSQL_BIN_LOG::sync_binlog_file,逻辑也很简单。

到这里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步将不会进行,也就是这里不会进行Innodb层的提交。

(21.) 这一步就是将COMMIT阶段的队列取出来准备进行处理。也就是这个时候COMMIT队列就不能在更改了。这个队列和FLUSH队列和SYNC队列并不一样,事务的顺序一样,数量可能不一样。

注意:如果rpl_semi_sync_master_wait_point参数设置为‘AFTER_SYNC’,这里将会进行ACK确认,可以看到实际的Innodb层提交操作还没有进行,等待期间状态为‘Waiting for semi-sync ACK from slave’。

(22.) 在Innodb层提交之前必须要更改last_commit了。COMMIT队列中每个事务都会去更新它,如果大于则更改,小于则不变。可参考Commit_order_trx_dependency_tracker::update_max_committed函数,下面是这一小段代码:

{
  m_max_committed_transaction.set_if_greater(sequence_number);
//如果更大则更改
}

(23.) COMMIT队列中每个事务按照顺序进行Innodb层的提交。可参考innobase_commit函数。

这一步Innodb层会做很多动作,比如:

完成这一步,实际上在Innodb层事务就可以见了。我曾经遇到过一个由于leader线程唤醒本组其他线程出现问题而导致整个commit操作hang住,但是在数据库中这些事务的修改已经可见的案例。

循环22~23直到COMMIT队列处理完。

注意:如果rpl_semi_sync_master_wait_point参数设置为‘AFTER_COMMIT’,这里将会进行ACK确认,可以看到实际的Innodb层提交操作已经完成了,等待期间状态为‘Waiting for semi-sync ACK from slave’。

(24.) 释放LOCK commit。

到这里COMMIT阶段就结束了。


七、步骤解析第五阶段(图中绿色部分)

(25.) 这里leader线程会唤醒所有的组内成员,各自进行各自的操作了。

(26.) 每个事务成员进行binlog cache的重置,清空cache释放临时文件。

(27.) 如果binlog_order_commits设置为0,COMMIT队列中的每个事务就各自进行Innodb层提交(不按照binary log中事务的的顺序)。

(28.) 根据前面第10步设置的切换标记,决定是否进行binary log切换。

(29.) 如果切换了binary log,则还需要根据expire_logs_days的设置判断是否进行binlog log的清理。


八、总结

到此,相信大家对“MySQL层事务提交的流程”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯