MySQL行锁是基于什么实现的?MVCC是什么?

2025年 阅读约 10 分钟 面试指南 · MySQL面试

深入解析MySQL InnoDB行锁实现机制、MVCC多版本并发控制原理,以及RR隔离级别下如何解决幻读。

一句话总结

InnoDB行锁基于索引实现,未命中索引会退化为表锁。MVCC通过Undo Log版本链 + Read View实现非阻塞读,RR级别下快照读通过MVCC避免幻读,当前读通过Next-Key Lock解决。

初级理解

行锁实现机制:

InnoDB的行锁是基于索引实现的,而非基于物理行。 如果SQL没有走索引,InnoDB将退化为表锁。

三种行锁类型:

• Record Lock(记录锁):锁定索引上的单条记录 • Gap Lock(间隙锁):锁定索引记录之间的间隙,防止其他事务插入新记录 • Next-Key Lock(临键锁):Record Lock + Gap Lock的组合,锁定记录及其前面的间隙
一句话总结:行锁基于索引,没索引变表锁;Next-Key Lock = 记录锁 + 间隙锁。

中级深入

MVCC原理:

MVCC(多版本并发控制)通过保存数据的历史版本来实现非阻塞读。 核心组件: • Undo Log版本链:每次修改记录都会生成Undo Log,通过roll_pointer串联成版本链 • Read View(读视图):包含当前活跃事务ID列表,用于判断版本可见性

Read View生成时机:

• RR(可重复读):在事务第一次执行SELECT时生成Read View,后续查询复用该视图 • RC(读已提交):每次执行SELECT都会重新生成Read View
注意:MVCC只在RC和RR隔离级别下生效,RU和Serializable不使用MVCC。

高级拓展

RR级别下如何解决幻读:

• 快照读(普通SELECT):通过MVCC的Read View机制,始终读取事务开始时的数据快照 • 当前读(SELECT...FOR UPDATE / UPDATE / DELETE):通过Next-Key Lock锁住索引记录及间隙

InnoDB使用表锁的情况:

• SQL未命中索引:执行全表扫描时,InnoDB会锁住所有记录 • DDL操作:ALTER TABLE、CREATE INDEX等 • 显式加表锁:LOCK TABLES ... • 外键检查:某些复杂的外键级联操作
面试加分项:能说出MVCC的两个核心组件、Read View在RR和RC下的区别、Next-Key Lock解决幻读的原理,说明你对InnoDB事务机制有深入理解。

实战场景

场景:行锁退化为表锁

-- 问题SQL:status列没有索引,行锁退化为表锁 BEGIN; UPDATE users SET status = 'VIP' WHERE status = 'ACTIVE'; -- 全表锁! -- 解决:给status列加索引 ALTER TABLE users ADD INDEX idx_status(status); -- 现在只锁status='ACTIVE'的行

场景:MVCC读取一致性视图

-- 事务A BEGIN; SELECT * FROM users WHERE id = 1; -- 生成Read View,读到name='张三' -- 事务B BEGIN; UPDATE users SET name = '李四' WHERE id = 1; COMMIT; -- 事务A再次查询(RR级别) SELECT * FROM users WHERE id = 1; -- 仍然读到name='张三'(MVCC快照读)

面试模拟

Q:为什么InnoDB行锁要基于索引?

A:因为InnoDB的行锁是加在索引记录上的,不是加在物理行上的。如果SQL没有走索引,InnoDB就需要扫描整张表来匹配条件,相当于锁住了所有行,退化为表锁。所以确保UPDATE/DELETE的WHERE条件命中索引是避免锁升级的关键。

Q:RR和RC级别下MVCC有什么区别?

A:主要区别在于Read View的生成时机:RR级别在事务第一次SELECT时生成Read View,后续复用,保证同一事务内多次读取结果一致;RC级别每次SELECT都重新生成Read View,能读到其他事务已提交的最新数据。