PostgreSQL Advisory Lock 是什么?和行锁有什么区别?
简化版
Advisory Lock 是 PostgreSQL 提供的应用级协作锁,锁的是一个业务自定义的数字 key,而不是某一行数据。它适合任务互斥、定时任务防重、业务资源串行化;但数据库不会理解这个 key 的业务含义,必须由应用统一约定并保证释放。
详细版
行锁依附于表行,update 或 select for update 会锁住真实数据行。Advisory Lock 不依附具体行,而是通过 pg_advisory_lock、pg_try_advisory_lock 等函数锁住一个整数 key。
特点:
- 可以做事务级锁或会话级锁。
- 适合没有天然数据行可锁的业务资源。
try版本能非阻塞尝试获取。- key 设计要避免冲突。
- 会话级锁要防连接池导致未释放。
它不是自动业务约束,所有参与方必须遵守同一套加锁规则。
完整版教学
一、Advisory Lock 是应用约定的锁
普通行锁锁的是数据库知道的行。Advisory Lock 锁的是应用传入的数字 key,数据库只负责互斥,不知道这个 key 代表订单、用户还是任务。
select pg_try_advisory_lock(1001);
如果所有应用都约定 1001 表示某个业务资源,那么这个锁就能实现互斥。如果有代码不遵守约定,直接绕过锁操作数据,数据库不会阻止它。
记忆钩子:Advisory Lock 是“君子协定锁”,数据库管互斥,业务管含义。
二、它和行锁的区别
行锁依赖真实行,适合保护某条记录。Advisory Lock 不需要有对应行,适合保护抽象资源。
| 对比 | 行锁 | Advisory Lock |
|---|---|---|
| 锁对象 | 表中的行 | 自定义 key |
| 数据库是否理解业务 | 理解行关系 | 不理解业务含义 |
| 典型场景 | 更新账户、订单 | 定时任务、资源互斥 |
| 绕过风险 | SQL 更新会遇到锁 | 不加锁的代码可绕过 |
如果资源本身就是一行数据,优先考虑行锁或条件更新;如果资源没有天然行,Advisory Lock 更方便。
三、事务级锁和会话级锁要分清
PostgreSQL Advisory Lock 有事务级和会话级两类。事务级锁会在事务结束时自动释放;会话级锁要显式释放或连接断开才释放。
-- 事务级
select pg_advisory_xact_lock(1001);
-- 会话级
select pg_advisory_lock(1001);
select pg_advisory_unlock(1001);
Web 应用里更推荐事务级锁,因为连接池复用连接时,会话级锁如果忘记释放,可能影响后续请求。
四、try lock 适合防重复任务
pg_try_advisory_lock 或 pg_try_advisory_xact_lock 会立即返回是否获取成功,不会一直等待。这适合定时任务防重复。
select pg_try_advisory_xact_lock(20260729);
如果返回 false,说明别的实例正在执行,当前实例可以直接跳过。这样多个应用实例部署同一个定时任务时,只会有一个真正执行。
五、key 设计要稳定且避免冲突
Advisory Lock 的 key 是数字,可以用一个 bigint,也可以用两个 int。业务上要把资源映射成稳定 key。
任务锁: hash('daily_report')
用户锁: user_id
订单锁: order_id
租户任务锁: tenant_id + job_type
如果 key 设计冲突,不相关资源会互相阻塞;如果 key 不统一,同一个资源又可能被多个 key 表示,锁就失效。
六、它不替代唯一约束和事务
Advisory Lock 可以减少并发冲突,但不能替代数据库约束。比如防止重复订单,仍应有唯一索引或幂等表兜底。
Advisory Lock: 减少同一资源并发执行
唯一索引: 防止重复数据最终落库
事务: 保证一组数据修改原子性
面试中要避免把 Advisory Lock 说成万能分布式锁。它是 PostgreSQL 内部锁,适用于使用同一个数据库作为协调点的场景。
七、常见误区与追问
- 误区:Advisory Lock 会自动保护某张表。 它只保护自定义 key,必须应用代码共同遵守。
- 误区:会话级锁用起来更安全。 连接池下忘记释放会污染连接,Web 请求更推荐事务级锁。
- 误区:有 Advisory Lock 就不用唯一索引。 唯一性仍要靠数据库约束兜底。
- 追问:try lock 有什么用? 非阻塞尝试获取锁,适合任务防重和快速失败。
- 追问:key 冲突会怎样? 不相关业务会互相阻塞,导致性能和可用性问题。
- 追问:它能跨服务使用吗? 可以,只要服务连接同一个 PostgreSQL 集群并遵守同一 key 规则。
八、面试中可以这样落地
多个实例执行同一个日报任务,可以用事务级 try lock:
begin;
select pg_try_advisory_xact_lock(900001);
-- true 才执行任务核心逻辑
commit;
如果获取失败就跳过本次执行。任务产生的数据仍要有唯一约束,例如 (report_date, tenant_id),防止异常情况下重复写入。
九、加强记忆
Advisory Lock 记住“自定义 key、应用约定、事务级优先、约束兜底”。它适合任务互斥和抽象资源串行化,但数据库不知道业务含义。面试回答时一定要补上连接池释放风险和唯一索引兜底。