SELECT ... FOR UPDATE 只在显式事务中生效,需禁用autocommit并用BEGIN开启事务;WHERE必须走索引否则升级表锁;UPDATE/DELETE自带行锁,无需先SELECT FOR UPDATE;多行操作须按固定顺序加锁防死锁。

SELECT ... FOR UPDATE 只在事务中生效
MySQL 的行级悲观锁不是靠独立的 LOCK 语句实现的,而是通过 SELECT ... FOR UPDATE 在可重复读(RR)或读已提交(RC)事务隔离级别下触发。它本质是给满足 WHERE 条件的索引记录加写锁(next-key lock 或 record lock),其他事务再尝试更新/删除这些行时会被阻塞。
常见错误现象:执行 SELECT ... FOR UPDATE 后没看到锁效果——大概率因为没开启事务,或者自动提交(autocommit=1)开着。MySQL 默认 autocommit=1,单条 SELECT ... FOR UPDATE 执行完就自动提交,锁立刻释放。
- 必须显式用
BEGIN或START TRANSACTION开启事务 - 执行
SELECT ... FOR UPDATE后,不COMMIT或ROLLBACK,锁一直持有 - 若 WHERE 条件未命中任何行,不会加任何锁(但会加 gap lock?取决于隔离级别和索引类型,RC 下通常不加 gap lock)
WHERE 必须走索引,否则升级为表锁
SELECT ... FOR UPDATE 要真正实现「行级」锁,WHERE 条件必须命中索引(主键、唯一索引、普通索引均可)。如果走全表扫描,InnoDB 会对所有扫描过的聚簇索引记录加锁——看起来像锁了整张表,尤其在大表上极易引发锁等待甚至死锁。
验证是否走索引:执行 EXPLAIN SELECT ... FOR UPDATE,看 type 是否为 const/ref/range,且 key 列非 NULL。
- 主键查询最安全:
SELECT * FROM orders WHERE id = 123 FOR UPDATE - 普通索引也有效:
SELECT * FROM users WHERE email = 'a@b.c' FOR UPDATE(前提是email有索引) - 模糊查询
LIKE '%abc'或无索引字段查询,基本等于自找表级阻塞
UPDATE 和 DELETE 语句自带隐式行锁,无需额外加 FOR UPDATE
很多人以为要先 SELECT ... FOR UPDATE 再 UPDATE,其实没必要。InnoDB 在执行 UPDATE 或 DELETE 时,会自动对将要修改/删除的行加写锁(record lock 或 next-key lock),效果等同于显式 FOR UPDATE,且更高效——少一次查询。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
典型误用场景:业务逻辑里先查余额,再判断是否足够,最后扣减。这种“查-判-改”三步不是原子的,中间可能被并发修改。正确做法是直接 UPDATE account SET balance = balance - 100 WHERE id = 123 AND balance >= 100,靠 WHERE 条件做原子校验。
- 想确保某行不被其他事务改,直接
UPDATE ... WHERE pk = ?就够了 - 需要读取旧值做复杂逻辑(比如日志记录、跨表校验),才需
SELECT ... FOR UPDATE显式加锁 -
INSERT ... ON DUPLICATE KEY UPDATE也会对冲突行加锁,注意唯一键冲突时的锁行为
死锁不是小概率事件,务必按固定顺序加锁
多个事务以不同顺序访问相同几行时,极容易触发死锁。例如事务 A 先锁 id=1 再锁 id=2,事务 B 反过来,InnoDB 检测到循环等待后会回滚其中一个事务,报错 Deadlock found when trying to get lock。
这不是配置能绕开的问题,得从代码设计入手:
- 所有涉及多行更新的逻辑,强制按主键升序(或其它确定性顺序)处理,比如先
SELECT ... ORDER BY id FOR UPDATE - 避免在事务里嵌套调用不确定顺序的 DAO 方法
- 应用层捕获
Deadlock found...错误,做有限重试(如 2–3 次),而不是静默失败
锁的粒度、持有时间、访问顺序,这三个变量只要有一个失控,高并发下锁争用就会快速放大。别指望加个 FOR UPDATE 就万事大吉。

















