写点什么

ava 王者修炼手册【Mysql 篇 - 事务】:吃透 ACID 本质 + 隔离级别底层 + 大事务排查优化方案,掌控事务核心逻辑

作者:DonaldCen
  • 2025-12-05
    广东
  • 本文字数:3127 字

    阅读完需:约 10 分钟

ava 王者修炼手册【Mysql 篇 - 事务】:吃透 ACID 本质 + 隔离级别底层 + 大事务排查优化方案,掌控事务核心逻辑

大家好,我是程序员强子。

前面学习了 Mysql 四大金刚其中的三大金刚 索引 & 日志 &

今天专注把 事务 相关给弄明白~

来看看今天学习的知识点:

  • ACID 是什么,底层是怎么实现的

  • 隔离级别是什么,底层是怎么实现的

  • 大事务是什么,怎么解决,有哪些解决方案

不解释了,赶紧上号~巅峰开始了

ACID

原子性(A)

靠「Undo Log + 事务状态标记」实现

核心目标

失败时回滚到事务开始前的状态,成功时提交所有修改

即同时成功,同时失败~

底层机制

依赖 Undo Log!!

undo log 的作用就是记录事务执行前的数据 快照反向操作,供事务回滚时使用

工作流程如下

  • 事务开始前,InnoDB 为每个修改操作记录 Undo Log:UPDATE:记录 旧值(如 A 余额 1000);DELETE:记录 被删除的数据,相当于逻辑上的 INSERT 反向操作 INSERT:记录 待删除的标识,事务回滚时直接删除这条插入数据

  • 事务执行中,若发生错误(如代码异常、网络中断、死锁),数据库触发 ROLLBACK:遍历该事务的 Undo Log,反向执行操作

  • 事务执行成功COMMIT 时,Undo Log 不会立即删除,后续用于 MVCC 读,会定期回收

持久性(D)

靠「Redo Log + WAL 机制」实现

核心目标

事务提交后,数据修改永久生效,不受崩溃影响

底层机制 1:Redo Log

作用

  • 记录事务执行的 物理修改操作

  • 比如 把数据页 X 中 A 的余额从 1000 改成 900,而非数据本身

特点

  • 日志是 顺序写入,这是因为磁盘顺序 IO 速度远快于随机 IO

  • 记录的是 已执行的修改,崩溃后可通过日志 重做 这些操作

底层机制 2:WAL

先写日志,再写数据文件

  • 数据修改最终要落盘,但属于随机 IO,速度慢;

  • 若先数据落盘再写日志,中途崩溃,磁盘的数据 和 日志就不一致~ 而且也无法恢复 日志 相关数据

  • 先写 Redo Log(顺序 IO 快),再异步刷盘数据文件,即使崩溃,重启后可通过 Redo Log 重做未刷盘的修改

工作流程

  • 事务执行中,每个修改操作先写入 Redo Log Buffer

  • 事务 COMMIT 时,触发 fsync 把 Redo Log Buffer 中的日志刷到磁盘

  • 数据库后台线程会定期把内存中的数据页(脏页)刷到磁盘数据文件;

  • 若崩溃时数据页未刷盘,重启后数据库:读取 Redo Log,找到所有已提交但未刷盘的事务相关日志;重新执行这些事务的 Redo Log 操作,把数据刷到磁盘,保证持久性

隔离性(I)

靠「锁机制 + MVCC」实现

核心目标

解决并发事务的干扰问题,避免脏读不可重复读幻读

并发事务的三大问题

核心实现

锁机制 + MVCC

本质是 : 通过控制写冲突,通过 MVCC 优化读并发

隔离也就说代表 多个事务并发进行 各自有序正常进行,互不影响

锁机制解决了什么问题?

  • S 锁 保证 多个事务可同时加 S 锁(读 - 读不冲突);

  • 一个事务加 X 锁后,其他事务不能加 S 锁X 锁(写 - 读、写 - 写冲突)

  • 核心作用是 保证 写操作互斥

MVCC 解决了什么问题?

  • 数据保留多个版本的快照,读事务访问 旧版本快照,写事务修改 新版本数据无需加锁,是非阻塞读

  • 读已提交(RC)解决脏读,允许不可重复读每次查询生成新的 Read View,只能读到 查询瞬间已提交的版本

  • 可重复读(RR)解决不可重复读部分幻读事务开始时生成一次 Read View,全程使用该视图

一致性(C)

靠「A+I+D + 约束校验」实现

  • 原子性(A):避免 部分修改生效 导致的数据不一致

  • 隔离性(I):避免 并发事务干扰导致的数据不一致(如脏读、幻读)

  • 持久性(D):避免 提交后数据丢失 导致的不一致

  • 数据库层面:主键唯一、外键关联

事务隔离级别

定义

实现原理

  • 通过控制 写冲突

  • 通过 MVCC 优化 读并发

  • Read View 的生成策略

通过以上三个核心,实现不同的隔离强度

读未提交(RU)

无锁 + 无 MVCC

  • 直接读取数据的最新版本,不加任何读锁,也不通过 MVCC 生成历史快照

  • 实际工作中很少使用

读已提交(RC)

行锁 + MVCC

  • 写操作加行级排他锁(避免写冲突)

  • 读操作通过 MVCC 实现

  • 每次查询都生成新的 Read View

  • 电商高并发场景会使用到,可重复读靠应用程序保证

可重复读(RR)

