行锁只锁匹配行且依赖索引,表锁锁整张表;InnoDB行锁在无索引时退化为全表扫描加锁,效果近似表锁;表锁多见于DDL或显式LOCK TABLES,死锁仅发生于行锁场景。

SELECT ... FOR UPDATE 和 LOCK TABLES ... WRITE 看似都是“加锁”,但实际效果天差地别——行锁只拦住冲突的那几行,表锁直接让整张表动弹不得。业务代码里用错一个,高并发下就可能从“慢一点”变成“全表卡死”。
行锁只在走索引时才真正生效
InnoDB 的行锁不是按“数据行”物理位置锁的,而是依赖索引定位。只要 WHERE 条件没命中主键或唯一索引,InnoDB 就会退化为全表扫描 + 行锁升级,实际效果接近表锁。
-
UPDATE users SET name = 'Alice' WHERE id = 100;→id是主键:只锁这一行 -
UPDATE users SET name = 'Alice' WHERE name LIKE '%li%';→name无索引:全表扫描,每行都尝试加锁,其他事务更新任意行都会被阻塞 - 即使有索引,
OR、函数包裹(如UPPER(name))、隐式类型转换(WHERE id = '100'但id是 INT)也会导致索引失效
表锁几乎只出现在 DDL 或显式 LOCK TABLES 场景
InnoDB 默认不使用表锁处理 DML(INSERT/UPDATE/DELETE),只有两类情况会真正触发:
- 执行
ALTER TABLE、TRUNCATE TABLE、DROP TABLE等 DDL 语句时,自动加元数据锁(MDL)+ 表级排他锁 - 手动执行
LOCK TABLES users WRITE;—— 这种写法在业务逻辑中应视为危险操作,它会阻塞所有其他连接对该表的读写,包括SELECT
MyISAM 引擎除外,它对所有 DML 都默认用表锁,但生产环境基本不用 MyISAM。
共享锁(S-lock)和排他锁(X-lock)的行为差异
行锁分两种底层模式,直接影响并发行为:
-
SELECT ... LOCK IN SHARE MODE加的是共享锁:多个事务可同时持有,但会阻塞其他事务的FOR UPDATE或写操作 -
SELECT ... FOR UPDATE加的是排他锁:当前事务修改前必须先获取,其他事务连SELECT ... LOCK IN SHARE MODE都会被等住 - 注意:
INSERT、UPDATE、DELETE在事务中会自动加 X-lock,无需显式写FOR UPDATE,但显式加能更早暴露锁冲突
死锁风险集中在行锁场景
表锁几乎不会死锁——因为锁粒度大,事务要么拿到全表,要么等;而行锁容易因加锁顺序不一致引发死锁:
- 事务 A 先锁
id=1,再试图锁id=2 - 事务 B 先锁
id=2,再试图锁id=1 - MySQL 检测到循环等待,会回滚其中一个事务(报错
Deadlock found when trying to get lock) - 避免方式:固定加锁顺序(如总按
id升序更新)、减少事务长度、避免在事务中做网络调用或用户输入等待
UPDATE 突然让整张订单表变慢——十有八九是索引失效导致行锁退化,或者开发顺手加了 LOCK TABLES 却忘了 UNLOCK TABLES。


















