MYSQL之两阶段提交和组提交(数据一致性)

news2024/12/29 14:08:35

我们在MySQL 的日志中详细的介绍了undo log、redo log、binlog这三个日志和所用到的一些缓存知识,那么下面我们分析一下更新语句执行过程,它们是怎么变化的呢?下面我们直接给答案吧。假如我们修改一行主键索引(id)为1的数据,更新执行流程如下:

1.  首先执行语句过程可以查考MYSQL语句执行过程文章,一直到调用存储引擎接口,通过主键索引值找到id=1的数据记录:

     a. 如果id=1这一行记录数据所在的数据页在 buffer pool中,那么直接返回给执行器更新

     b. 如果记录不在buffer pool中,那么就从磁盘中读取数据页到buffer pool中,然后返回给           执行器更新

2.  执行器拿到数据记录之后,会比较更新前和更新后的数据是否一样?如果一样,那么就不进行后续的更新过程;如果不一样,那么就会把更新前和更新后的记录传给InnoDB去执行真正的更新过程

3.  到了InnoDB中,会先开启事务,并在InnoDB 层更新记录前,首先会在undo log中做相应的记录,即通过生产一条undo log把更新的列的旧值记下来,undo log 会写入 Buffer Pool 中的 Undo 页面,不过在修改该 Undo 页面前需要先记录对应的 redo log,所以先记录修改 Undo 页面的 redo log ,然后再真正的修改 Undo 页面

4.  InnoDB开始更新记录,根据WAL技术,修改数据页面的过程是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘

5.  至此,一条记录更新完了

6.  在一条更新语句执行完成后,然后开始记录该语句对应的 binlog,此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件,在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘

事务提交,剩下的就是「两阶段提交」的事情了,也是下面重点讲解的问题。

什么是两阶段提交?

两阶段提交其实是分布式事务一致性协议,它可以保证多个逻辑操作要不全部成功,要不全部失败,不会出现半成功的状态。

把单个事务的提交拆分成了 2 个阶段,分别是分别是「准备(Prepare)阶段」和「提交(Commit)阶段」,每个阶段都由协调者(Coordinator)和参与者(Participant)共同完成。注意,不要把提交(Commit)阶段和 commit 语句混淆了,commit 语句执行的时候,会包含提交(Commit)阶段。更通俗点讲,就比如两个人打拳,裁判就是协调者,拳击手就是参与者,在所有拳击手都准备好之后,是不会开始比赛的,这个阶段就是准备阶段;当都准备好之后,那就开始打拳,但是如果有一个人还没有准备好,那么暂停比赛(这就是事务回滚操作),这个阶段就是提交阶段。

为什么需要两阶段提交?

在事务提交之后,我们有redo log 和binlog都要持久化到磁盘中,但是这两个逻辑是相互独立的,那么就会出现半成功的状态,这就导致了两份日志逻辑不一致问题。假如修改id = 1 这行数据,半成功状态具体如下:

1. 如果redo log 刷入磁盘之后, MySQL 突然宕机了,而 binlog 还没有来得及写入。MySQL 重启后,通过redo log 可以把Buffer Pool 中的id = 1这行数据修改的值恢复到最新的值。但是,由于binlog里面没有记录这条更新语句,而在主从架构中,binlog会被复制到从库中,所以由于binlog丢失了这条更新语句,而从库对这行的数据还是旧值,从而出现主从值不一致问题

2.  如果binlog刷入磁盘之后,MySQL突然宕机了,而redo log 还没有来得及写入。MySQL 重启后,由于redo log 还没有写入这个数据更新信息,所以崩溃恢复之后这个更新事务是无效的,所以id = 1 这行数据更改的值还是旧值。但是,binlog已经记录了这条更新语句,那么在主从架构中,binlog会被复制到从库中,从而从库会执行这条更新语句,即从库是这行数据是新值,从而导致主从值不一致问题

从上面可以知道,在redo log 和 binlog 这两份日志持久化的时候,可能会出现半成功状态,而出现主从环境中数据不一致问题。这是因为redo log影响主库,binlog影响从库,所以要解决上面的问题,必须要保证这两个日志的一致性。所以MYSQL为了解决这个问题,使用了两阶段提交解决。

