postgresql怎么模拟分布式锁
这段代码利用 PostgreSQL 的咨询锁来实现分布式互斥先用尝试加锁;拿到锁后在本地事务里安全执行写数据库/第三方调用;最终显式释放锁。若同时有多个进程运行、且使用同一lockId,同一时刻最多只有一个进程能进入“业务区”,从而保证操作的串行安全性。
·
<?php
$pdo = new PDO('pgsql:host=127.0.0.1;dbname=test', 'postgres', '123456');
$lockId = 20240616; // 自定义:同一资源必须用同一数字
// 尝试加锁(非阻塞)
$stmt = $pdo->query("SELECT pg_try_advisory_lock($lockId)");
if (!$stmt->fetchColumn()) {
exit("资源正被其它进程占用,稍后再试\n");
}
echo "拿到锁,开始业务\n";
try {
$pdo->beginTransaction();
// TODO:写数据库 / 调用第三方
$pdo->commit();
echo "业务完成\n";
} finally {
// 主动释放;若忘记释放,连接关闭也会自动释放
$pdo->query("SELECT pg_advisory_unlock($lockId)");
}
解释
下面只解释这段代码本身在做什么,逐行说明其目的。
$pdo = new PDO('pgsql:host=127.0.0.1;dbname=test', 'postgres', '123456');
- 连接本地 PostgreSQL 数据库
test。
返回的 PDO 对象用于后续执行 SQL。
$lockId = 20240616; // 同一资源统一用同一个数字
- 定义一个 锁 ID(64 位整数)。
在 PostgreSQL 的“咨询锁”(advisory lock) 机制里,这个数字就是被锁定的资源标识;并发进程只要用同一个数字,就能互斥。
// 尝试加锁(非阻塞)
$stmt = $pdo->query("SELECT pg_try_advisory_lock($lockId)");
if (!$stmt->fetchColumn()) {
exit("资源正被其它进程占用,稍后再试\n");
}
- 调用
pg_try_advisory_lock()尝试获得锁:- 若锁空闲→返回
true⇒ 继续往下执行。 - 若锁被其他进程持有→返回
false⇒ 立即退出,提示“资源被占用”。
- 若锁空闲→返回
echo "拿到锁,开始业务\n";
try {
$pdo->beginTransaction(); // 开启数据库事务
// TODO:写数据库 / 调用第三方 ← 在这里放真正的业务代码
$pdo->commit(); // 提交事务
echo "业务完成\n";
} finally {
- 执行业务逻辑
- 先输出拿锁成功信息。
- 用本地事务包裹对数据库的写操作或调用外部接口。
- 成功后
commit();如果try块里抛异常会直接进入finally。
// 主动释放;若忘记释放,连接关闭也会自动释放
$pdo->query("SELECT pg_advisory_unlock($lockId)");
}
- 释放锁
pg_advisory_unlock()显式把锁释放掉,让其他进程可以继续获得。- 即便忘记调用,数据库连接关闭时,PostgreSQL 会自动删除该会话持有的咨询锁,避免死锁。
总结一句话
这段代码利用 PostgreSQL 的咨询锁来实现 分布式互斥:
- 先用
pg_try_advisory_lock()尝试加锁; - 拿到锁后在本地事务里安全执行写数据库/第三方调用;
- 最终显式
pg_advisory_unlock()释放锁。
若同时有多个进程运行、且使用同一lockId,同一时刻最多只有一个进程能进入“业务区”,从而保证操作的串行安全性。
更多推荐



所有评论(0)