通过redis集合记录加锁的key,然后监听这个集合的key,判断锁key是否存在,不存在则在集合的key进行删除,存在则判断过期时间是否小于3秒,如果小于3秒则更新过期时间,保证redis执行命令的原子性,需要通过lua脚本来实现。
实现的流程图:

RedisLock代码
<?php
class RedisLock
{
//集合key分片最大数量
public $sliceMax = 2;
//保存redis
protected $redis;
//集合key名字
protected $lockKeyName = "lock_key_list";
/**
* 构造函数
* @param string $host redis地址
* @param int $prot redis端口
*/
public function __construct($host = '127.0.0.1',$port = 6379) {
$this->redis = new Redis();
$this->redis->connect($host,$port);
}
/**
* 加锁
* @param string $key 加锁key
* @param string $lockId 加锁的唯一表示
* @param int $ttl 生存时间
* @param int $retry 重试多少次
* @param int $usleep 休眠多少微秒
*/
public function lock($key,$lockId,$ttl = 5,$retry = 3, $usleep = 1000000) {
$return = false;
$luaScript = <<<LUA
if redis.call("setnx",KEYS[1],ARGV[1])
then
redis.call("expire",KEYS[1],tonumber(ARGV[2]))
redis.call("sadd",KEYS[2],KEYS[1])
return 1
else
return 0
end
LUA;
$this->setKeyName($key);
$return = $this->redis->eval($luaScript,[ $key,$this->lockKeyName,$lockId,$ttl],2);
if(!$return) {
while($retry-- >= 0) {
usleep($usleep);
$return = $this->redis->eval($luaScript,[ $key,$this->lockKeyName,$lockId,$ttl],2);
if($return) {
break;
}
}
}
return $return;
}
/**
* 解锁
* @param string $key 加锁key
* @param string $lockId 加锁的唯一表示
*/
public function unlock($key,$lockId) {
$luaScript = <<<LUA
if redis.call("get",KEYS[1]) == ARGV[1]
then
redis.call("del",KEYS[1])
redis.call("srem",KEYS[2],KEYS[1])
return 1
else
return 0
end
LUA;
$this->setKeyName($key);
$return = $this->redis->eval($luaScript,[ $key,$this->lockKeyName,$lockId ],2);
return $return;
}
/**
* 设置集合key的名称
*/
protected function setKeyName($key) {
if($this->sliceMax > 1) {
$num = $this->slices($key);
$this->lockKeyName = $this->lockKeyName.':'.$num;
}
}
/**
* 分片集合key算法
*/
protected function slices($key)
{
return abs(crc32($key)) % $this->sliceMax;
}
}
$r = new RedisLock();
$r->lock("lock:809","x12233");
//$r->unlock("lock:1","x12233");
ListenLock.php代码:
<?php
class ListenLock
{
protected $redis;
protected $lockKeyName = "lock_key_list";
public function __construct($host = '127.0.0.1',$port = 6379) {
$this->redis = new Redis();
$this->redis->connect($host,$port);
}
public function listen($argv)
{
if(isset($argv[1]) && !empty($argv[1]))
{
$this->lockKeyName = $this->lockKeyName.":".$argv[1];
}
while(true)
{
$arr = $this->redis->sMembers($this->lockKeyName);
if(!empty($arr))
{
foreach($arr as $k => $key) {
$luaScript = <<<LUA
if redis.call("exists",KEYS[1])
then
if redis.call("ttl",KEYS[1]) < tonumber(ARGV[1])
then
redis.call("expire",KEYS[1],tonumber(ARGV[2]))
return 1
end
return 0
else
redis.call('srem',KEYS[2],KEYS[1])
return 0
end
LUA;
$a = $this->redis->eval($luaScript,[$key,$this->lockKeyName,4,10],2);
}
}
sleep(1);
}
}
}
$l = new ListenLock();
$l->listen($argv);
执行监听命令
php ListenLock.php [分片的数字]

文章介绍了一个基于Redis的分布式锁实现,通过lua脚本确保操作的原子性。锁的添加和删除都通过特定的lua脚本完成,同时还有一个监听锁的机制,检查并更新锁的过期时间,确保锁的有效管理。

560

被折叠的 条评论
为什么被折叠?