两阶段提交的提交过程

 在MYSQL的InnoDB存储引擎中,如果开启了binlog情况下,MYSQL会同时维护binlog和InnoDB中的redo log,为了保证这两个日志的一致性问题,它使用了内部XA事务(当然也有外部XA事务)解决。内部XA事务是由binlog作为协调者,redo log 作为参与者。MySQL 内部开启一个 XA 事务,分两阶段来完成 XA 事务的提交,如下图:

两阶段提交过程

 从两个阶段提交定义和上图可以知道,我们是把redo log 拆分为两个步骤:prepare 和 commit,而在这中间加入binlog的写入,具体如下:

1. prepare 阶段:将 XID(内部 XA 事务的 ID) 写入到 redo log,同时将 redo log 对应的事务状态设置为 prepare,然后将 redo log 刷新到硬盘

2. commit 阶段:把 XID  写入到 binlog,然后将 binlog 刷新到磁盘,接着调用引擎的提交事务接口,将 redo log 状态设置为 commit

 如果在两个阶段出现异常怎么处理?

两个阶段出现异常情况如下图:

两个阶段出现异常情况

 上图解释说明:

1. 时刻A: redo log 写入,binlog没有写入;时刻A: redo log 和binlog都写入,但没有提交commit标识;

2. 不管在时刻A,还是时刻B, redo log 都是处于 prepare 阶段

如果在MYSQL重启之后会按照顺序读取redo log 文件,当碰到处于 prepare阶段的redo log 文件,就会拿着redo log 的XID去binlog中寻找,看是否有该XID:

1.  如果binlog中没有该XID,那么就说明redo log 刷盘完成,但binlog还没有,则回滚事务,即时刻A阶段异常

2.  如果binlog中有该XID,那么就说明redo log 和binlog都刷盘完成,但commit标识还没有提交,则提交事务,即时刻B阶段异常

所以从上面可以看出,对于处于 prepare 阶段的 redo log,即可以提交事务,也可以回滚事务,这取决于是否能在 binlog 中查找到与 redo log 相同的  XID,如果有就提交事务,如果没有就回滚事务。这样就可以保证 redo log 和 binlog 这两份日志的一致性了。

总结:两阶段提交是以 binlog 写成功为事务提交成功的标识,因为 binlog 写成功了,就意味着能在 binlog 中查找到与 redo log 相同的  XID。

问题1: 处于 prepare 阶段的 redo log 加上完整的binlog, 就提交事务,MYSQL为什么这么设计?

答案: 因为binlog写入了,那么根据主从原理得知最后从库也会写入。所以主库需要提交这个事务,使主从库数据一致。

问题2:如果事务还没有提交,redo log会持久化吗?

答案:会持久化。因为事务执行过程中redo log 会直接写到 redo log buffer中,这些在 redo log buffer里的redo log 会每隔一秒就被持久化到磁盘中(根据持久化策略决定)。所以事务没有提交,redo log可能会持久化到磁盘的。

问题3: 如果在事务还没有提交,而redo log 已经被持久化磁盘了。这时MYSQL崩溃了,怎么办?

答案:在MYSQL重启时,事务会进行回滚操作。因为事务没提交的时候,binlog 是还没持久化到磁盘的。

两阶段提交解决两个日志一致性,那它会不会带来新的问题呢?

虽然两阶段提交是解决了两个日志数据一致性问题,但是它也带来了一定性能问题:

1. 磁盘 I/O 次数高:对于“双1”配置(sync_binlog 和 innodb_flush_log_at_trx_commit 都配置为 1),每个事务提交都会进行两次 fsync(刷盘),一次是 redo log 刷盘,另一次是 binlog 刷盘

2. 锁竞争激烈:两阶段提交虽然能够保证「单事务」两个日志的内容一致,但在「多事务」的情况下,却不能保证两者的提交顺序一致,因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志的提交顺序一致。

磁盘 I/O 次数高

因为redo log 和binlog 都是存在对应的缓存里,即redo log缓存在redo log buffer,binlog缓存在 binlog cache中,而持久化它们是各自通过参数来控制的。一般为了数据不会丢失,都会设置这两个参数为1:

  • 当 sync_binlog = 1 的时候,表示每次提交事务都会将  binlog cache 里的 binlog 直接持久到磁

  • 当 innodb_flush_log_at_trx_commit = 1 时,表示每次事务提交时,都将缓存在  redo log buffer 里的  redo log 直接持久化到磁盘

