系列文章redis
当多个进程不在同一个系统中,就须要用分布式锁控制多个进程对资源的访问。bash
使用redis来实现分布式锁主要用到如下命令:微信
SETNX KEY VALUE
若是key不存在,就设置key对应字符串value并发
expire KEY seconds
设置key的过时时间dom
del KEY
删除key异步
代码实现以下:分布式
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$ok = $redis->setNX($key, $value);
if ($ok) {
//获取到锁
... do something ...
$redis->del($key);
}
复制代码
上面代码有没有问题呢? 若是咱们在逻辑处理过程当中出现了异常状况,致使KEY没有删除,那就出现了死锁了。因此通常咱们在拿到锁以后再给KEY加一个过时时间post
为了保证执行的原子性,使用了multi
就有了以下代码ui
$redis->multi();
$redis->setNX($key, $value);
$redis->expire($key, $ttl);
$res = $redis->exec();
if($res[0]) {
//获取到锁
... do something ...
$redis->del($key);
}
复制代码
可是这样的又有一个问题第一个请求成功了,以后的请求虽然没有拿到锁可是每次都刷新了锁的时间。这样咱们设置锁过时时间的意义就不存在了。因此咱们在拿到锁之后再进行过时时间的操做,这时候咱们就能够祭出原子性操做的lua脚本,代码以下lua
$script = <<<EOT
local key = KEYS[1]
local value = KEYS[2]
local ttl = KEYS[3]
local ok = redis.call('setnx', key, value)
if ok == 1 then
redis.call('expire', key, ttl)
end
return ok
EOT;
$res = $redis->eval($script, [$key,$val, $ttl], 3);
if($res) {
//获取到锁
... do something ...
$redis->del($key);
}
复制代码
借助lua脚本虽然解决了问题,可是未免有些麻烦,Redis从 2.6.12 版本开始, SET 命令的行为能够经过一系列参数来修改:
$ok = $redis->set($key, $random, array('nx', 'ex' => $ttl));
if ($ok) {
//获取到锁
... do something ...
if ($redis->get($key) == $random) {
$redis->del($key);
}
}
复制代码
能够看到上面咱们咱们的值引入了一个随机数,这是为了防止逻辑处理时间过长致使锁的过时时间已经失效,这时候下一个请求就得到了锁,可是前一个请求在逻辑处理完直接删除了锁。
锁主要用在并发请求如秒杀等场景中,以上即是redis锁的实现。
本文亦在微信公众号【小道资讯】发布,欢迎扫码关注!