select for update nowait 一加就报错,是因为未在显式事务中执行:必须先 start transaction 或 set autocommit = 0,否则隐式事务导致锁瞬时释放,后续操作无保护;error 3572 表明锁申请失败,常因全表扫描触及其他被锁行,需确保 where 走高效索引并精准捕获该错误码。

SELECT FOR UPDATE NOWAIT 为什么一加就报错?
不是语法写错了,而是你没在显式事务里执行。FOR UPDATE NOWAIT 必须搭配 START TRANSACTION 或 SET autocommit = 0,否则 MySQL 会隐式开启又立刻提交事务,锁根本留不住,后续 UPDATE 就裸奔了。
常见错误现象:ERROR 3572 (HY000) 频繁出现,但业务逻辑却没卡住——说明锁确实没生效,只是语句被中断了。
- 检查当前 autocommit 状态:
SELECT @@autocommit;,值为1就得先关掉 - 正确顺序必须是:
START TRANSACTION→SELECT ... FOR UPDATE NOWAIT→ 校验 →UPDATE→COMMIT或ROLLBACK - 别在存储过程里拼接
NOWAIT字符串——MySQL 不支持动态锁提示
EXPLAIN 显示 type=ALL,NOWAIT 却总失败?
ERROR 3572 的真实原因,往往不是“你要的那行被锁了”,而是查询扫描了太多行,其中任意一行被锁就触发失败。全表扫描时,InnoDB 对每条扫描记录加意向锁,哪怕目标行没锁也会中招。
实操建议:
- 用
EXPLAIN SELECT ... FOR UPDATE NOWAIT看执行计划,重点盯type(不能是ALL)、key(必须有索引名)、Extra(避免Using where单独出现) - WHERE 条件字段必须有高效索引:主键最优,其次是唯一索引、覆盖索引;复合索引注意最左前缀匹配
- 避免
WHERE status = 'pending'这类无索引条件——加索引或改用ENUM+ 前缀索引
应用层怎么捕获和处理 ERROR 3572?
这个错误不是异常,是你设计的快速失败路径被命中了。泛化 catch 所有 SQL 错误会掩盖问题,必须精准识别。
关键点:
- 捕获条件必须同时满足:
SQLSTATE = 'HY000'且errno = 3572;只抓1205是错的(那是死锁超时) - 切忌无脑重试原语句——高并发下可能瞬间打爆连接池
- 合理 fallback 策略包括:
SELECT ... FOR UPDATE SKIP LOCKED再查一次、返回“正在处理中,请稍后再试”、写入异步队列、或走降级逻辑(如标记待人工审核) - 客户端驱动需支持获取
errno和SQLSTATE,比如 MySQLi 要用mysqli_errno()+mysqli_sqlstate()
NOWAIT 能不能用在 UPDATE 或子查询里?
不能。语法上严格限定:只允许出现在 SELECT ... FOR UPDATE NOWAIT 或 SELECT ... FOR SHARE NOWAIT 中。
以下全部非法,会直接报错:
-
UPDATE t SET x=1 WHERE id=123 NOWAIT——UPDATE不支持锁提示 -
SELECT * FROM t WHERE id IN (SELECT id FROM t2 FOR UPDATE NOWAIT)—— 子查询里不允许 -
SELECT * FROM t FOR UPDATE NOWAIT SKIP LOCKED——NOWAIT和SKIP LOCKED语义冲突,MySQL 明确禁止共用 -
SELECT * FROM t NOWAIT—— 没配FOR UPDATE或FOR SHARE,语法错误
真正容易被忽略的是:它只作用于行级锁,对 LOCK TABLES 这类表级锁完全无效;而且基于语句的复制(SBR)下不安全,生产环境务必用 ROW 或 MIXED 复制模式。