这两个参数都设置为1,就是“双1”配置。那么当有事务提交的时候,至少就是刷两个磁盘(分别是 redo log 刷盘和binlog 刷盘),所以就导致了性能问题。

锁竞争激烈

 在比较老的MYSQL版本中,为了解决了顺序一致性的问题是通过加锁来处理的。即通过使用 prepare_commit_mutex 锁来保证事务提交的顺序,在一个事务获取到锁时才能进入 prepare 阶段,一直到 commit 阶段结束才能释放锁,下个事务才可以继续进行 prepare 操作。但是在并发量很大的时候,会导致锁竞争激烈,而性能低下。

在比较新的MYSQL版本里,为了解决这两个问题,使用了组提交。

组提交

为了解决两阶段提交性能问题,MYSQL引入了binlog 组提交机制,就是当有多个事务提交时,会将多个binlog 刷盘操作合并成一个,从而减少磁盘 I/O 的次数。

其实binlog组提交机制就把之前的 commit 阶段,拆分为三个过程:

  • flush 阶段:多个事务按进入的顺序将 binlog 从 cache 写入文件(不刷盘);

  • sync 阶段:对 binlog 文件做 fsync 操作(多个事务的 binlog 合并一次刷盘);

  • commit 阶段:各个事务按顺序做 InnoDB commit 操作;

其实上面这三个阶段中每个阶段都有一个队列来完成,而每个队列有锁来保护,从而保证了事务写入的顺序,第一个进入队列的事务会成为 leader,leader领导所在队列的所有事务,全权负责整队的操作,完成后通知队内其他事务操作结束。

每个阶段都有一个队列

 对每个阶段引入了队列后,锁就只针对每个队列进行保护,不再锁住提交事务的整个过程,可以看的出来,锁粒度减小了,这样就使得多个阶段可以并发执行,从而提升效率

有  binlog 组提交,那有  redo log 组提交吗?

在MYSQL5.7版本之前是没有的,从该版本就开始有了。即在 prepare 阶段不再让事务各自执行 redo log 刷盘操作,而是推迟到组提交的 flush 阶段,也就是说 prepare 阶段融合在了flush 阶段。

这个改进是将 redo log 的刷盘延迟到了 flush 阶段之中,sync 阶段之前。通过延迟写 redo log 的方式,为 redolog 做了一次组写入,这样 binlog 和 redo log 都进行了优化。

下面,在“双 1” 配置中介绍每个阶段的过程。

flush 阶段

第一个事务会成为 flush 阶段的 Leader,此时后面到来的事务都是 Follower :

接着,获取队列中的事务组,由绿色事务组的 Leader 对 rodo log 做一次  write + fsync,即一次将同组事务的 redolog 刷盘:

完成了 prepare 阶段后,将绿色这一组事务执行过程中产生的 binlog 写入 binlog 文件(调用 write,不会调用 fsync,所以不会刷盘,binlog 缓存在操作系统的文件系统中)。

从上面这个过程,可以知道 flush 阶段队列的作用是用于支撑 redo log 的组提交

如果在这一步完成后数据库崩溃,由于 binlog 中没有该组事务的记录,所以 MySQL 会在重启后回滚该组事务。

sync 阶段

绿色这一组事务的 binlog 写入到 binlog 文件后,并不会马上执行刷盘的操作,而是会等待一段时间,这个等待的时长由 Binlog_group_commit_sync_delay 参数控制,目的是为了组合更多事务的 binlog,然后再一起刷盘,如下过程:

不过,在等待的过程中,如果事务的数量提前达到了 Binlog_group_commit_sync_no_delay_count 参数设置的值,就不用继续等待了,就马上将 binlog 刷盘,如下图:

从上面的过程,可以知道 sync 阶段队列的作用是用于支持 binlog 的组提交

如果想提升 binlog 组提交的效果,可以通过设置下面这两个参数来实现:

  • binlog_group_commit_sync_delay= N,表示在等待 N 微妙后,直接调用 fsync,将处于文件系统中 page cache 中的 binlog 刷盘,也就是将「 binlog 文件」持久化到磁盘。

  • binlog_group_commit_sync_no_delay_count = N,表示如果队列中的事务数达到 N 个,就忽视binlog_group_commit_sync_delay 的设置,直接调用 fsync,将处于文件系统中 page cache 中的 binlog 刷盘。