行锁 + 间隙锁 + MVCC

  • 写操作加行级排他锁

  • 范围查询加间隙锁 / Next-Key Lock

  • 读操作通过 MVCC 实现

  • 事务开始时生成一次 Read View,全程复用

  • mysql 默认使用这个级别

串行化(S)

表锁 / 行锁串行执行

  • 读操作加表级共享锁

  • 写操作加表级排他锁,或通过行锁强制事务按顺序执行

  • 完全禁用并发;

  • 仅适用于数据一致性要求极高并发量极低的场景

线上排查大事务问题

核心原则: 先止血再定位后优化

优先保证业务可用性,再通过监控日志数据库工具逐层拆解根因

线上大事务不会凭空出现,通常伴随

  • 接口超时

  • 数据库连接耗尽

  • 锁等待飙升

等一系列现象,所以需要使用相关工具去排查

依赖工具

排查步骤

从应用层锁定嫌疑接口

  • 查看监控平台:筛选 事务耗时 p95>500ms 的接口

  • 检索应用日志在 ELK 中搜索关键词 @Transactional、Transaction committed、Transaction rolled back,结合接口路径和耗时,找到执行时间长的事务方法

P90 ,P95,P99 是什么意思?有什么用?

比如王者,

  • 统计 100 次技能释放的延迟(单位:ms),按从小到大排序

  • P95 就是 找到第 95 个数值,这个值就是 P95

  • P90 P99 同理

同理,请求的时延,也是 100 次,然后排序好取 95 个

那为什么是 P90/P95/P99 而不是其他的?

  • P95 能 避开极端值干扰 ,能反映绝大多数用户的真实体验

  • 行业惯例 导致了通用性

  • 主流压测工具(JMeter、Gatling)、监控系统(SkyWalking、New Relic)默认展示 P90/P95/P99 这三个指标

不同场景下的百分位选型参考

  • 核心接口(支付、登录):用 P99(仅 1% 异常),优先保障核心流程零卡顿;

  • 非核心接口(商品列表查询、历史记录统计):可用 P90,降低服务器负载压力;

  • 通用业务接口(订单查询、用户信息修改):P95 是默认最优解。

从数据库层锁定嫌疑 SQL

  • 通过监控,或者通过查看慢 sql 日志 定位慢 sql

  • 找到 sql 后,分析 sql 执行慢的原因?是否加索引,索引是否失效是否是数据量太大是否是深度分页导致是否是锁竞争激烈导致,是否有死锁

定位根因

排查维度 1:事务边界是否过大

  • 远程调用(Feign/HTTP 接口):比如下单接口中调用支付服务、物流服务;

  • 消息发送(RocketMQ/Kafka):同步发送消息且未异步化;

  • 文件 IO / 缓存操作:比如事务内写文件、同步更新 Redis(非核心操作);

  • 循环操作:比如事务内循环插入 1000 条数据、循环查询数据库。

排查维度 2:事务内 SQL 是否低效

  • 分析 SQL 执行计划:对每条 SQL 执行 EXPLAIN,重点关注:type 字段:是否为 ALL(全表扫描),需优化为 range/ref/eq_ref;key 字段:是否为 NULL(未使用索引),需补充索引;rows 字段:预估扫描行数是否过大(比如超过 1000 行);Extra 字段:是否有 Using filesort(文件排序)、Using temporary(临时表),需优化 SQL 结构

  • 排查批量操作

排查维度 3:是否存在锁竞争

大事务是否长时间持有锁,导致其他事务阻塞

  • 分析锁类型:通过 show engine innodb status\G 查看 InnoDB 状态,重点看 TRANSACTIONS 部分,判断是行锁、表锁还是间隙锁导致的阻塞

  • 表锁:通常因 DDL 操作、无索引条件的 UPDATE/DELETE 导致

  • 间隙锁:RR 隔离级别下的范围查询(如 WHERE id BETWEEN 1 AND 100)可能触发,导致插入阻塞

排查维度 4:是否存在资源瓶颈

数据库连接池、磁盘 IOCPU 是否达到瓶颈,导致事务执行变慢

  • 监控连接池状态(通过 Prometheus):活跃连接数空闲连接数等待队列长度

  • 检查数据库服务器资源 CPUtop 命令查看 MySQL 进程 CPU 占用率(是否持续 > 80%);磁盘 IOiostat 命令(--help 查看完整命令) 查看磁盘读写 IO(%util 持续 > 90% 表示 IO 瓶颈);内存free 命令查看 MySQL 缓存(innodb_buffer_pool_size)是否足够,是否频繁换页

验证优化

总结

作为 Java 后端开发者,事务是我们绕不开的核心技能,更是构建可靠系统的 地基

希望这篇文章能帮你打通 ACID隔离级别大事务的知识任督二脉,在实际项目中少踩坑

如果觉得帮到你们,烦请加个点赞关注推荐 三连,感谢感谢~~

熟练度刷不停,知识点吃透稳,下期接着练~

发布于: 刚刚阅读数: 2
用户头像

DonaldCen

关注

有个性,没签名 2019-01-13 加入

跟我在峡谷学Java 公众号:程序员悟空的宝藏乐园

评论

发布
暂无评论
ava 王者修炼手册【Mysql 篇 - 事务】:吃透 ACID 本质 + 隔离级别底层 + 大事务排查优化方案,掌控事务核心逻辑_ACID 实现原理_DonaldCen_InfoQ写作社区