Redis+Lua锁

一、Redis单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* Redis单例
*
* User: xiangqian
* Date: 17/8/3
* Time: 下午3:40
*/
namespace Model\Redis;
use \Redis;
class BaseModel
{
private static $_instances = [];
public $redis;
/**
* 构造方法
*
* BaseModel constructor.
* @param array $config
*/
private function __construct(array $config)
{
try {
$this->redis = new Redis();
$this->redis->connect($config['host'], $config['port'], $config['time_out']);
$this->redis->auth($config['auth']);
} catch (\RedisException $e) {
exit($e->getMessage());
}
}
/**
* 获取实例
*
* @param array $config
* @return BaseModel
*/
public static function getInstance(array $config)
{
ksort($config);
$key = md5(serialize($config));
if (isset(self::$_instances[$key]) && self::$_instances[$key] instanceof self) {
return self::$_instances[$key];
}
self::$_instances[$key] = new self($config);
return self::$_instances[$key];
}
/**
* 禁止克隆
*/
public function __clone()
{
trigger_error('can not clone', E_USER_ERROR);
}
}

二、Redis + Lua锁实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* Redis + Lua实现锁
*
* User: xiangqian
* Date: 17/12/19
* Time: 下午5:14
*/
namespace Model\Redis;
class Lock
{
private static $redisConfig = [
'host' => '127.0.0.1',
'port' => 6379,
'time_out' => 1,
'auth' => '123456'
];
/**
* 加锁
*
* @param $key
* @param $token
* @param int $ttl
* @return int
*/
public static function lock($key, $token, $ttl = 100)
{
$script = '
local ok = redis.call("setnx", KEYS[1], ARGV[1])
if ok == 1 then
redis.call("expire", KEYS[1], ARGV[2])
end
return ok';
return BaseModel::getInstance(self::$redisConfig)->redis->eval($script, [$key, $token, $ttl], 1);
}
/**
* 释放锁
*
* @param $key
* @param $token
* @return int
*/
public static function unlock($key, $token)
{
$script = '
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end';
return BaseModel::getInstance(self::$redisConfig)->redis->eval($script, [$key, $token], 1);
}
}

三、使用示例

1
2
3
4
5
$key = 'test_key'; //根据业务定名称
$token = uniqid($key); //每个请求生成唯一的token, 防止被别的请求释放锁
Lock::lock($key, $token, 60); //加锁
#do something with lock
Lock::unlock($key, $token); //释放锁