MySQL InnoDB中一个update语句从执行到提交的全过程(3)

 接上文MySQL InnoDB中一个update语句从执行到提交的全过程(2)-CSDN博客

目录

六、本地提交

怎样保证binlog和redo log的状态一致呢?

MySQL 中的内部 XA 机制

宕机时不同状态的处理

物理落盘策略

七、主备复制

八、返回提交成功

总结一下

九、脏页刷入磁盘

​编辑


六、本地提交

修改完成之后,还有一个至关重要的步骤,那就是commit。只有完成commit,这个update才能真正地持久化下来,不会因为MySQL的任何故障而丢失。

对于Innodb存储引擎而言,提交过程真正需要写入的只有redolog。

Innodb存储引擎中,redolog落盘的步骤是什么?

redo日志文件的结构

  • MySQL 8.0.30之前,redo日志文件默认有两个:ib_logfile0和ib_logfile1,两个日志
  • 文件会进行交替的写入;
  • MySQL 8.0.30,对redo log进行了重构,允许了redo log空间的动态修改。

提交阶段InnoDB存储引擎要落盘redo log,MySQL服务器层要落盘binlog(binlog在之前的事务执行阶段就会生成)。

怎样保证binlog和redo log的状态一致呢?

MySQL 中的内部 XA 机制

MySQL采用了一种内部XA事务的机制保证binlog和redolog的状态和顺序都一致,其核心是我们在分布式领域耳熟能详的两阶段提交(2PC),具体过程如下:

  • prepare阶段:InnoDB刷redo log到磁盘,redo log落盘完成后,修改事务状态为TRX_PREPARED。

    • prepare如果失败,那么事务会回滚;而prepare成功后,就进入两阶段提交的commit阶段。

  • commit阶段:MySQL 服务器层会首先将 Binlog 写入磁盘,写入完成后,修改事务状态为TRX_NOT_STARTE。事务的提交就算成功了。

宕机时不同状态的处理
  • 事务状态为TRX_ACTIVE,那么直接回滚事务;
  • 事务状态为TRX_NOT_STARTED,表示事务的redo log和binlog都已落盘,认为事务已经提交;
  • 恢复时如果发现一个事务状态为TRX_PREPARED,根据binlog的写入状态来判断提交还是回滚:
    • 如果binlog没写入成功,则回滚
    • 如果binlog写入成功,则提交并修改事务状态为TRX_NOT_STARTED

通过这种方式,MySQL 能够确保在系统崩溃的情况下,Binlog 和 Redo Log 保持一致性,即不会出现 Binlog 已记录但 Redo Log 未提交的情况,或反之亦然。这种一致性保证对于基于 Binlog 的主从复制和 Point-in-Time 恢复非常关键。

物理落盘策略

只不过上面提到的都是逻辑上,实际是否把日志写入磁盘,与物理落盘策略有关,由下面俩参数控制:

  • nnodb_flush_log_at_trx_commit:控制redo log的落盘,可选值为0,1,2。
    • 0表示每秒进行一次刷新
    • 1表示每次事务提交都会落盘
    • 2表示每次事务提交会把redo log缓冲写入操作系统缓冲,每秒刷盘
  • sync_binlog:控制binlog的落盘,可选值为0,1。
    • 0表示每次提交事务,不刷binlog
    • 1表示每次提交事务,都会立刻写binlog到磁盘
  • MySQL 8.0默认都设置为1。

这样,我们的SQL就在当前MySQL服务器上提交完成了。

七、主备复制

但在很多场景下,这并不意味着我们直接可以返回客户端提交成功了,如果配置了主从复制,还需要根据我们的同步策略来判定是否符合提交成功的条件。

主从复制的策略

  • 异步复制:主库写完binlog后即可返回提交成功,无需等待备库响应
  • 半同步复制:主库接收到指定数量的备机转储relay log成功的ACK后可返回提交成功;
  • 同步复制:主库等到备库回放relaylog执行完事务之后才可返回提交成功。

因为半同步复制兼具了高可用性和性能,所以我们通常都会选择半同步复制的策略。

假设我们这条SQL是发往一个一主两备的MySQL集群,配置的备机响应数=1,那么主库接收到一个备库转储relaylog成功的响应后,即可返回提交成功。

此时,如果备机一直没有响应怎么办?难不成一直等下去?Mysql原生的半同步复制策略有一个超时时间,超过这个时间还没有备机响应的话,主机就自动提交了。显然这是很不安全的,因为这种情况下半同步复制退化成了异步复制策略。所以很多开源工具和基于MySQL的分布式数据库都在这里进行了一些改造。

八、返回提交成功

到这里,客户端收到了提交成功的反馈,可以认为整个事务已经结束了,修改命令已经执行完成,并且持久化在了我们的MySQL数据库中。

总结一下

(1)首先,任何sql都要在事务中执行,所以我们的第一步就是开启事务,但不会在这里就分配事务号。接下来update sql就发往mysql服务器层,这时我们分配的T024这个事务号。

(2)然后经过了sql解析、词法分析、语法分析。查询优化等步骤,最终生成一个物理执行计划,通过这个物理执行计划,mysql执行器开始调用Innodb存储引擎的接口进行后面的修改操作。

(3)修改这项数据之前把它查出来,我们先根据根节点的页号获取到B+树的根节点,然后解析根节点这个索引页,通过其中的条件比较找到下一层节点页号,重复这个过程,直至找到叶子节点,解析数据页,从而获取到我们想要的数据。

这个过程中的所有页要么一开始就在buffer pool中,要么从磁盘加载到buffer pool中。

(4)然后我们要对这样数据进行校验锁和加锁。这里会上三个锁,分别是mysql服务器层的元数据锁,Innodb存储引擎层的表级意向IX锁和行级排他X锁。加上锁后,我们就可以对数据进行修改。

(5)这个过程需要在InnoDB存储引擎写入数据页、undolog和redolog,在MySQL服务器层写入binlog。所有的这些在这一步都不用落盘。

(6)修改和生成日志完成后,就可以开始提交了。

主机提交需要redolog和binlog落盘。这里,通过MySQL内部XA事务机制来协调redolog和binlog,保证两个日志的状态一致。

(7)如果配置了主备复制,那么还需要把binlog同步给备机。在最常用的半同复制模式下,有指定数量的备机转储返回成功后即可返回提交成功。如果超过超时时间还没达到指定数量的反馈,则会退化成异步复制,主机直接返回提交成功。

(8)至此,从客户端的角度来说,这个事务提交成功了。

九、脏页刷入磁盘

但在数据库内部的概念里,我们修改后的数据已经还没落盘。

看一下MySQL把脏页落盘的过程。

想象一下MySQL落盘的操作,要把这个16K的页刷到磁盘中的一块区域中,但这里有一个风险。

如果我们写这个页的时候,比如说只写了4K,系统就发生了宕机,这个时候的异常状态怎么处理呢?大家可能第一时间会想到用redolog来恢复,但我们已经解读过redolog的结构。redolog记录页物理结构所做的变更。是一个基于完整且正确,只是老版本的页了。现在我们在磁盘中的页已经被破坏,是不完整的。所以redolog也没法很好的恢复。

这个时候应该怎么处理呢?

MySQL针对这种情况设计了两次写(double write)机制,其实原理很简单,就是先把脏页都复制到doublewrite buffer中,然后doublewrite buffer先落盘,而后脏页再真正落盘。

这样如果发生了宕机恢复时,doublewrite buffer中的页覆盖磁盘中的页,再进一步用redolog恢复,这样就能确保页是完整的,解决了我们的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值