如果在这一步完成后数据库崩溃,由于 binlog 中已经有了事务记录,MySQL会在重启后通过 redo log 刷盘的数据继续进行事务的提交。

commit 阶段

最后进入 commit 阶段,调用引擎的提交事务接口,将 redo log 状态设置为 commit。

commit 阶段队列的作用是承接 sync 阶段的事务,完成最后的引擎提交,使得 sync 可以尽早的处理下一组事务,最大化组提交的效率。

组提交总结

如果出现 MySQL 磁盘 I/O 很高的现象,我们可以通过控制以下参数,来 “延迟” binlog 和 redo log 刷盘的时机,从而降低磁盘 I/O 的频率:

  • 设置组提交的两个参数:binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,延迟 binlog 刷盘的时机,从而减少 binlog 的刷盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险。

  • 将 sync_binlog 设置为大于 1 的值(比较常见是 100~1000),表示每次提交事务都 write,但累积 N 个事务后才 fsync,相当于延迟了 binlog 刷盘的时机。但是这样做的风险是,主机掉电时会丢 N 个事务的 binlog 日志。

  • 将 innodb_flush_log_at_trx_commit 设置为 2。表示每次事务提交时,都只是缓存在  redo log buffer 里的  redo log 写到 redo log 文件,注意写入到「 redo log 文件」并不意味着写入到了磁盘,因为操作系统的文件系统中有个 Page Cache,专门用来缓存文件数据的,所以写入「 redo log文件」意味着写入到了操作系统的文件缓存,然后交由操作系统控制持久化到磁盘的时机。但是这样做的风险是,主机掉电的时候会丢数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137225.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电力系统激励型需求响应+自适应多群体优化算法(Python实现)

目录 ​编辑 0 前言 1 激励型DR和价格型DR 2 激励型DR模型 3 Python代码实现 4 自适应多群体优化算法(AMPO) 5 Python代码实现 0 前言 风、光等清洁能源因具有环保、资源丰富等优点而受到电力行业的重视,电力行业开始大力发展清洁能源发电。同时…

MySQL详解,库和表的基础操作

目录 前言 一、预备知识 1、服务器,数据库,表关系 2、SQL分类 3、连接服务器 二、库的操作 1、创建数据库 2、查看字符集和校验规则 2.1 查看系统默认字符集以及校验规则 2.2 查看数据库支持的字符集和字符集校验规则 3、操纵数据库 3.1查看…

ros tf坐标

参考: 讲解:https://www.bilibili.com/video/BV1zt411G7Vn/?p18&vd_source3a1ad336af3eaae4fcced56c75d309d1ROS程序:https://gitee.com/guyuehome/ros_21_tutorials/tree/master/learning_tfROS2程序:https://gitee.com/gu…

公司企业兔年祝福元旦祝福贺卡邀请函模板!

能群发的贺卡邀请函如何制作?想制作一个专属的祝福贺卡邀请函有什么方法?下面跟着小编的乔拓云工具教程!教你如何使用这个工具在线就能轻松搞定设计需求,不仅有海量模板供你使用,还能一键生成链接轻松转发!…

利用vue-cli创建vue3工程

