S锁与S锁兼容,S锁与X锁冲突,X锁与X锁冲突;意向锁IS/IX用于声明行锁意图,IX与表级X/S锁均冲突,间隙锁仅与插入意向锁互斥。

共享锁(S锁)和排他锁(X锁)的兼容性怎么判断
直接看结论:S 与 S 兼容,S 与 X 冲突,X 与 X 冲突。这是所有行级锁冲突的基础逻辑。
实际中你遇到的阻塞,90%都源于这个规则:比如事务 A 持有某行的 X 锁(执行了 UPDATE 或 SELECT ... FOR UPDATE),事务 B 此时想对该行加 S 锁(SELECT ... LOCK IN SHARE MODE)或再加一个 X 锁,都会被挂起等待。
注意:锁的“持有者”和“请求者”是按事务粒度计算的,不是语句粒度;同一个事务内重复加同类型锁不会冲突。
意向锁(IS/IX)为什么必须存在,它和表锁怎么互斥
意向锁是 InnoDB 实现高效行锁的前提——它不锁数据,只做“声明”:IS 表示“我这个事务打算在本表某些行上加 S 锁”,IX 表示“我打算加 X 锁”。它们本身不互斥(IS 和 IX 兼容),但会拦住真正的表级锁。
关键兼容关系如下(仅列出常见组合):
-
IS与S(表级读锁)兼容,但与X(表级写锁)冲突 -
IX与S冲突,也与X冲突 -
IX与IX兼容,IS与IS兼容
换句话说:只要表上有任意事务加了 IX,其他事务就无法对这张表执行 ALTER TABLE 或 LOCK TABLES ... WRITE,因为这些操作需要先获得表级 X 锁。
为什么 SELECT ... FOR UPDATE 有时锁整张表
这不是锁机制变坏了,而是 SQL 没走索引导致的退化行为。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
InnoDB 的行锁永远加在索引上。如果 WHERE 条件没命中任何索引(包括主键、唯一索引、普通索引),InnoDB 就无法定位具体行,只能遍历聚簇索引——此时会对**所有已存在的聚簇索引记录**加 X 锁,等效于锁全表(注意:不是锁表结构,是锁所有数据行)。
常见诱因包括:
- 查询字段未建索引,且无可用覆盖索引
- 对
TEXT/BLOB字段做=查询(无法用索引) - 隐式类型转换,如
WHERE user_id = '123'(user_id 是 INT) - 使用函数或表达式,如
WHERE YEAR(create_time) = 2025
这种场景下,SHOW ENGINE INNODB STATUS 的 TRANSACTIONS 部分会显示大量 RECORD LOCKS,且 space id 和 page number 覆盖范围极广。
间隙锁(Gap Lock)和临键锁(Next-Key Lock)到底兼容谁
间隙锁只和插入意向锁(Insert Intention Lock)互斥,和其他所有锁类型都兼容——包括别的间隙锁、记录锁、甚至另一个间隙锁。
这意味着:两个事务同时执行 SELECT ... WHERE id BETWEEN 10 AND 20 FOR UPDATE(非唯一索引),它们可以各自拿到 (10,20) 这个间隙的 Gap Lock,互不阻塞;但如果第三个事务想执行 INSERT INTO t VALUES (15),就会被两个间隙锁同时拦住,因为它需要申请 (10,20) 上的 Insert Intention Lock,而该锁与间隙锁冲突。
临键锁 = 记录锁 + 左侧间隙锁,所以它的兼容性继承二者:对目标记录本身是 X 级别(排斥一切),对左侧间隙部分则遵循间隙锁规则(只排斥插入意向锁)。
唯一例外:当查询条件命中唯一索引且为等值匹配(如 WHERE id = 100,id 是主键),InnoDB 会退化为纯记录锁,不加任何间隙部分——这是避免过度锁的关键优化点,也是线上调优时最常被忽略的细节。

















