mysql什么时候会加行锁_mysql执行原理说明

来源:这里教程网 时间:2026-02-28 20:49:34 作者:

MySQL 只在 InnoDB 引擎 + WHERE 条件命中索引时,才真正加行锁;其他情况基本等于“锁整张表”。

怎么判断你的 UPDATE 或 SELECT FOR UPDATE 真的只锁了 1 行?

别信语义,看执行计划。行锁不是“写了 WHERE 就有”,而是优化器决定走不走索引:

EXPLAIN SELECT * FROM users WHERE id = 1001 FOR UPDATE;
→ 如果
type
const
ref
,且
key
显示用了主键或唯一索引,才算稳稳锁 1 行
EXPLAIN SELECT * FROM users WHERE name = 'Alice' FOR UPDATE;
→ 若
name
没索引,
type
ALL
,那就不是“没加锁”,而是对**所有扫描到的记录都加 X 锁**,外加间隙锁,效果等同于锁表
隐式转换也致命:
UPDATE users SET status=1 WHERE phone = 13800138000;
phone
VARCHAR
)→ 触发全表扫描加锁,不是 bug,是规则

为什么明明写了 WHERE 主键,还是被卡住?

因为行锁 ≠ 立刻释放。InnoDB 遵循两阶段锁协议:锁从需要时加上,直到事务

COMMIT
ROLLBACK
才释放。

事务里先调了外部 API 耗时 3 秒,再执行
SELECT * FROM order WHERE id = 123 FOR UPDATE;
→ 这 3 秒内,锁已持有,别人全得等
SELECT ... FOR UPDATE
后不做任何更新、直接
COMMIT
?锁照样等到提交那一刻才松手
查锁状态用:
SELECT * FROM performance_schema.data_locks;
LOCK_DATA
LOCK_MODE
,别靠猜

SELECT 不加锁?那是默认 MVCC 快照读 —— 但一加 FOR UPDATE 就变天

普通

SELECT
在 RR/RC 下完全不加锁,靠 MVCC 返回快照;可一旦显式加锁提示,行为彻底改变:

SELECT * FROM stock WHERE sku_id = 'ABC123' FOR UPDATE;
→ 加 X 锁,阻塞其他事务的
FOR UPDATE
UPDATE
SELECT * FROM stock WHERE price > 100 FOR UPDATE;
→ 范围查询,在 RR 级别下自动升级为 Next-Key Lock(Record Lock + Gap Lock),连
INSERT price = 150
都可能被堵住
想禁用间隙锁?临时切隔离级别:
SET SESSION transaction_isolation = 'READ-COMMITTED';
,但得接受幻读风险

最常被忽略的一点:没有主键或唯一索引的 InnoDB 表,InnoDB 会用隐藏的

row_id
构建聚簇索引,此时锁行为极难预测——看着只改 1 行,实际可能锁住一大片。上线前务必确认表有主键,且关键查询字段建了索引。

相关推荐