需注意:想创建vue3工程,对vue-cli版本有要求,必须确保vue-cli在4.5.0以上 目录 1、查看vue-cli版本 2、创建工程 3、启动 1、查看vue-cli版本 vue --version(小写v) vue --version或者 vue -V(大写v&…

深入分析Java中finalize方法的作用和底层原理

finalize方法是什么 finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC在首次回收对象之前调用该方法。 finalize方法与C的析构函数的区别 finalize方法与C中的析构函数不是对应的,C中的析构函数调…

(小程序)后台交互-首页

目录 一、小程序首页动态数据加载 1.数据库准备 2.后台准备 ① pom.xml ② 配置数据源 ③ 整合mybatis ④ 代码生成 ⑤ mybatis-generator 二、准备前端的首页的数据 1、Promise 2.封装request 3.会议展示 三、通过wxs将首页动态数据优化 一、小程序首页动态数据加…

Git命令版(powernode)

Git命令版(powernode) 目录Git命令版(powernode)1.添加文件相关命令案例实操小结:2.工作区和暂存区2.1 名词解释。2.1.1 工作区(Working Directory)2.1.2 版本库(Repository&#xf…

数据结构之双向链表

双向链表与单向链表较为类似,单向链表有一个指针域,用来指向后继结点,而双向链表有两个指针域,分别用来指向前驱结点和后继结点。玩双向链表时一定要从单向链表的思维中跳出来,否则在操作双向链表时就会出现各种问题。…

【Python百日进阶-数据分析】Day144 - plotly箱线图:go.box()实例

文章目录4.2 go.Box 箱线图4.2.1 基本箱线图4.2.2 基本水平箱线图4.2.3 显示基础数据的箱线图4.2.4 修改计算四分位数的算法4.2.5 带有预先计算的四分位数的箱线图4.2.6 彩色箱线图4.2.7 箱线图样式均值和标准差4.2.8 造型异常值4.2.9 分组箱线图4.2.10 分组水平箱线图4.2.11 彩…

一起学习用Verilog在FPGA上实现CNN----(四)池化层设计

1 池化层设计 自顶而下分析池化层的设计过程 1.1 Average Pool Multi Layer 图为该项目的平均池化层,其包含一个AvgPoolSingle单元,模块的输入为图像特征矩阵,输出为池化后的特征矩阵 图片来自附带的技术文档《Hardware Documentation》 …

Java开发学习(三十六)----SpringBoot三种配置文件解析

一、 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时候就可以不写端口号了,如下 http://localhost/books/1 而 SpringB…

电脑屏录软件,这3款良心软件,分享给你

现在很多人会使用电脑屏录软件,有些用来记录游戏中的精彩操作,有些用来记录在线教学课程,有些用来记录在线视频会议。现在有各种各样的电脑屏录软件。选择一个好的电脑屏录软件是非常重要的。电脑屏录软件哪个好?下面小编分享3款良…

前端基础(四)_数据类型的强制转换

数据类型的强制转换就是通过js提供的函数进行数据转换。常见的就是将其他类型的数据转换成number类型和string类型。 一、其他类型转 number 类型 1.Number Number 方法将其他类型的数据转换为Number类型,返回一个新的数值,不会改变变量本身。 例1&…

【Linux】Linux编辑器-vim的使用以及指令集

推荐先将vim配置好后再使用会方便一些,就是将Linux下vim打造成C IDE的样子。自动配置vim vim1.vim的基本概念2.vim的基本操作2.1进入vim编辑界面2.2 如何在vim编辑代码2.3 退出vim并保存代码2.4一张图总结基本操作3.vim正常模式命令集3.1 进入插入模式和退出插入模式…

自己电脑中安装黑群辉NAS

前期准备:一个品牌U盘(制作后就是启动盘需一直插电脑上)、一台安装群辉的电脑(可以没有硬盘)、一台可正常开机的电脑 资源下载: 百度网盘链接:链接: https://pan.baidu.com/s/1t_yVON16Pt8H1ytpvf0J-A?pwdxe7m 提取…

Little Snitch 5 - Mac 老牌防火墙安全工具软件小飞贼,监控和组织特定软件的网络连接

Little Snitch 5 - Mac 老牌防火墙安全工具软件小飞贼,监控和组织特定软件的网络连接 一旦连接到Internet,应用程序就可以随时随地发送它们想要的任何东西。通常情况下,他们是为你做的。但有时,例如在跟踪软件、木马或其他恶意软件…

ICLR2022 | ViT-VQGAN+:Vector-quantized Image Modeling with Improved VQGAN

论文链接:https://openreview.net/forum?idpfNyExj7z2 | https://arxiv.53yu.com/abs/2110.04627原文标题:Vector-quantized Image Modeling with Improved VQGAN一、问题提出Natural language processing (NLP) has recently experienced dramatic imp…

程序结构你都懂了吗

上一篇:Python中基本输入和输出 昨天有粉丝问我,在看别人代码时总感觉脑子很混乱,不知道代码到哪步是停止,哪步又是开始,这是怎么回事呢? 其实很简单,因为还没有明白程序的基本执行流程&#x…

空间滤波基础

空间滤波是一种图像处理技术,它通过对每个像素周围的像素进行加权平均来平滑图像。这个过程的基本思想是,将每个像素的灰度值与它周围像素的灰度值进行加权平均,然后用平均值来替换原来的像素值。空间滤波器的大小和形状决定了每个像素的加权…