当前位置: 首页 > news >正文

MYSQL—两阶段提交

binlog 和 redo log:

有binlog了为什么还要有redo log:

  1. 历史原因,MyISAM不支持崩溃恢复,而InnoDB在加入MySQL前就已经支持崩溃恢复了
  2. InnoDB使用的是WAL技术,事务提交后,写完内存和日志,就算事务完成了,脏页有没有落盘不在乎!没有redo的情况下,奔溃恢复后如果binlog是残缺的,说明事务没成功,可以通过undo回滚;如果binlog是完整的,却并不能确定崩溃前脏页有没有落盘,这就无法以binlog是否完整判断事务在数据库崩溃前是否已经持久化、脏页是否落盘,而引入redo后,配合两阶段提交,加上redo是物理日志,就可以进行崩溃恢复了。而且binlog本身因为内容格式的原因,并不适合做崩溃恢复,更适合主从同步或备份恢复。

能不能只用redo,不用binlog:

不考虑主从集群、备份恢复,只从崩溃恢复和高效写WAL角度考虑的话,可以把binlog关闭。binlog本身默认就是关闭

为什么 binlog cache 是每个线程自己维护的,而 redo log buffer 是全局共用的:

binlog 是不能“被打断的”。一个事务的 binlog 必须连续写(主从同步要求,不然可能会出错),因此要整个事务完成后,再一起写到文件里。而 redo log 并没有这个要求(redo用于崩溃恢复,物理日志,先修改哪个后修改哪个无所谓),中间有生成的日志可以写到 redo log buffer 中。redo log buffer 中的内容还能“搭便车”,其他事务提交的时候可以被一起写到磁盘中。

两阶段提交

两阶段提交只有在开启了binlog的情况下才会有,而binlog默认是关闭的,那么默认情况下就是

将事务的数据变更记录到redo(prepare状态的redo),当事务提交时,进入到commit阶段,先把redo的状态更改为commit,然后根据innodb_flush_log_at_trx_commit参数有不同行为:

参数为0,啥也不干

参数为1,write + fsync

参数为2,write

崩溃恢复时,如果redo是commit,那么直接用来做数据恢复,如果是prepare,结合redo的完整性决定是恢复还是回滚事务

在开启了binlog的情况下,需要考虑redo log 和 binlog的一致性:

  • 如果在将 redo log 刷入到磁盘之后, MySQL 突然宕机了,而 binlog 还没有来得及写入。MySQL 重启后,通过 redo log 能崩溃恢复,但是 binlog 里面没有该事务的记录,在主从架构中,binlog 会被复制到从库,由于 binlog 丢失了记录,主从发生了不一致;
  • 如果在将 binlog 刷入到磁盘之后, MySQL 突然宕机了,而 redo log 还没有来得及写入。由于 redo log 还没写,崩溃恢复以后这个事务无效,而 binlog 里面记录了这条记录,在主从架构中,binlog 会被复制到从库,从库更新了这条记录,主从发生了不一致;

MySQL 为了避免出现两份日志之间的逻辑不一致的问题,使用了「两阶段提交」来解决:

prepare 阶段:将 XID(内部 XA 事务的 ID) 写入到 redo log,同时将 redo log 对应的事务状态设置为 prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用,如果是0,不会持久化);

commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘(sync_binlog = 1 的作用),接着调用引擎的提交事务接口,将 redo log 状态设置为 commit,此时该状态并不需要持久化到磁盘,只需要 write 到文件系统的 page cache 中就够了,因为只要 binlog 写磁盘成功,就算磁盘里的 redo log 的状态还是 prepare 也没有关系,一样会被认为事务已经执行成功;

不管是时刻 A(redo log 已经写入磁盘, binlog 还没写入磁盘),还是时刻 B (redo log 和 binlog 都已经写入磁盘,还没写入 commit 标识)崩溃,此时的 redo log 都处于 prepare 状态。

在 MySQL 重启后会按顺序扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就拿着 redo log 中的 XID 去 binlog 查看是否存在此 XID:

  • 如果 binlog 中没有当前内部 XA 事务的 XID,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。
  • 如果 binlog 中有当前内部 XA 事务的 XID,说明 redolog 和 binlog 都已经完成了刷盘,则提交事务(使用prepare的redo进行崩溃恢复,虽然是prepare,但redo是完整的)。对应时刻 B 崩溃恢复的情况。

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

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

两阶段缺点:

  • 磁盘 I/O 次数高:对于“双1”配置,每个事务提交都会进行两次 fsync(刷盘),一次是 redo log 刷盘,另一次是 binlog 刷盘。
  • 锁竞争激烈:两阶段提交虽然能够保证「单事务」两个日志的内容一致,但在「多事务」的情况下,却不能保证两者的提交顺序一致,因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志的提交顺序一致。在早期的 MySQL 版本中,通过使用 prepare_commit_mutex 锁来保证事务提交的顺序,在一个事务获取到锁时才能进入 prepare 阶段,一直到 commit 阶段结束才能释放锁,下个事务才可以继续进行 prepare 操作。

相关文章:

  • 影刀RPA怎么和AI结合,制作自动采集小红书爆款文章+自动用AI改写标题、内容+用AI文生图生成发文图片+自动在小红书上发布文章
  • 【NLP】This Post Is All You Need阅读笔记
  • 【数字图像处理】立体视觉信息提取
  • Relay IR的核心数据结构
  • Docker 与 Docker-Compose 的区别
  • leetcode day36 01背包问题 494
  • 08_Docker Portainer可视化管理
  • 【Linux】47.高级IO(1)
  • SQLiteDatabase 增删改查(CRUD)详细操作
  • Java函数生成实际应用案例:数据处理流水线
  • # 基于PyTorch的食品图像分类系统:从训练到部署全流程指南
  • 基于javaweb的SpringBoot校园失物招领系统设计与实现(源码+文档+部署讲解)
  • 鸿蒙NEXT开发权限工具类(申请授权相关)(ArkTs)
  • Python-27:游戏英雄升级潜力评估
  • 【TeamFlow】4.3.1 SI单位系统库(Units)
  • 《MySQL 核心技能:SQL 查询与数据库概述》
  • 达梦官方管理工具 SQLark 更新--不仅支持达梦、Oracle、MySQL,还新增 PostgreSQL 数据库!
  • android 发送onkey广播,Android 添加键值并上报从驱动到上层
  • PerfettoSQL
  • 【RAG】一篇文章介绍多模态RAG(MRAG)
  • 民生访谈|让餐饮店选址合规性可查、社区妙趣横生,上海有实招
  • 王励勤谈国乒备战洛杉矶奥运会:要对六块金牌制定新的战略
  • 新“出差三人组”亮相!神二十乘组简历来了
  • 读懂城市丨“花木之乡”沭阳,一场持续五年的“诚信实验”
  • 2025年度“沪惠保”今日开售:保费维持129元/人,进一步扩增国内外特药种类
  • 江南大部、江淮南部等地今起有较强降雨,水利部部署防范工作