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

MySQL大事务的Recovery优化

你有没有碰到过mysqld进程启动了很长时间也起不来的情况?这时候我们可以用perf top命令查看一下MySQL进程主要在干什么事情。如果你查看到的信息如下图所示,启动过程中MySQL的主线程(mysqld_main函数开始的线程)绝大多数的时间都花在了回滚事务上。那么很可能是遇到了大事务回滚。

这种情况最常见的一个场景是一个大事务在写Binlog时把磁盘空间占满了,导致了实例的宕机重启。我曾经遇到的最大的Binlog文件超过了114GB。由于Binlog Cache的临时文件在写完Binlog后才被清理,所以这个事务总共占用了228GB的空间。MySQL的参数binlog_error_action用来控制写Binlog文件失败的行为,默认的配置是ABORT_SERVER,就是关闭进程。用户也可以配置这个参数为IGNORE_ERROR,意思是当写Binlog失败时关闭Binlog文件,后续的事务不再产生Binlog。这种情况显然会导致主备的数据不一致,因此除非不得以不要这样设置。

根因分析

为什么在MySQL进程启动时,主线程要做事务回滚的操作呢?这源自于Binlog的Crashsafe机制,详细的原理可以参考《MySQL的CrashSafe和Binlog的关系》,这里只做一个概括的介绍。事务的DML执行时会产生Binlog Events,当事务提交时这些Binlog Events会被写入到Binlog文件并持久化。为了保证MySQL宕机重启后数据和Binlog的一致性,MySQL设计了一个Crashsafe的机制。该机制对普通事务采用了两阶段提交(2PC),也称为内部 XA(Internal XA)。

如上图所示,在内部 XA 机制下一个事务的提交过程分为三个步骤:

  1. 存储引擎Prepare事务。事务状态由ACTIVE变为PREPARED,并将事务的状态和XID持久化到Redo中。
  2. 事务会产生一个Xid_event,同DML的Binlog Events一同写入到binlog文件中,并持久化。
  3. 提交事务。

当异常宕机时,事务可能处于以下几种状态之一:

  • Active:在两阶段提交里,此类事务从未被写入 Binlog。
  • Prepared 但未写入 Binlog(或仅部分写入):事务已处于 Prepared 状态,但其 XID 未出现在 Binlog 文件中。
  • Prepared 且已写入 Binlog:事务已处于 Prepared 状态,且其 XID 已出现在 Binlog 文件中。
  • Committed:事务已经写入Binlog并且提交。

对于Committed的事务,设计上已经保证了它的Binlog Events一定写入了Binlog文件。因此Binlog和数据是一致的,启动时无需任何操作。对于Active的事务,Binlog Events肯定没有写入Binlog文件,InnoDB有一个后台回滚线程会自动将其回滚Prepared的事务则需根据最后一个Binlog 文件中的XID信息进行处理。如果该事务的XID出现在了Binlog文件中,则需要提交该事务来保证Binlog和数据的一致性;反之则回滚该事务。处理Prepared事务的过程称为Binlog Recovery必须在MySQL向用户提供服务之前完成。事务提交通常很快,但回滚往往耗时与其执行时间相当。如果一个事务执行用了1小时,回滚很可能也需要 1小时,MySQL在此期间将不可用。

为什么必须要在提供服务前回滚所有事务呢?这和XID的实现有关系。XID是用MySQL前缀加上query_id构成的,query_id是一个全局的计数器,系统重启后会重新从1开始计数。如果在启动后,不对之前的Prepared事务进行提交或者回滚,那么就可能出现两个Prepared的事务有相同XID的情况。在恢复时,就无法区分哪个事务该提交,哪个事务该回滚。

异步回滚Prepared事务

在AliSQL中,我们设计了一套异步回滚的机制来解决这个问题。

如上图所示,这个设计中将Prepared的事务回滚分为两个部分:

  1. 主线程将事务状态设为Active并持久化该状态。
  2. 利用InnoDB的后台回滚线程异步回滚事务的所有操作。

Binlog Recovery在完成第一部分后即可立即对外提供服务。由于第一步的执行非常快,Binlog Recovery可以在很短的时间内完成。

在宕机重启时,Active的事务会被InnoDB通过后台线程直接回滚掉,不需要XID来辅助决策。所以恢复时,只要将要回滚的事务的状态从Prepared改成Active就能避免两个Prepared事务有相同XID的问题。这里关键是要对Active的状态做持久化,保证在宕机重启后事务的状态仍然是Active,这样InnoDB就会自动将这个事务回滚掉。

社区版的InnoDB原本对Prepared事务的回滚就是先设置成Active状态,然后再根据Undo记录进行回滚。Active的状态会记录到Redo中,只是没有对Redo做持久化。然而InnoDB默认每秒会做一次Redo的持久化,所以在改成Active后,很快就会被持久化。因此当碰到了大事务回滚造成实例无法启动的情况时,即使是在社区版本,我们只要强制重启MySQL进程,大事务就会转变成后台回滚,不再阻塞实例的启动

这个功能的源码贡献给了MariaDB,已经合并到MariaDB-11.7中,详情参考MDEV-33853[1]。

结论

通过异步回滚的设计,在Binlog Recovery阶段只需要将Prepared的事务的状态设置为Active,真正耗时的事务回滚则由InnoDB的后台回滚线程异步的执行。通过这个优化,原本需要几十分钟甚至几个小时的启动过程,被缩短到秒级完成。

http://www.jsqmd.com/news/248868/

相关文章:

  • 智慧校园一站式解决方案 | 创新教育前沿平台
  • 智慧校园软件平台:实现校园管理的统一入口与数据融合
  • 日常渗透测试怎么玩?通杀漏洞挖掘的实现方法看这里
  • 基于CODESYS开发的多轴运动控制程序框架将逻辑和运动控制分开,通过封装单轴控制功能块来操作...
  • 瀚高数据库中 java代码类型与bit对应
  • 可靠性测试
  • 用Spring的ApplicationEventPublisher进行事件发布和监听
  • nacos服务安装并启动
  • 方法类的倒推过程结束 七
  • 人工智能之数字生命-场景类的功能
  • 50个域渗透手法全覆盖 万字长文 适合收藏!从零基础入门到精通,收藏这一篇就够了!
  • PaperNex领衔9款AI论文工具实操指南:半天3万字+真实参考文献
  • 收藏必备!情境工程:大模型时代企业知识管理系统的革命性变革
  • 一文搞懂大模型智能体工作原理:从PEAS模型到TAO循环,小白也能轻松入门!
  • 智慧校园系统-打造数字化、智能化的教育管理平台
  • django-flask基于python的车辆挡泥板机器人工厂管理系统
  • 程序员转型大模型产品经理必看:收藏这份详细学习路线_大模型产品经理学习路线详述
  • django-flask基于python的超市库存管理系统的设计与实现
  • ARP欺骗攻击的7种解决方案,你知道几种?
  • 深度测评10个AI论文网站,本科生轻松搞定毕业论文!
  • django-flask基于python的车牌识别停车场与车辆管理系统
  • EtherNet/IP转Profibus DP协议转换网关实现汇川PLC与西门子PLC通讯在矿山与冶金的应用案例
  • LLM语音合成让医患沟通更顺畅
  • AI知识库(2)豆包AI手机介绍
  • eSIM工业网关是什么?有什么优势?
  • InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default
  • 如何使用SpringAI来实现一个RAG应用系统
  • 环保与水务行业PLC设备远程诊断与维护解决方案
  • 储能系统绝缘监测的技术突破:微电流传感器在直流侧的应用与优化
  • 巴菲特的股东回报政策:股息与回购的平衡