
PHP后端分布式锁实现方案:从Redis到ZooKeeper的实战指南
在分布式系统开发中,我经常遇到需要协调多个进程同时访问共享资源的情况。如果没有合适的锁机制,就会导致数据不一致、重复处理等严重问题。今天我就结合自己的实战经验,分享几种在PHP中实现分布式锁的方案。
为什么需要分布式锁
记得有一次,我们的订单系统在促销活动时出现了重复扣款的问题。排查后发现,多个PHP进程同时处理同一个订单,导致业务逻辑被执行了多次。这就是典型的并发问题,分布式锁正是解决这类问题的利器。
基于Redis的分布式锁实现
Redis是目前最常用的分布式锁实现方案,我推荐使用RedLock算法。下面是我在实际项目中使用的代码:
class RedisDistributedLock
{
private $redis;
private $lockKey;
private $lockValue;
private $expireTime;
public function __construct($redis, $lockKey, $expireTime = 30)
{
$this->redis = $redis;
$this->lockKey = $lockKey;
$this->expireTime = $expireTime;
$this->lockValue = uniqid();
}
public function acquire()
{
$result = $this->redis->set(
$this->lockKey,
$this->lockValue,
['NX', 'EX' => $this->expireTime]
);
return $result !== false;
}
public function release()
{
$script = "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
";
$this->redis->eval($script, [$this->lockKey, $this->lockValue], 1);
}
}
这里有个踩坑经验:一定要使用Lua脚本来保证释放锁的原子性,否则可能会出现误删其他进程锁的情况。
基于数据库的分布式锁
在没有Redis的环境下,我们可以利用数据库来实现分布式锁。我通常使用MySQL的唯一索引特性:
class DatabaseDistributedLock
{
private $pdo;
private $tableName = 'distributed_locks';
public function __construct($pdo)
{
$this->pdo = $pdo;
}
public function acquire($lockName, $timeout = 30)
{
try {
$stmt = $this->pdo->prepare(
"INSERT INTO {$this->tableName} (lock_name, acquired_at, expires_at) VALUES (?, NOW(), DATE_ADD(NOW(), INTERVAL ? SECOND))"
);
$stmt->execute([$lockName, $timeout]);
return true;
} catch (PDOException $e) {
// 锁已存在,尝试获取过期锁
return $this->acquireExpiredLock($lockName, $timeout);
}
}
private function acquireExpiredLock($lockName, $timeout)
{
$stmt = $this->pdo->prepare(
"UPDATE {$this->tableName} SET acquired_at = NOW(), expires_at = DATE_ADD(NOW(), INTERVAL ? SECOND) WHERE lock_name = ? AND expires_at < NOW()"
);
$stmt->execute([$timeout, $lockName]);
return $stmt->rowCount() > 0;
}
}
基于ZooKeeper的分布式锁
对于更复杂的分布式场景,ZooKeeper提供了更可靠的锁机制。虽然配置相对复杂,但它的强一致性保证是其他方案无法比拟的。
use ZooKeeper;
class ZookeeperDistributedLock
{
private $zk;
private $lockPath = '/locks/';
private $currentNode;
public function __construct($hosts)
{
$this->zk = new ZooKeeper($hosts);
}
public function acquire($lockName)
{
$path = $this->lockPath . $lockName;
$this->currentNode = $this->zk->create(
$path . '/lock-',
'',
[ZooKeeper::EPHEMERAL | ZooKeeper::SEQUENCE],
ZooKeeper::PERM_ALL
);
return $this->waitForLock($path);
}
private function waitForLock($path)
{
$children = $this->zk->getChildren($path);
sort($children);
$currentNodeName = basename($this->currentNode);
return $children[0] === $currentNodeName;
}
}
实战中的注意事项
根据我的经验,选择哪种方案需要考虑以下几个因素:
- 性能要求:Redis性能最好,适合高并发场景
- 一致性要求:ZooKeeper提供最强的一致性保证
- 运维成本:数据库方案部署最简单
- 锁超时设置:一定要设置合理的超时时间,避免死锁
总结
在实际项目中,我通常根据具体场景选择合适的方案。对于大多数Web应用,Redis分布式锁已经足够;对于金融等对一致性要求极高的场景,ZooKeeper是更好的选择。希望这些经验能帮助你在分布式系统开发中少走弯路!
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
源码库 » PHP后端分布式锁实现方案
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
源码库 » PHP后端分布式锁实现方案
