iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >MySQL层事务提交的流程
  • 243
分享到

MySQL层事务提交的流程

2024-04-02 19:04:59 243人浏览 安东尼
摘要

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

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

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

一、参数设置

本部分假定参数设置为:

  • 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

关于参数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参数则有两个功能:

  • 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刷盘,约等于指定次group commit后刷盘。同时会在flush阶段后通知DUMP线程发送Event。

二、总体流程图

这里我们先展示整个流程,如下(图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。其中包含三种可能取值方式,如下 :

  • Commit_order_trx_dependency_tracker

  • Writeset_trx_dependency_tracker

  • Writeset_session_trx_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有:

  • QUERY_EVENT

  • MAP_EVENT

  • DML EVENT

  • XID_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一共包含两个参数如下:

  • binlog_group_commit_sync_delay:通过人为的设置delay时长来加大整个GROUP COMMIT组中事务数量,并且减少进行磁盘刷盘sync的次数,但是受到binlog_group_commit_sync_no_delay_count的限制。单位为1/1000000秒,最大值1000000也就是1秒。

  • binlog_group_commit_sync_no_delay_count:在delay的时间内如果GROUP COMMIT中的事务数量达到了这个设置就直接跳出等待,而不需要等待binlog_group_commit_sync_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层会做很多动作,比如:

  • Readview的更新

  • Undo的状态的更新

  • 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的清理。


八、总结

  • 整个过程我们看到生成last commit和seq number的过程并没有其它的开销,但是下一节介绍的基于WRITESET的并行复制就有一定的开销了。

  • 我们需要明白的是FLUSH/SYNC/COMMIT每一个阶段都有一个相应的队列,每个队列并不一样。但是其中的事务顺序却是一样的,是否能够在从库进行并行回放完全取决于准备阶段获取的last_commit,这个我们将在第19节详细描述。

  • 对于FLUSH/SYNC/COMMIT三个队列事务的数量实际有这样关系,即COMMIT队列>=SYNC队列>=FLUSH队列。如果压力不大它们三者可能相同且都只包含一个事务。

  • 从流程中可以看出基于COMMIT_ORDER 的并行复制如果数据库压力不大的情况下可能出现每个队列都只有一个事务的情况。这种情况就不能在从库并行回放了,但是下一节我们讲的基于WRITESET的并行复制却可以改变这种情况。

  • 这里我们也更加明显的看到大事务的Event会在提交时刻一次性的写入到binary log。如果COMMIT队列中包含了大事务,那么必然堵塞本队列中的其它事务提交,后续的提交操作也不能完成。我认为这也是MySQL不适合大事务的一个重要原因。

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

您可能感兴趣的文档:

--结束END--

本文标题: MySQL层事务提交的流程

本文链接: https://www.lsjlt.com/news/60195.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySQL层事务提交的流程
    本篇内容主要讲解“MySQL层事务提交的流程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL层事务提交的流程”吧!本节将来解释一下MySQL层详细的提交...
    99+
    2024-04-02
  • 如何理解MySQL层事务提交流程
    如何理解MySQL层事务提交流程,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。部分栈帧:prepare栈帧(gdb) bt #0&n...
    99+
    2024-04-02
  • MySQL事务提交的流程
    本篇内容介绍了“MySQL事务提交的流程”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • MySQL事务的提交过程
    本篇内容介绍了“MySQL事务的提交过程”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!MySQL事务提交过...
    99+
    2024-04-02
  • 找出未提交的MySQL线程/事务
    找出未提交的MySQL线程/事务:SELECT * from information_schema.processlist;   这个能看到上面哪个SQL线程ID(下图的378号线程就是造...
    99+
    2024-04-02
  • MySQL事务处理:自动提交与手动提交的区别
    MySQL事务处理:自动提交与手动提交的区别 在MySQL数据库中,事务是一组SQL语句的集合,要么全部执行成功,要么全部执行失败,保证了数据的一致性和完整性。在MySQL中,事务可以...
    99+
    2024-04-02
  • Mysql之事务提交和隔离级别
    Mysql之事务提交和隔离级别一、事务是什么事务简言之就是一组SQL执行要么全部成功,要么全部失败。MYSQL的事务在存储引擎层实现。1、事务都有ACID特性:原子性(Atomicity):一个事务必须被视...
    99+
    2024-04-02
  • MySQL事务提交的三个阶段是什么
    本篇内容介绍了“MySQL事务提交的三个阶段是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!事务提交分...
    99+
    2024-04-02
  • mysql 5.5 lock tables与隐式事务提交commit
    结论:lock tables可以隐式提交其它会话的未提交事务 测试明细: ----session 1 mysql> start transaction; Query OK, 0 rows affec...
    99+
    2024-04-02
  • 怎么理解MySQL事务两段式提交
    本篇内容主要讲解“怎么理解MySQL事务两段式提交”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么理解MySQL事务两段式提交”吧!⒈两段式提交的目的:解决参...
    99+
    2024-04-02
  • MySQL中begin后事务为什么不提交
    这篇文章主要介绍“MySQL中begin后事务为什么不提交”,在日常操作中,相信很多人在MySQL中begin后事务为什么不提交问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”...
    99+
    2024-04-02
  • 如何理解mysql隐式提交事务transaction
    如何理解mysql隐式提交事务transaction,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。隐式提交事务概述  事务是一个完整...
    99+
    2024-04-02
  • MySQL未提交事务造成的等待事件怎么解决
    本篇内容主要讲解“MySQL未提交事务造成的等待事件怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL未提交事务造成的等待事件怎么解决”吧! ...
    99+
    2024-04-02
  • PostgreSQL 源码解读(123)- MVCC#8(提交事务-实际提交过程)
    本节介绍了PostgreSQL提...
    99+
    2024-04-02
  • 怎么在MySQL中找出未提交的事务信息
    本篇文章给大家分享的是有关怎么在MySQL中找出未提交的事务信息,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。mysql> s...
    99+
    2024-04-02
  • golang实现mysql数据库事务的提交与回滚
    MySQL 事务主要用于处理操作量大,复杂度高的数据。在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。 事务用来管理 insert,update...
    99+
    2024-04-02
  • mysql怎么查看事务是否自动提交
    要查看MySQL的事务是否自动提交,可以使用以下查询语句: SHOW VARIABLES LIKE 'autocommit...
    99+
    2024-04-02
  • SpringBoot数据层测试事务回滚的实现流程
    目录数据层测试事务回滚dao下pojo对象service测试用例数据设定数据层测试事务回滚 pom.xml导入对应的一些坐标,mysql,Mp,等 <dependency>...
    99+
    2022-11-13
    SpringBoot事务回滚 SpringBoot数据层测试事务回滚
  • MySQL中Spring管理的事务开启后不提交引起的事故
    1. 前言 了解到一个事故,在MySQL数据库中,使用Spring管理的事务在开启以后没有在操作结束时提交或回滚,使得原有线程在后续执行数据库操作时可能继续使用原有事务,且不会提交,导致对数据库的修改...
    99+
    2023-09-03
    spring mysql java
  • 详解Spring事务回滚和事务提交
    目录事务回滚事务提交事务回滚 回滚逻辑如下: 判断是否存在事务,只有存在事务才执行回滚 根据异常类型判断是否回滚。如果异常类型不符合,仍然会提交事务 回滚处理 ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作