Mysql的事务是如何实现的
大家好,我是程序员花卷,现在是一名大三学生,在持续学习进步,在这里分享一些我的学习感悟和积累
对于MySQL的引擎InnoDB来说,核心靠:redo log、undo log、MVCC、锁四大组件实现事务 ACID。
一、ACID 分别靠什么保证
- A 原子性:要么全成要么全回滚 → undo log
- C 一致性:业务规则 + AID 共同保障,这个是最后的结果
- I 隔离性:多个事务互不干扰 → MVCC + 行锁 + 间隙锁
- D 持久性:提交后数据永久保存,宕机不丢 → redo log
二、核心日志详解
1. undo log(回滚日志,原子性)
记录数据修改前的旧版本。
- 事务修改数据前,先把原始数据写入 undo log
- 事务 rollback:直接拿 undo log 里旧数据恢复,实现回滚
- 同时:undo log 是MVCC 版本链的数据源,保存历史快照版本
undo log 不是立即删除,没有事务再引用这个版本才会 purge 清理。
2. redo log(重做日志,持久性)
记录数据修改之后的变更,是 crash-safe 核心。
-
InnoDB 不是每次写都刷磁盘页(随机 IO 慢),采用WAL 预写日志:
- 修改内存 buffer pool 里的数据页
- 先写 redo log 到磁盘(顺序写,很快)
- 后台线程异步刷脏页到磁盘(刷盘时机由 checkpoint 控制)
-
宕机恢复:重启读取 redo log,把已经提交但还没刷到数据页的变更重做,保证持久化。
-
redo log 是循环写,有固定大小的文件组。
WAL 核心原则:写数据前先写日志
三、MVCC(多版本并发控制 → 隔离性)
InnoDB 的可重复读 RR默认用 MVCC,不加锁实现读,解决快照读的幻读问题(RR 级别)。 核心要素:
-
隐藏列:
每行数据自带三个隐藏字段
DB_TRX_ID:最近修改这条记录的事务 IDDB_ROLL_PTR:指针,指向 undo log 里的旧版本,串成版本链表DB_ROW_ID:行 ID,没有主键时使用
-
Read View(读视图)
事务开启快照读时生成,用来判断当前版本对本事务是否可见:
- 记录当前活跃事务 ID 集合、最小活跃 id、下一个待分配事务 id 可见规则:
- 版本 trx_id < 最小活跃 id:可见(事务已经提交)
- 版本 trx_id > 下一个待分配 id:不可见(事务还没开始)
- 在活跃区间内:判断是否在活跃事务列表,在则不可见,不在可见
-
快照读 vs 当前读
- 快照读:普通
select,走 MVCC,读历史版本,不加行锁 - 当前读:
select ... for update / lock in share mode、update、delete、insert,读取最新版本,加行锁
- 快照读:普通
RR 与 RC 的区别:
- RC(读已提交):每次 select 都会重新生成 Read View,可以读到别的事务已提交数据,会有幻读
- RR(可重复读):事务内只在第一次 select 生成一次 Read View,全程复用,解决快照读幻读;但当前读的幻读靠间隙锁 + 临键锁解决
四、锁机制(隔离性,解决当前读冲突)
MVCC 只解决快照读;写操作 / 当前读依靠锁:
- 行锁:锁住某一行记录
- 间隙锁 (Gap Lock):锁住索引间隙,防止插入,解决幻读(RR 才有,RC 没有间隙锁)
- 临键锁 (Next-Key Lock):行锁 + 间隙锁,左开右闭区间,InnoDB RR 默认加的锁
五、事务执行简化流程
begin;
update t set a=1 where id=1;
commit;
- begin:开启事务
- 修改 id=1 行:先把旧数据写入 undo log;修改 buffer pool 内存数据;写 redo log(prepare 阶段)
- commit:redo log 写入 commit 标记,事务提交成功
- 后台:后续 checkpoint 把内存脏页刷入磁盘数据文件
- 若中途 rollback:读取 undo log 恢复旧数据
六、两阶段提交(2PC,保证 redo 和 binlog 一致性)
MySQL 有 redo log (InnoDB) + binlog (Server 层) 两份日志,用2PC保证两份日志要么都成功,要么都失败。
- prepare:写 redo log 并标记 prepare,写 binlog
- commit:redo log 标记 commit 宕机恢复判断:
- redo prepare 有,但 binlog 没写完 → 回滚
- redo prepare + binlog 完整 → commit
一句话总结
InnoDB 事务:WAL 的 redo log 保证宕机持久;undo log 提供回滚和数据版本;MVCC 利用版本链 + ReadView 实现无锁快照读;锁(临键锁)处理写冲突;2PC 保证 redo 与 binlog 日志一致,最终实现 ACID。
我是后端新人花卷,持续学习中,如果你觉得文章有帮助,请帮忙转发给更多的好友,感谢大家的阅读。