nowait不是让查询“不卡”,而是将50秒静默等待变为毫秒级error 3572;必须显式开启事务、确保索引命中、精准捕获该错误码并做业务降级,否则会 silently 退化为普通读或卡在间隙锁。

直接说结论:NOWAIT 不是让查询“不卡”,而是让卡住这件事立刻暴露出来——它把 50 秒静默等待,换成毫秒级的 ERROR 3572。你得在应用层捕获这个错误,而不是指望它自动变快。
SELECT FOR UPDATE NOWAIT 必须显式开启事务才生效
autocommit = 1 时,SELECT ... FOR UPDATE NOWAIT 会隐式开启并立即提交事务,锁拿不到、留不住,NOWAIT 也形同虚设。更糟的是——它不报错,只是悄悄退化成普通读,业务以为“抢到了”,其实根本没锁住。
- 必须先执行
START TRANSACTION或SET autocommit = 0 - 不能依赖框架自动事务管理,要确认事务真正开启且未提前提交
- 常见误判:日志没报错 + 返回了数据 → 就以为成功加锁,实际可能已降级
NOWAIT 只响应记录锁,对间隙锁完全无效
如果你的 WHERE 条件没走索引,或者用了非唯一二级索引,InnoDB 很可能加的是 gap lock 或 next-key lock。NOWAIT 对这类锁不触发立即失败,语句仍会卡住——你以为加了 NOWAIT 就安全了,其实只是“假装没锁”。
- 用
EXPLAIN SELECT ... FOR UPDATE NOWAIT看type字段:必须是const或ref,避免ALL或index -
Extra出现Using where往往意味着全索引扫描,锁范围远超预期 - 主键或唯一索引查询最稳妥;
status = 'pending'这类条件务必建好索引,否则 NOWAIT 白加
应用层必须精准捕获 ERROR 3572,不能泛化处理
ERROR 3572 是 NOWAIT 的专属错误码,和死锁(ERROR 1213)、超时(ERROR 1205)完全不同。混在一起捕获,会导致重试逻辑错乱、掩盖真实瓶颈,甚至引发重复扣减。
- PHP 中检查
$pdo->errorInfo()[1] === 3572 - Java JDBC 中需同时判断
SQLState == "HY000"且getErrorCode() == 3572 - 捕获后别直接返回 500:这是设计路径,不是系统故障;应走降级、提示“资源正被占用”或转异步队列
- 切忌无退避重试原语句——高并发下瞬间打满连接池
真正容易被忽略的点是:NOWAIT 失败 ≠ 查询没命中数据,而极可能是你扫了 1 万行,其中任意一行被锁就立刻报错。所以每次看到 ERROR 3572,第一反应不该是“换重试策略”,而是跑一遍 EXPLAIN,确认锁到底加在哪儿了。











