InnoDB实现MVCC时如何规避一致读原子性问题与读写冲突?
InnoDB MVCC 并发读写冲突规避机制解答
你提到的并发场景在InnoDB实际实现中并不会发生,核心是InnoDB通过内存页闩锁(latch)、严格的修改顺序设计两层机制规避了这类问题:
第一层保护是页级闩锁,这是专门用来保护内存中数据页结构的轻量锁,和事务级别的表锁、行锁完全不同,持有时间仅持续到页内操作完成,开销极低:
- 读操作读取tuple的完整流程(包括读取trx_id、undo指针、所有字段值、判断可见性)全程持有对应页的共享S闩锁,持有期间其他线程无法拿到该页的排他X闩锁修改页内数据
- 写操作修改tuple的完整流程(写入undo日志、更新undo指针、修改字段值、更新trx_id)全程持有对应页的排他X闩锁,持有期间其他线程无法拿到该页的S闩锁读取页内数据
这就从根本上避免了你描述的「读操作读了trx_id之后,写操作中途插入修改数据」的情况,要么读操作先完成全量读取返回正确值,要么写操作先完成全量修改,读操作后续读取时根据trx_id判断可见性,未提交就顺着undo指针读取历史版本,绝不会读到半修改的脏数据。
第二层是修改顺序的兜底设计,即便不考虑闩锁,InnoDB修改tuple的顺序也严格遵循
写入undo -> 更新undo指针 -> 修改字段值 -> 更新trx_id的顺序,trx_id是最后修改的元数据。读操作优先读取trx_id,如果trx_id还是旧值,说明所有修改都未生效,读到的所有字段都是旧值;如果trx_id已经更新为新事务ID,说明所有字段修改都已经完成,此时再判断可见性,也不会出现半新半旧的异常结果。
另外你提到的非原子字节拷贝的问题也被闩锁完全覆盖,不管修改的字段长度是多少,读写操作在闩锁的保护下是串行执行的,不会出现读一半被修改的情况。
内容的提问来源于stack exchange,提问作者Zeng
相关产品推荐
相关产品推荐

