5.4.3 InnoDB ⾏锁 InnoDB 的行锁是 MySQL 实现高并发能力的核心也是 InnoDB 能够成为默认存储引擎的重要原因。与 MyISAM 的表级锁不同InnoDB 的行锁粒度更细允许多个事务同时修改同一张表的不同行从而显著提升数据库的并发性能。 InnoDB 行锁的基石索引InnoDB 的行锁有一个核心特性锁是加在索引记录上的。命中索引如果查询条件能有效利用索引InnoDB 就只锁定命中的那几行并发性最高。未命中索引如果查询条件没有可用的索引InnoDB 只能进行全表扫描这会锁住表中的每一行效果等同于表锁会严重阻塞并发。因此为查询条件建立合理的索引是使用行锁、保证高并发性能的前提。️ InnoDB 行锁的两大基本类型和表锁一样行锁也分为两种基本模式。锁类型英文俗称作用兼容性共享锁Shared Lock (S Lock)读锁允许事务读取一行数据但禁止修改该行。多个事务的S锁可以同时加在同一行上互不阻塞。排他锁Exclusive Lock (X Lock)写锁允许事务读取和修改数据。一个事务加了X锁其他事务不能再对该行加任何锁S或X。如何触发共享锁 (S)手动添加使用SELECT ... FOR SHARE(MySQL 8.0)或SELECT ... LOCK IN SHARE MODE。排他锁 (X)UPDATE、DELETE、INSERT语句会自动添加。手动添加使用SELECT ... FOR UPDATE。这两种锁是行锁的“权限”基础在实际加锁时会具体表现为下面三种算法。⚙️ 行锁的三种实现算法这是 InnoDB 行锁最精细的部分决定了锁住的具体范围。1. 记录锁 (Record Lock)描述锁定单个索引记录也就是锁定一行。它是最基本的行锁粒度最细并发影响最小。场景在唯一索引包括主键的等值查询中并且记录存在时会使用记录锁。-- 假设 id 是主键BEGIN;-- 这条语句会在 id1 的索引记录上加一个排他的记录锁SELECT*FROMusersWHEREid1FORUPDATE;COMMIT;2. 间隙锁 (Gap Lock)描述锁定一个索引范围但不包括记录本身。它的目的是防止其他事务在这个间隙中插入新记录。场景只在REPEATABLE READ及更高的隔离级别下生效。在READ COMMITTED级别下不会使用。-- 假设 users 表有 id 值为 1, 5, 10BEGIN;-- 这条语句可能会在 (1, 5) 和 (5, 10) 等间隙上加上间隙锁SELECT*FROMusersWHEREidBETWEEN3AND7FORUPDATE;COMMIT;此时其他事务将无法插入id为 4 或 6 的新记录直到该事务结束。3. 临键锁 (Next-Key Lock)描述记录锁 间隙锁的组合。它锁定一个左开右闭的索引区间。例如对于id值为1, 5, 10临键锁会锁定(1, 5]这个范围既防止修改id5的记录也防止在(1,5)之间插入新记录。场景这是 InnoDB 在REPEATABLE READ隔离级别下的默认行锁算法也是其解决幻读问题的核心机制。临键锁的退化规则RR级别下唯一索引等值查询且记录存在临键锁会退化为记录锁只锁该行。唯一索引等值查询且记录不存在临键锁会退化为间隙锁只锁间隙。非唯一索引等值查询无论记录是否存在都会使用临键锁。范围查询会对扫描到的所有索引记录及其间隙加临键锁。4. 插入意向锁 (Insert Intention Lock)这是一种特殊的间隙锁由INSERT操作在等待间隙锁时获取。它表明事务打算在某个间隙中插入新数据多个事务可以在同一个间隙的不同位置插入只要插入的位置不冲突。 辅助工具意向锁 (Intention Lock)意向锁是 InnoDB 自动添加的表级锁用于协调行锁和表锁之间的关系。意向共享锁 (IS)事务准备给某些行加S 锁前先给表加 IS 锁。意向排他锁 (IX)事务准备给某些行加X 锁前先给表加 IX 锁。它们的作用就像一个“门卫”。当另一个事务想给整个表加锁如LOCK TABLES ... WRITE时只需检查表级是否有 IX 或 IS 锁就能快速判断是否有行锁存在而无需遍历所有行去检查。 锁的兼容性矩阵下表总结了各种锁之间的兼容性。锁类型意向共享锁 (IS)意向排他锁 (IX)共享锁 (S)排他锁 (X)意向共享锁 (IS)✅ 兼容✅ 兼容✅ 兼容❌ 冲突意向排他锁 (IX)✅ 兼容✅ 兼容❌ 冲突❌ 冲突共享锁 (S)✅ 兼容❌ 冲突✅ 兼容❌ 冲突排他锁 (X)❌ 冲突❌ 冲突❌ 冲突❌ 冲突兼容表示两种锁可以同时存在冲突则表示后申请的锁必须等待前一个锁释放。 行锁引发的问题死锁 (Deadlock)两个或多个事务相互等待对方持有的锁导致所有事务都无法继续。InnoDB 的处理自动检测死锁并回滚“代价较小”的事务。如何避免固定访问顺序所有事务都按相同顺序如主键升序访问表和行。缩短事务减少锁持有时间降低冲突窗口。使用READ COMMITTED隔离级别该级别下没有间隙锁能大幅降低死锁概率。应用层重试捕获死锁异常如Error 1213并重试整个事务。锁等待超时 (Lock Wait Timeout)事务等待锁的时间超过了innodb_lock_wait_timeout默认50秒设置的阈值事务会自动回滚。️ 如何监控和分析行锁SHOW ENGINE INNODB STATUS查看 InnoDB 的整体状态其中LATEST DETECTED DEADLOCK部分记录了最近一次死锁的详细信息。performance_schema.data_locks表 (MySQL 8.0)直接查询当前数据库中的所有锁信息是诊断锁问题最直接的方法。performance_schema.data_lock_waits表 (MySQL 8.0)显示当前的锁等待关系即哪个事务在等待哪个事务持有的锁。 总结InnoDB 的行锁是一个精密的系统理解其核心要点是写出高性能、高并发 SQL 的基础锁是加在索引上的这是行锁生效的前提无索引则退化为表锁。两大基本类型共享锁S读锁和排他锁X写锁。三种实现算法记录锁锁行、间隙锁锁范围、临键锁锁行范围RR默认。意向锁是门卫表级意向锁IS/IX用于快速判断表锁与行锁的冲突。RR 级别下临键锁防幻读这是 InnoDB 默认隔离级别REPEATABLE READ能保证数据一致性的关键。死锁不可怕理解其成因特别是间隙锁通过规范编码和重试机制来应对。