Mysql的事务是如何实现的

5 阅读4分钟

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 预写日志:

    1. 修改内存 buffer pool 里的数据页
  1. 先写 redo log 到磁盘(顺序写,很快)
  2. 后台线程异步刷脏页到磁盘(刷盘时机由 checkpoint 控制)
  • 宕机恢复:重启读取 redo log,把已经提交但还没刷到数据页的变更重做,保证持久化。

  • redo log 是循环写,有固定大小的文件组。

WAL 核心原则:写数据前先写日志

三、MVCC(多版本并发控制 → 隔离性)

InnoDB 的可重复读 RR默认用 MVCC,不加锁实现读,解决快照读的幻读问题(RR 级别)。 核心要素:

  1. 隐藏列:

    每行数据自带三个隐藏字段

    • DB_TRX_ID:最近修改这条记录的事务 ID
    • DB_ROLL_PTR:指针,指向 undo log 里的旧版本,串成版本链表
    • DB_ROW_ID:行 ID,没有主键时使用
  2. Read View(读视图)

    事务开启快照读时生成,用来判断当前版本对本事务是否可见:

    • 记录当前活跃事务 ID 集合、最小活跃 id、下一个待分配事务 id 可见规则:
    • 版本 trx_id < 最小活跃 id:可见(事务已经提交)
    • 版本 trx_id > 下一个待分配 id:不可见(事务还没开始)
    • 在活跃区间内:判断是否在活跃事务列表,在则不可见,不在可见
  3. 快照读 vs 当前读

    • 快照读:普通select,走 MVCC,读历史版本,不加行锁
    • 当前读:select ... for update / lock in share mode、update、delete、insert,读取最新版本,加行锁

RR 与 RC 的区别:

  • RC(读已提交):每次 select 都会重新生成 Read View,可以读到别的事务已提交数据,会有幻读
  • RR(可重复读):事务内只在第一次 select 生成一次 Read View,全程复用,解决快照读幻读;但当前读的幻读靠间隙锁 + 临键锁解决
四、锁机制(隔离性,解决当前读冲突)

MVCC 只解决快照读;写操作 / 当前读依靠锁:

  1. 行锁:锁住某一行记录
  2. 间隙锁 (Gap Lock):锁住索引间隙,防止插入,解决幻读(RR 才有,RC 没有间隙锁)
  3. 临键锁 (Next-Key Lock):行锁 + 间隙锁,左开右闭区间,InnoDB RR 默认加的锁
五、事务执行简化流程
begin;
update t set a=1 where id=1;
commit;
  1. begin:开启事务
  2. 修改 id=1 行:先把旧数据写入 undo log;修改 buffer pool 内存数据;写 redo log(prepare 阶段)
  3. commit:redo log 写入 commit 标记,事务提交成功
  4. 后台:后续 checkpoint 把内存脏页刷入磁盘数据文件
  5. 若中途 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。

我是后端新人花卷,持续学习中,如果你觉得文章有帮助,请帮忙转发给更多的好友,感谢大家的阅读。