本文目录导读:

在PHP中实现API分布式锁,主要有以下几种常见方案,这里以最推荐的Redis方案为例。
Redis分布式锁(推荐)
基础实现(使用SET NX EX)
class RedisDistributedLock {
private $redis;
private $lockKey;
private $lockValue;
private $ttl;
public function __construct($redis, $key, $ttl = 10) {
$this->redis = $redis;
$this->lockKey = "lock:{$key}";
$this->lockValue = uniqid('', true); // 唯一标识,用于安全释放锁
$this->ttl = $ttl;
}
/**
* 获取锁
*/
public function acquire() {
// 使用SET NX EX原子操作
$result = $this->redis->set(
$this->lockKey,
$this->lockValue,
['NX', 'EX' => $this->ttl]
);
return $result === true || $result === 'OK';
}
/**
* 释放锁(使用Lua脚本保证原子性)
*/
public function release() {
$script = '
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
';
return $this->redis->eval($script, [$this->lockKey, $this->lockValue], 1);
}
/**
* 尝试获取锁,带重试机制
*/
public function acquireWithRetry($maxRetries = 3, $retryDelay = 200000) {
$attempts = 0;
while ($attempts < $maxRetries) {
if ($this->acquire()) {
return true;
}
$attempts++;
usleep($retryDelay); // 微秒
}
return false;
}
}
Redlock算法实现(高可用)
class RedLock {
private $redisInstances = [];
public function __construct($redisConfigs) {
foreach ($redisConfigs as $config) {
$redis = new Redis();
$redis->connect($config['host'], $config['port']);
if (isset($config['password'])) {
$redis->auth($config['password']);
}
$this->redisInstances[] = $redis;
}
}
/**
* 获取锁(Redlock算法)
*/
public function lock($resource, $ttl = 10000) {
$startTime = microtime(true) * 1000;
$lockValue = uniqid('', true);
$quorum = floor(count($this->redisInstances) / 2) + 1;
$successCount = 0;
foreach ($this->redisInstances as $redis) {
try {
$result = $redis->set(
"lock:{$resource}",
$lockValue,
['NX', 'PX' => $ttl]
);
if ($result === true || $result === 'OK') {
$successCount++;
}
} catch (Exception $e) {
// 忽略连接失败
}
}
$elapsedTime = (microtime(true) * 1000) - $startTime;
// 检查是否获取到多数节点的锁,且耗时没有超过TTL
if ($successCount >= $quorum && $elapsedTime < $ttl) {
return $lockValue;
}
// 获取失败,释放所有锁
$this->unlock($resource, $lockValue);
return false;
}
/**
* 释放锁
*/
public function unlock($resource, $lockValue) {
$script = '
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
';
foreach ($this->redisInstances as $redis) {
try {
$redis->eval($script, ["lock:{$resource}", $lockValue], 1);
} catch (Exception $e) {
// 忽略错误
}
}
}
}
使用场景示例
API接口限流
class OrderAPI {
private $lock;
public function createOrder($userId, $productId) {
// 防止重复提交
$lockKey = "order:{$userId}:{$productId}";
$this->lock = new RedisDistributedLock($redis, $lockKey, 30);
try {
if (!$this->lock->acquire()) {
throw new Exception('请勿重复提交订单');
}
// 处理订单逻辑
$this->processOrder($userId, $productId);
} finally {
$this->lock->release();
}
}
}
秒杀场景
class SeckillService {
private $lock;
public function seckill($productId, $userId) {
// 分布式锁防止超卖
$lockKey = "seckill:{$productId}";
$this->lock = new RedisDistributedLock($redis, $lockKey, 5);
try {
if (!$this->lock->acquire()) {
// 返回友好提示
return ['code' => -1, 'msg' => '系统繁忙,请重试'];
}
// 检查库存
$stock = $redis->get("stock:{$productId}");
if ($stock <= 0) {
return ['code' => -1, 'msg' => '库存不足'];
}
// 扣减库存
$newStock = $redis->decr("stock:{$productId}");
if ($newStock < 0) {
$redis->incr("stock:{$productId}");
return ['code' => -1, 'msg' => '库存不足'];
}
// 创建订单
$this->createOrder($productId, $userId);
return ['code' => 0, 'msg' => '抢购成功'];
} finally {
$this->lock->release();
}
}
}
其他方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Redis | 高性能、原子操作支持好 | 需要Redis服务 | 大多数场景 |
| ZooKeeper | 强一致性、自动释放锁 | 性能不如Redis | 对一致性要求极高 |
| MySQL | 实现简单、无需额外服务 | 性能瓶颈、死锁风险 | 低并发场景 |
| Memcached | 高性能 | 不支持原子操作(需配合其他机制) | 简单锁需求 |
最佳实践建议
锁的粒度
// 细粒度锁(推荐)
$lockKey = "user:{$userId}:order";
// 粗粒度锁(不推荐)
$lockKey = "order:system";
锁超时处理
// 设置合理的超时时间
$lock = new RedisDistributedLock($redis, $key, 10); // 10秒超时
// 使用看门狗机制自动续期
class LockWithWatchDog {
private $lock;
private $watchDog;
public function __construct($redis, $key, $ttl = 10) {
$this->lock = new RedisDistributedLock($redis, $key, $ttl);
$this->startWatchDog();
}
private function startWatchDog() {
$this->watchDog = function() use ($key) {
while ($this->lock->isHeld()) {
$this->lock->expire($key, $ttl);
sleep($ttl / 3);
}
};
}
}
错误处理
try {
if (!$lock->acquire()) {
// 记录日志
Logger::warning("Failed to acquire lock: {$key}");
return response()->json(['error' => '系统繁忙'], 429);
}
// 业务逻辑
} catch (Exception $e) {
Logger::error("Lock operation failed: " . $e->getMessage());
throw $e;
} finally {
$lock->release();
}
注意事项
- 锁超时时间:根据业务逻辑执行时间合理设置,避免锁自动释放后其他进程获取到锁
- 唯一标识:使用UUID或uniqid作为锁值,防止误删其他进程的锁
- 原子操作:释放锁时必须使用Lua脚本确保原子性
- 重试机制:对于高竞争场景,实现带退避的重试策略
- 监控告警:记录锁获取失败次数,设置告警阈值
对于PHP API项目,建议首选Redis分布式锁,简单高效且能满足大多数场景需求,如果需要更高的可用性,可以考虑使用Redlock算法或引入ZooKeeper。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播