PostgreSQL SKIP LOCKED 如何避免阻塞等待的批量更新
发布时间 - 2026-01-28 00:00:00 点击率:次SKIP LOCKED 只在 SELECT ... FOR UPDATE 或 FOR NO KEY UPDATE 的子查询中生效,需用 WITH 或子查询包裹并显式加锁,外层 UPDATE 不加锁;避免无索引过滤、缺失 ORDER BY 导致重复争抢,且无法跳过未提交的隐式锁行。
SKIP LOCKED 在 UPDATE 语句中怎么写才生效
SKIP LOCKED 只在 SELECT ... FOR UPDATE 或 SELECT ... FOR NO KEY UPDATE 的子查询中起作用,不能直接写在 UPDATE 顶层。常见错误是以为可以这样写:UPDATE t SET status = 'done' WHERE id IN (SELECT id FROM t WHERE status = 'pending' LIMIT 10 FOR UPDATE SKIP LOCKED) —— 这其实没问题,但关键在于:外层 UPDATE 本身不加锁,真正加锁和跳过的是子查询里的 SELECT。
实操建议:
- 必须用子查询包裹,且子查询需显式带
FOR UPDATE或FOR NO KEY UPDATE+SKIP LOCKED - 避免在子查询里用非索引字段过滤,否则可能全表扫描并锁大量无关行
- 如果业务允许“非严格 FIFO”,优先用
FOR NO KEY UPDATE(锁粒度更小,不阻塞其他UPDATE同一行的非键列操作)
为什么加了 SKIP LOCKED 还会卡住
典型现象是并发执行时某次查询返回空结果,或延迟明显升高。根本原因不是 SKIP LOCKED 失效,而是它只跳过“已被其他事务锁定的行”,不跳过“正在被其他事务修改但尚未提交、且未显式加锁”的行 —— 比如另一个事务刚执行了 UPDATE t SET status = 'processing' WHERE id = 123 但还没 COMMIT,此时你的 SELECT ... FOR UPDATE SKIP LOCKED 仍会等待该行的行级锁释放。
排查与应对:
- 用
pg_stat_activity查看是否有长事务或未提交的UPDATE卡在中间 - 确保所有涉及同一批数据的写操作都统一走带
SKIP LOCKED的路径,避免混用普通UPDATE - 设置合理的
lock_timeout(如SET lock_timeout = '500ms'),让等待失败可被捕获而非无限挂起
批量更新时 LIMIT 和 ORDER BY 的取舍
用

LIMIT 控制每次取多少行很自然,但若没配 ORDER BY,PostgreSQL 不保证顺序,不同事务可能反复抢同一组 ID,导致某些行长期得不到处理。而加 ORDER BY(尤其非索引列)又可能拖慢查询、引发排序临时文件。
推荐做法:
- 优先在过滤条件字段上建索引(如
status+created_at),再用ORDER BY created_at LIMIT 10,这样能走索引扫描 - 避免
ORDER BY random()或函数表达式,它们无法利用索引且强制排序 - 如果业务允许“近似公平”,可用
WHERE id > $last_id ORDER BY id LIMIT 10做游标分页,比全局排序更轻量
并发安全的完整模板示例
以下是一个生产环境验证过的最小可行模式,用于从待处理队列表中安全取一批任务:
WITH candidates AS ( SELECT id FROM tasks WHERE status = 'pending' ORDER BY priority DESC, id LIMIT 20 FOR NO KEY UPDATE SKIP LOCKED ) UPDATE tasks SET status = 'processing', worker_id = 'w-abc123', updated_at = now() WHERE id IN (SELECT id FROM candidates) RETURNING id, payload;
注意点:
-
FOR NO KEY UPDATE比FOR UPDATE更宽松,适合仅改状态类字段 -
RETURNING必须有,否则应用拿不到实际被更新的行,无法继续后续逻辑 - 不要依赖
UPDATE ... RETURNING的返回顺序做业务判断——它不保证和子查询ORDER BY一致
最易被忽略的是:SKIP LOCKED 解决不了长事务阻塞,也解决不了索引缺失导致的锁升级(比如锁整页甚至整个分区)。它只是并发调度的工具,不是万能锁优化器。
# 工具
# 为什么
# sql
# for
# select
# 并发
# postgresql
# 加锁
# 跳过
# 的是
# 只在
# 是一个
# 还没
# 还会
# 已被
# 分页
# 再用
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
Laravel怎么配置S3云存储驱动_Laravel集成阿里云OSS或AWS S3存储桶【教程】
Laravel如何使用Eloquent ORM进行数据库操作?(CRUD示例)
微信公众帐号开发教程之图文消息全攻略
音响网站制作视频教程,隆霸音响官方网站?
如何用IIS7快速搭建并优化网站站点?
ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集
如何在腾讯云服务器快速搭建个人网站?
Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
Python高阶函数应用_函数作为参数说明【指导】
如何快速搭建个人网站并优化SEO?
LinuxCD持续部署教程_自动发布与回滚机制
Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】
青岛网站建设如何选择本地服务器?
英语简历制作免费网站推荐,如何将简历翻译成英文?
如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】
如何快速生成专业多端适配建站电话?
在线教育网站制作平台,山西立德教育官网?
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives
Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】
公司门户网站制作流程,华为官网怎么做?
大同网页,大同瑞慈医院官网?
如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)
在线制作视频的网站有哪些,电脑如何制作视频短片?
JavaScript常见的五种数组去重的方式
北京网站制作公司哪家好一点,北京租房网站有哪些?
Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】
Java遍历集合的三种方式
Laravel如何创建自定义Facades?(详细步骤)
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
轻松掌握MySQL函数中的last_insert_id()
如何在Windows环境下新建FTP站点并设置权限?
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
Python面向对象测试方法_mock解析【教程】
如何为不同团队 ID 动态生成多个独立按钮
北京网页设计制作网站有哪些,继续教育自动播放怎么设置?
Java类加载基本过程详细介绍
Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧
香港服务器租用费用高吗?如何避免常见误区?
如何在万网利用已有域名快速建站?
网站制作软件有哪些,制图软件有哪些?
Laravel如何使用Telescope进行调试?(安装和使用教程)
JavaScript如何实现错误处理_try...catch如何捕获异常?
网站制作软件免费下载安装,有哪些免费下载的软件网站?
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
常州企业网站制作公司,全国继续教育网怎么登录?
黑客如何利用漏洞与弱口令入侵网站服务器?
佛山网站制作系统,佛山企业变更地址网上办理步骤?

