innodb通过mvcc机制实现select不加锁却读取一致快照:事务启动时创建read view,结合db_trx_id和db_roll_ptr从undo log回溯可见版本,repeatable read复用首次read view,read committed每次新建;update/delete等操作则绕过快照直接当前读并加锁。

为什么 SELECT 不加锁却能读到“一致快照”?
InnoDB 的 SELECT(默认隔离级别 REPEATABLE READ)不阻塞写,也不被写阻塞,靠的是每行数据背后维护的隐藏字段:DB_TRX_ID(最近修改事务ID)和 DB_ROLL_PTR(指向 undo log 中前一版本的指针)。执行查询时,InnoDB 不看最新行,而是根据当前事务的 TRX_ID 和活跃事务链表(trx_list),从 undo log 链上“向上找”满足可见性规则的第一个版本——这个过程不加锁、不等待,自然非阻塞。
READ COMMITTED 和 REPEATABLE READ 的快照生成时机差异
两种隔离级别都用 MVCC,但“快照”怎么取,差别很大:
-
READ COMMITTED:每次SELECT都重新获取一次最新read view,所以能看到其他事务已提交的新数据 -
REPEATABLE READ:事务第一次SELECT时创建read view,之后所有查询复用它,保证“可重复读”
这意味着:同一个事务中,REPEATABLE READ 下两次 SELECT 可能返回不同结果(如果中间有 UPDATE + COMMIT),但普通 SELECT 仍不加锁——因为读的是快照,不是当前行。
哪些操作会破坏 MVCC 的非阻塞特性?
MVCC 只对纯读有效。一旦涉及写或显式加锁,就绕过快照逻辑:
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
-
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE:直接对当前最新行加锁,阻塞其他写/读 -
INSERT/UPDATE/DELETE:必须操作最新版本,且要加行锁(record lock)或间隙锁(gap lock) - 唯一键冲突检测、外键检查等内部操作,也会访问最新行并可能触发锁等待
注意:UPDATE t SET x=1 WHERE id=5 看似是“读+写”,但 InnoDB 先按可见性找到匹配的快照行,再定位到对应最新物理记录去加锁修改——读快照不锁,改最新行才锁。
undo log 太大导致 purge 延迟时,MVCC 会变慢甚至卡住
MVCC 依赖 undo log 保留旧版本。如果长事务不提交,或者 innodb_max_purge_lag 设置不合理,undo log 积压,会导致:
- 新事务的
read view中活跃事务列表过长,判断行可见性变慢 - 历史版本链过长,顺着
DB_ROLL_PTR回溯耗时增加 - 极端情况下,
purge thread跟不上,undo 表空间膨胀,甚至触发Lock wait timeout exceeded(实际是 purge 卡住导致锁无法释放)
这不是 MVCC 设计缺陷,而是使用边界:MVCC 的轻量读,建立在后台 purge 能及时清理旧版本的基础上。监控 INFORMATION_SCHEMA.INNODB_METRICS 中的 purge_trx_count 和 purge_undo_log_bytes 很关键。










