首页/数据/SQL 速查/NOWAIT / SKIP LOCKED 锁读策略
变更与事务52 条

NOWAIT / SKIP LOCKED 锁读策略

锁读的两个变体:NOWAIT 拿不到锁立刻报错,SKIP LOCKED 跳过被锁行——后者是队列消费场景的常用模式。

SQL 速查数据更新于 2026-10-13

NOWAIT 与 SKIP LOCKED 是什么

SELECT … FOR UPDATE 默认会一直等锁,高并发抢任务队列时会排出一条长长的等待队列。NOWAIT 让语句遇到已加锁的行就立刻报错返回;SKIP LOCKED 直接跳过被锁住的行,只返回能成功加锁的那些行。MySQL 8.0 与 PostgreSQL 9.5+ 都在 FOR UPDATE / FOR SHARE 上支持这两个修饰符,它们让「多个 worker 并发消费同一张任务表」变得简单而高效。

基本信息

适用平台MySQL 8.0+、PostgreSQL 9.5+
语法SELECT … FOR UPDATE NOWAIT / SELECT … FOR UPDATE SKIP LOCKED
NOWAIT 行为遇到已锁行立即报错,由应用决定重试或放弃
SKIP LOCKED 行为跳过被锁行,只返回当前可锁的行
必须包事务锁在事务提交或回滚时释放,不包事务等于没加锁

典型使用场景

  • 任务队列取单:多个 worker 从 jobs 表抢任务,避免互相等待
  • 消息表消费:每批取若干条待处理消息,处理完再改状态
  • 分布式定时任务去重:同一时刻只允许一个实例拿到锁
  • 批量清理数据:跳过正在被其他事务修改的行,避免长时间阻塞
  • 库存扣减排队优化:抢不到就快速失败,由上层重试而不是干等

MySQL 抢任务写法

MySQL 抢任务写法
START TRANSACTION;
SELECT id, payload
FROM jobs
WHERE status = 'pending'
ORDER BY id
LIMIT 1
FOR UPDATE SKIP LOCKED;      # 跳过已被其他 worker 锁住的任务

UPDATE jobs
   SET status = 'running', worker = 'w1', started_at = NOW()
 WHERE id = 5;               # 用上一步拿到的 id
COMMIT;                      # 事务要尽量短,锁在这里释放

PostgreSQL 用法

PostgreSQL 用法
BEGIN;
SELECT id FROM jobs
 WHERE status = 'pending'
 ORDER BY id
 LIMIT 1
 FOR UPDATE SKIP LOCKED;      -- 取不到可锁行时返回空结果集

-- 快速失败:锁冲突时直接报错,不排队
SELECT id FROM jobs WHERE status = 'pending' FOR UPDATE NOWAIT;
COMMIT;

三种锁读方式对照

写法遇到已锁行适用判断
FOR UPDATE一直等待直到锁超时必须处理这一行、且可以等
FOR UPDATE NOWAIT立即报错返回快速失败、由应用重试
FOR UPDATE SKIP LOCKED跳过该行继续找下一行任务队列、并发消费
FOR SHARE SKIP LOCKED跳过被排他锁占用的行读多写少的共享校验场景
SKIP LOCKED 的结果是不确定的:被跳过的行可能进本轮也可能进下一轮,绝不能用于统计、对账或任何需要完整数据的查询,否则会出现「结果少了几行但没有任何报错」。
NOWAIT 抛出的是锁冲突错误而不是业务异常,应用必须能区分二者(MySQL 错误码 3572,PostgreSQL 的 SQLSTATE 55P03),否则会把正常的锁竞争误记成任务失败。

易错点

  • 把 SKIP LOCKED 用在报表或全量统计上:被锁行被静默排除,结果少数据且毫无提示
  • 忘记包在事务里:语句一结束锁就释放,多个 worker 可能重复处理同一条任务
  • 事务里取到任务后调用慢接口,事务持续数秒,其他 worker 只能反复跳过,吞吐反而下降
  • 把 NOWAIT 的锁错误当成业务错误处理,导致任务被错误标记为失败

数据来源与参考

  • MySQL 8.0 Reference Manual · Locking Reads
  • PostgreSQL Documentation · SELECT
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