工作日志之postgresql实现分布式锁

在PostgreSQL中实现分布式锁,有几种主流方案,各有侧重。其中最经典高效的是使用咨询锁(Advisory Locks)。

方案 核心原理 优点 缺点 适用场景

  1. 基于咨询锁 利用PG内置的 pg_advisory_lock(key) 函数在服务器内存中加锁。 性能极高、无表结构依赖、会话/事务级锁灵活。 锁与连接绑定,连接断开会释放;需自行处理死锁。 高并发、短任务(如秒杀、定时任务调度)。
  2. 基于行级锁 使用 SELECT ... FOR UPDATE 锁定表中的一条特定记录。 利用事务机制,可靠自动释放;锁状态可查。 性能不如咨询锁;需要创建锁表;事务持有时间长影响并发。 需要与业务数据强关联或跨事务复杂锁的场景。
  3. 基于排他锁 使用 LOCK TABLE ... IN ACCESS EXCLUSIVE MODE 锁整张表。 实现最简单粗暴。 并发性能最差,阻塞所有其他操作。 极少使用,仅用于极端维护操作。
    ⚠️ 重要前提:所有方案都要求连接到同一个PostgreSQL数据库集群,适用于分布式服务共享一个数据库的场景。若你的服务连接的是完全独立的不同数据库,则需寻求其他方案(如基于Redis或ZooKeeper)。

方案一: 基于咨询锁实现分布式锁

这是PostgreSQL为应用程序锁提供的原生、高性能方案。关键在于选择会话级或事务级锁。

(1)会话级锁:pg_advisory_lock(key),连接断开才释放,适合跨事务的长时间锁定。

(2)事务级锁:pg_advisory_xact_lock(key),事务结束(提交或回滚)即释放,更常用。

java 复制代码
-- 事务1:获取锁(这里使用事务级锁,key可以是一个或多个整数,或一个字符串的哈希值)
BEGIN;
SELECT pg_advisory_xact_lock(123456);
-- 执行你的临界区业务逻辑...
-- COMMIT; 提交后锁自动释放

-- 事务2:尝试获取同一个锁(会阻塞等待,直到事务1释放)
BEGIN;
SELECT pg_advisory_xact_lock(123456); -- 这里会等待
-- 获取成功后执行...
COMMIT;

非阻塞尝试加锁

java 复制代码
-- 使用 pg_try_advisory_xact_lock(key),立即返回 true/false,不会阻塞
BEGIN;
SELECT pg_try_advisory_xact_lock(123456);
-- 如果返回 false,表示未获取到锁,可以执行其他降级逻辑
COMMIT;

方法二: 基于行级锁(唯一性约束)

创建表

sql 复制代码
CREATE TABLE distributed_lock (
    lock_key VARCHAR(100) PRIMARY KEY,
    locked_by TEXT,
    locked_at TIMESTAMPTZ DEFAULT NOW()
);

获取锁(利用唯一性约束)

sql 复制代码
BEGIN;
-- 尝试插入一条记录作为获取锁
INSERT INTO distributed_lock (lock_key, locked_by) VALUES ('order_operation', 'service_01')
ON CONFLICT (lock_key) DO UPDATE SET
    locked_by = EXCLUDED.locked_by,
    locked_at = NOW()
WHERE distributed_lock.locked_at < NOW() - INTERVAL '30 seconds'; -- 实现简单的超时释放
-- 检查是否更新成功(即获取到锁)
GET DIAGNOSTICS row_count = ROW_COUNT;
IF row_count = 0 THEN
    -- 未获取到锁
    ROLLBACK;
ELSE
    -- 成功获取锁,执行业务逻辑...
    COMMIT; -- 提交后,锁记录依然存在,但可通过删除记录或更新状态来释放
END IF;

释放锁

sql 复制代码
-- 释放锁:删除记录或更新字段标识
DELETE FROM distributed_lock WHERE lock_key = 'order_operation';

以上方案适合没有redis,zookeeper等典型用于实现分布式锁的系统,并且前提是多个实例共享同一个pg集群或数据库实例。

相关推荐
sitellla13 分钟前
MySQL 入门:最流行的开源关系型数据库介绍
数据库·mysql·其他·开源
精益数智工坊15 分钟前
拆解制造业仓库物料管理流程:如何通过标准化仓库物料管理流程解决账实不符难题
大数据·前端·数据库·人工智能·精益工程
nbwenren25 分钟前
办公AI实测:Gemini3、GPT-4o、Claude3.5谁更强?
服务器·数据库·php
2401_8242226929 分钟前
如何卸载并重装Oracle Grid_Deinstall脚本与ASM磁盘清理
jvm·数据库·python
杨云龙UP32 分钟前
Oracle数据库启动失败:ORA-29701、ORA-01565、ORA-17503故障处理记录_20260429
linux·运维·数据库·oracle·centos
qq_4142565732 分钟前
生产库如何利用Navicat实现配置特定触发器事件调度_提高管理效率
jvm·数据库·python
2301_8084143834 分钟前
MySQL表的约束
数据库·mysql
2301_7756398938 分钟前
mysql如何查看服务器支持的存储引擎_使用SHOW ENGINES命令
jvm·数据库·python
a7963lin40 分钟前
html标签怎样表示搜索框_input type=search语义优化【操作】
jvm·数据库·python
a7963lin44 分钟前
Python数据分析如何识别异常值_IQR四分位距检测法实战
jvm·数据库·python