主键非自增ID易致全表锁:UUID等无序主键破坏聚簇索引有序性,WHERE未走索引时UPDATE/DELETE触发全表扫描并加锁;复合主键字段顺序不当、外键缺索引、间隙锁误伤、隔离级别过高均加剧锁问题。

主键不是自增ID时,UPDATE/DELETE容易锁全表
非自增主键(比如UUID、字符串或业务编码)会让InnoDB无法按物理顺序组织数据,导致范围查询、ORDER BY或隐式扫描时无法利用聚簇索引的有序性。一旦WHERE条件没走索引,或索引选择性差,UPDATE和DELETE就会触发全表扫描——InnoDB会为每行加记录锁,甚至升级为间隙锁,锁范围远超预期。
- 用
EXPLAIN检查执行计划:重点关注type是否为ALL或index,如果是,说明没走有效索引 - 避免在主键字段上做函数操作,例如
WHERE UPPER(id) = 'ABC'会失效索引 - 如果必须用UUID,至少搭配一个自增列作为聚簇索引(通过
ALTER TABLE ... ORDER BY seq_id重建表,并设seq_id为主键)
复合主键中字段顺序错乱引发锁扩大
当用多列组成主键(如(tenant_id, order_id)),但业务查询总是只查order_id,而没带上tenant_id,InnoDB就无法使用最左前缀匹配,可能退化为全索引扫描。此时锁住的不只是目标行,而是整个索引分支上的所有记录。
- 确认高频查询的过滤条件,把区分度高且常单独使用的字段放在复合主键最左侧
- 若无法调整主键顺序,给高频单字段查询单独建二级索引,确保
WHERE order_id = ?能走索引 - 注意二级索引回表带来的额外锁开销:如果
SELECT *+FOR UPDATE,InnoDB会先锁二级索引项,再锁聚簇索引行
主键值分布稀疏导致间隙锁误伤
自增ID中间有大量空缺(比如频繁DELETE后未重用ID,或批量分配ID留白),InnoDB在RR隔离级别下会对“不存在的间隙”加锁。例如id IN (100, 200, 300)更新时,id=150这个间隙可能被锁住,阻塞其他事务插入id=150附近的值。
- 不要依赖
AUTO_INCREMENT值连续性,业务逻辑别假设ID是密集递增的 - 若业务允许,将隔离级别降为
READ COMMITTED(SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED),它不使用间隙锁 - 批量插入时用
INSERT ... ON DUPLICATE KEY UPDATE替代先查后插,减少间隙锁触发场景
外键约束让锁从行级升级到索引级别
主键被其他表设为外键引用时,MySQL会在父表UPDATE/DELETE主键值时,自动对子表相关索引加S锁(共享锁)用于校验约束。如果子表缺少对应外键字段的索引,InnoDB会扫描全表来检查参照完整性——这直接导致锁范围爆炸。
- 每个外键字段,必须在子表上单独建索引(不只是联合索引里包含它)
- 检查
SHOW CREATE TABLE child_table,确认外键列是否有独立索引;没有就加:ALTER TABLE child_table ADD INDEX idx_fk_order_id (order_id) - 上线前跑一次
SELECT COUNT(*) FROM child_table WHERE fk_col = ?,验证该查询是否能命中索引
Waiting for table metadata lock或Lock wait timeout exceeded,此时再改主键代价极高。与其事后补救,不如在建表评审阶段就盯住主键字段类型、顺序、是否带外键依赖这三个点。


















