<?php
/**
* Created by PhpStorm.
* User: Hank
* Date: 2019/3/25 0025
* Time: 下午 15:14
*/
namespace App\Lib\Redis;
use Predis\ClientInterface;
/**
* redis锁
* 安全和可靠性保证
在描述我们的设计之前,我们想先提出三个属性,这三个属性在我们看来,是实现高效分布式锁的基础。
一致性:互斥,不管任何时候,只有一个客户端能持有同一个锁。
分区可容忍性:不会死锁,最终一定会得到锁,就算一个持有锁的客户端宕掉或者发生网络分区。
可用性:只要大多数Redis节点正常工作,客户端应该都能获取和释放锁。
* Class RedisLock
* @author hank
* @package App\Lib\Redis
*/
class RedisLock
{
const LOCK_REDIS_KEY_PREFIX = "LOCK_KEY:";
/**
* @var ClientInterface
*/
private $redis;
/**
* RedisLock constructor.
* @param ClientInterface $redis
*/
public function __construct(ClientInterface $redis)
{
$this->redis = $redis;
}
/**
* 获得锁
* @param $key
* @param $timeout int 超时秒数默认10秒
* @return bool|int 获得成功锁返回随机value
*/
public function lock($key, $timeout = 10){
$myRandomValue = mt_rand();
$key = self::LOCK_REDIS_KEY_PREFIX.$key;
if($this->redis->eval("return redis.call('SET', KEYS[1], KEYS[2], 'NX', 'PX', KEYS[3])", 3, $key, $myRandomValue, $timeout*1000)){
return $myRandomValue;
}
return false;
}
/**
* 解锁
* !这里如果锁自动过期不存在了也返回 解锁成功true!
* @param $key
* @param $myRandomValue int 解锁随机值
* @return bool
*/
public function unLock($key, $myRandomValue){
$key = self::LOCK_REDIS_KEY_PREFIX.$key;
if($this->redis->eval("if redis.call('EXISTS',KEYS[1]) == 0 then return 1 else if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end end", 1, $key, $myRandomValue)){
return true;
}
return false;
}
}
这里需要明白为什么使用lua脚本,因为redis的lua脚本是
原子性(事务)的
,加锁解锁整个过程必须保证原子性(事务),具体的原理可以参考:
《Redis官方文档》用Redis构建分布式锁
版权声明:本文为huang007guo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。