mysql事务通过锁机制实现隔离性:rc级别当前读加记录锁,rr级别加next-key锁防幻读,serializable强制所有读加共享锁;锁范围取决于语句、索引和执行计划;死锁由innodb自动检测并回滚代价小的事务。

MySQL 的锁机制和事务是面试高频考点,核心在于理解“事务如何通过锁来保证隔离性”,而不是孤立背概念。下面从实际场景出发,拆解关键逻辑。
事务的隔离级别决定了要加什么锁
隔离级别不是抽象设置,它直接控制 InnoDB 在读写时是否加锁、加什么锁:
- READ UNCOMMITTED:几乎不加读锁,可能读到未提交数据(脏读),实际很少用;
- READ COMMITTED:快照读(普通 SELECT)不加锁,但当前读(如 SELECT ... FOR UPDATE、UPDATE、DELETE)只对匹配到的行加记录锁,且每次查询都重新生成 Read View;
- REPEATABLE READ(MySQL 默认):快照读基于事务启动时的 Read View,不加锁;当前读加的是记录锁 + 间隙锁(合称 next-key lock),防止幻读;
- SERIALIZABLE:所有读操作都隐式转化为 SELECT ... LOCK IN SHARE MODE,强制加共享锁,彻底串行化。
锁类型得看语句+索引+执行计划
同样一条 UPDATE,锁的范围可能差十倍:
本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。
- 有主键或唯一索引且等值查询 → 只锁命中行(记录锁);
- 有普通索引等值查询 → 锁该索引记录 + 对应的主键记录(二级索引会回表,两处都要锁);
- 无索引或范围查询(如 WHERE age > 25)→ 可能触发间隙锁或临键锁,锁住不存在的“空隙”,避免其他事务插入新行造成幻读;
- SELECT ... FOR UPDATE 在 RR 级别下,即使没走索引也会锁全表(实际是锁所有索引段,效果类似表锁)。
死锁不是偶然,是资源争抢的必然结果
InnoDB 检测到死锁后,会主动回滚其中事务代价更小的那个(按 undo log 大小估算)。常见诱因:
- 多个事务以不同顺序更新同一组行(如事务 A 先更新 id=1 再更新 id=2,事务 B 反过来);
- 间隙锁重叠:两个事务同时想在同一个间隙插入数据,各自持有间隙锁又申请对方持有的锁;
- 没有索引导致锁升级:UPDATE 不走索引 → 锁全表 → 极易阻塞甚至死锁。
排查用 SHOW ENGINE INNODB STATUS\G,重点关注 LATEST DETECTED DEADLOCK 部分。
可重复读 ≠ 数据绝对不变
这是高频误区。RR 级别下:
- 自己事务内,多次快照读看到的数据一致(基于初始 Read View);
- 但其他事务的修改一旦提交,本事务的当前读(如 SELECT ... FOR UPDATE)会立即看到最新数据,并加对应锁;
- UPDATE/DELETE 的 where 条件始终基于最新版本判断(即“当前读语义”),所以可能出现“幻更新”——两次 SELECT 结果一样,但 UPDATE 影响了新插入的行。









