DEV Community

port smith
port smith

Posted on

MySQL 的 MVCC:看不见的“时间机器”怎么帮你躲过脏读和幻读?

MySQL 的 MVCC:那台你从没启动过,却一直在为你倒带的“时间机器”

你有没有在图书馆借过一本《数据库原理》?翻开时目录还是旧版,而管理员其实在你借走后悄悄上架了修订版——但你完全不受影响。又或者,多人协同编辑一份文档,每人打开时看到的都是自己那一刻的快照,互不干扰。这并非魔法,而是 MySQL InnoDB 引擎中 MVCC(多版本并发控制)在 quietly work:一台无需你点开、不用你设置、却始终为你精准回溯数据状态的“隐形时间机器”。

一、快照、版本链与 ReadView:MVCC 的三驾马车

InnoDB 从不只存一行数据的一个版本。每行记录自带两个隐藏字段:trx_id(最后修改该行的事务 ID)和 roll_ptr(指向 undo 日志中前一版本的指针),由此串成一条“版本链”。当事务开启,InnoDB 并不复制整库,而是生成一个轻量级的 ReadView——相当于为这次查询配了一副“专属眼镜”。它记录三项关键信息:当前活跃事务 ID 列表、最小未提交事务 ID(min_trx_id)、最大已分配事务 ID(max_trx_id)。后续所有普通 SELECT 都依据这套规则判断:“这个版本,我能不能看见?”

判定逻辑极简:仅当某行版本的 trx_id 小于 min_trx_id,或等于当前事务 ID,或属于已提交且不在活跃列表中的事务,才对本次查询可见。

二、RR 与 RC:同一句 SQL,为何结果“变脸”?

核心差异,就在 ReadView 的生成时机。

✅ RC(读已提交):每次 SELECT 都新建 ReadView;

✅ RR(可重复读):仅事务首次 SELECT 时创建 ReadView,后续复用。

来个小实验(建议在空库执行):

CREATE TABLE t (id INT PRIMARY KEY, v INT);
INSERT INTO t VALUES (1, 10);
-- 事务 A(RR):START TRANSACTION; SELECT * FROM t; → 返回 (1,10)
-- 事务 B(RC):START TRANSACTION; UPDATE t SET v=20 WHERE id=1; COMMIT;
-- 事务 A 再次 SELECT * FROM t; → 仍返回 (1,10)(RR 复用快照)
-- 事务 C(RC)此时 SELECT → 立刻看到 (1,20)(新建 ReadView)
Enter fullscreen mode Exit fullscreen mode

RR 的“可重复读”,不是数据没变,而是你始终透过同一副眼镜看世界——时间被定格在第一次查询那一刻。

三、幻读:MVCC 解决了吗?答案是——一半。

普通 SELECT(即快照读)在 RR 级别下确实规避了幻读:新插入的行若 trx_id 大于 ReadView 的 max_trx_id,或属于未提交事务,一律不可见。但一旦触发 当前读(如 SELECT ... FOR UPDATEUPDATEINSERT ... SELECT),InnoDB 会绕过 MVCC,直接加锁并访问最新数据——此时若另一事务插入了满足条件的新行,幻读便真实发生。因此,RR 的“防幻读”本质是 MVCC + 间隙锁(Gap Lock)双保险:MVCC 负责让你“看不见”,间隙锁负责让别人“插不进”。

四、生产陷阱:Undo 日志爆满、长事务拖垮性能

MVCC 依赖 undo 日志保存历史版本。若长期不清理,undo 表空间持续膨胀,甚至触发 innodb_undo_log_truncated 告警。更危险的是长事务:它会让 ReadView 持有极老的 min_trx_id,导致大量旧版本无法回收,undo 日志堆积如山,最终拖慢整个实例。

三个立刻见效的优化动作:

  1. 定期查看 SHOW ENGINE INNODB STATUS,重点关注 History list length(历史链长度),超 10 万需警惕;
  2. 设置 max_execution_time = 30000(单位毫秒),自动中断超时查询;
  3. 执行 SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) > 300,及时 KILL 运行超 5 分钟的事务。

五、总结:MVCC 不是银弹,而是一场精密协奏

它不解决写-写冲突(仍需行锁),不替代应用层的数据校验,更不是 MyISAM 那种“无事务”的代名词。它的强大,源于与 InnoDB 存储引擎、隔离级别策略、锁机制的深度咬合。理解 MVCC,不只是记住 trx_id 规则,更是看清:每一次“看似无锁”的读,背后都有时间戳的精准裁决;每一次“理所当然”的一致性,都依赖 undo 日志的沉默承重。

真正的数据库高手,从不迷信机制——而是在读懂设计边界之后,让工具,真正为人所用。

Top comments (0)