nowait 不是避免等待,而是把“卡住等锁”变成“立刻报错”,错误码固定为 error 3572;它只在显式事务 + 行级记录锁 + 精确索引查询下才真正生效。

直接结论:NOWAIT 不是避免等待,而是把“卡住等锁”变成“立刻报错”,错误码固定为 ERROR 3572;它只在显式事务 + 行级记录锁 + 精确索引查询下才真正生效。
为什么加了 NOWAIT 还是卡住?
常见现象是语句没报错、也没返回结果,看起来像卡死——这说明 NOWAIT 根本没起作用。根本原因通常是:
- 没开启显式事务:
autocommit = 1下执行SELECT ... FOR UPDATE NOWAIT,MySQL 会隐式开事务并立即提交,锁不持续,NOWAIT 失效 - WHERE 条件没走索引:执行计划出现
type: ALL或Extra: Using where,导致扫描大量行,锁落在非目标行上,NOWAIT 对间隙锁(GAP)或临键锁(NEXT-KEY)无效 - 用了子查询、UNION、视图或 UPDATE/DELETE:这些语法不支持 NOWAIT,MySQL 直接忽略或报错
Unknown syntax
必须满足的硬性前提条件
缺一不可,否则 NOWAIT 形同虚设:
- 必须在显式事务中:先执行
START TRANSACTION或SET autocommit = 0,不能依赖自动提交 - 必须搭配
FOR UPDATE或FOR SHARE:单独写NOWAIT语法错误;且NOWAIT必须紧贴其后,中间不能换行、不能有注释 - 仅 MySQL 8.0.1+ 支持:低版本直接报错,不是警告
- 只对行级记录锁(RECORD LOCK)响应:对间隙锁(GAP)、表锁、MDL 锁完全无感
应用层怎么正确捕获和处理 ERROR 3572
这个错误不是数据库异常,而是你设计的快速失败路径被触发。处理不当会导致雪崩或数据错乱:
- 必须精准识别:
SQLSTATE = 'HY000'且errno = 3572;不能和ERROR 1205(超时)或ERROR 1213(死锁)混用同一套重试逻辑 - PHP 中检查
$pdo->errorInfo()[1] === 3572;Java JDBC 需判断getSQLState().equals("HY000") && getErrorCode() == 3572 - 不建议原地重试:高并发下无退避重试会瞬间打满连接池;更适合 fallback 到
SKIP LOCKED、异步队列、或返回“资源正被占用”提示 - 切忌泛化为 500 错误:把 3572 当系统故障上报,会掩盖真实的业务争抢瓶颈
最容易被忽略的执行计划陷阱
NOWAIT 报错后第一反应不该是改代码重试,而是看 EXPLAIN:
- 执行
EXPLAIN SELECT * FROM t WHERE id = 100 FOR UPDATE NOWAIT,确认type是const或ref,不是ALL或index - 主键或唯一索引最安全;非唯一二级索引 +
FOR UPDATE极易触发间隙锁,此时 NOWAIT 完全无效 - 若
Extra出现Using index condition或Using where,说明索引未覆盖查询,可能锁住额外记录
真正难的不是写对语法,而是确保每次查询都命中单行记录锁——这要求索引设计、查询条件、事务边界三者严丝合缝。任何一环松动,NOWAIT 就会静默失效,或者报错却不知为何而错。











