UPDATE必须走当前读,因为它需读取最新已提交版本并加锁,避免基于过期快照覆盖他人修改;不依赖ReadView,ReadView仅对普通SELECT生效。

UPDATE 为什么必须走当前读,不依赖 ReadView
UPDATE 不会用 ReadView 判断可见性,它根本就不是快照读。执行 UPDATE 时,InnoDB 必须拿到记录的最新版本(即“当前读”),否则可能基于一个过期快照做修改,把别人刚提交的变更覆盖掉。
常见错误现象:在可重复读(RR)下,事务 A 先 SELECT 查到某行值为 100,接着事务 B 提交了 UPDATE ... SET val = 200,事务 A 再执行 UPDATE ... SET val = val + 1,结果是基于 100 算出 101 —— 这不是 MVCC 的 bug,而是因为第一次 UPDATE 触发的是当前读,它会忽略事务 A 自己的 ReadView,直接去聚簇索引上查最新版本并加锁。
-
UPDATE、DELETE、SELECT FOR UPDATE都强制走当前读,跳过 MVCC 版本链遍历 - 即使事务已生成 ReadView,当前读也完全不参考它;ReadView 只对普通
SELECT生效 - 当前读查到最新版本后,会在该版本基础上生成新版本,并更新
DB_TRX_ID和DB_ROLL_PTR
UPDATE 执行前的“读取”阶段到底读了什么
虽然 UPDATE 整体不走 MVCC,但它在真正修改前,仍需定位目标行——这个定位过程涉及隐式读取,而读取策略取决于是否命中索引、隔离级别和语句写法。
使用场景举例:执行 UPDATE t SET c = c + 1 WHERE id = 5,InnoDB 需先找到 id = 5 对应的记录。如果 id 是主键或唯一索引,它直接定位到聚簇索引页上的最新记录;如果是普通索引,则先查二级索引,再回表,回表那一步就是当前读。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 无论隔离级别是
READ COMMITTED还是REPEATABLE READ,当前读都只读最新已提交版本(除非有未提交的本事务修改) - 若目标行被其他事务加锁(如
SELECT FOR UPDATE占着),UPDATE会阻塞等待,而不是退而求其次读历史版本 - 没有索引时全表扫描,每一行都触发一次当前读,性能代价高,且容易锁住大量无关行
为什么不能让 UPDATE 基于 ReadView 做“乐观更新”
技术上可以设计,但 InnoDB 没这么做——因为语义风险太大。MVCC 的 ReadView 是为“读一致性”服务的,而写操作必须保证“写正确性”,两者目标冲突。
典型问题:事务 A 在 RR 下第一次 SELECT 得到 ReadView,之后事务 B 插入新行并提交,事务 C 修改某行并提交。事务 A 若基于旧 ReadView 去 UPDATE,可能:
- 找不到 B 插入的新行(幻读场景),导致漏更新
- 读到 C 修改前的老版本(比如余额 100),+1 后写回 101,覆盖 C 提交的 200
- InnoDB 选择用加锁 + 当前读兜底,确保
UPDATE总是基于真实最新状态 -
READ COMMITTED下每次UPDATE前都会重新获取最新 ReadView?不,它依然走当前读,只是当前读看到的“最新”可能比 RR 下更早(因 RC 不复用 ReadView,但 UPDATE 本身不看 ReadView) - 真要实现无锁更新,得靠应用层用
WHERE条件校验(如UPDATE t SET c = 10 WHERE id = 5 AND c = 9),失败则重试
容易被忽略的关键点:UPDATE 的可见性判断其实发生在两个地方
很多人以为“可见性”只在读的时候发生,但 UPDATE 的可见性判断是分阶段的:第一阶段是定位行(当前读,查最新),第二阶段是生成新版本(写入时检查本事务能否覆盖)。
第二阶段的判断逻辑其实和 ReadView 类似,但只用到两个字段:creator_trx_id 和 DB_TRX_ID。当 InnoDB 准备把新版本写入聚簇索引时,会确认:这行是不是本事务自己刚改过的?如果是,允许覆盖;如果不是,就按正常流程追加 undo log 并更新 DB_ROLL_PTR。
- 这个判断不查
m_ids或min_trx_id,因为它不关心其他事务是否活跃,只关心“能不能写” - 所以长事务中反复
UPDATE同一行,不会越改越慢——undo log 链长度只取决于并发修改它的其他事务数,而非本事务的更新次数 - 但若该行正被其他事务加锁,
UPDATE就卡在第一阶段,根本进不到第二阶段

















