Redis 锁机制

INCR

这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行加一。然后其它用户在执行 INCR 操作进行加一时,如果返回的数大于 1 ,说明这个锁正在被使用当中。

复制代码; "复制代码")

1、 客户端A请求服务器获取key的值为1表示获取了锁 2、 客户端B也去请求服务器获取key的值为2表示获取锁失败 3、 客户端A执行代码完成,删除锁 4、 客户端B在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功 5、 客户端B执行代码完成,删除锁 $redis->incr($key); $redis->expire($key, $ttl); //设置生成时间为1秒

复制代码; "复制代码")

SETNX

这种加锁的思路是,如果 key 不存在,将 key 设置为 value,如果 key 已存在,则 SETNX 不做任何动作

复制代码; "复制代码")

1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功 2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败 3、 客户端A执行代码完成,删除锁 4、 客户端B在等待一段时间后在去请求设置key的值,设置成功 5、 客户端B执行代码完成,删除锁 $redis->setNX($key, $value); $redis->expire($key, $ttl);

复制代码; "复制代码")

SET

上面两种方法都有一个问题,会发现,都需要设置 key 过期。那么为什么要设置key过期呢?如果请求执行因为某些原因意外退出了,导致创建了锁但是没有删除锁,那么这个锁将一直存在,以至于以后缓存再也得不到更新。于是乎我们需要给锁加一个过期时间以防不测。
但是借助 Expire 来设置就不是原子性操作了。所以还可以通过事务来确保原子性,但是还是有些问题,所以官方就引用了另外一个,使用 SET 命令本身已经从版本 2.6.12 开始包含了设置过期时间的功能。

复制代码; "复制代码")

1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功 2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败 3、 客户端A执行代码完成,删除锁 4、 客户端B在等待一段时间后在去请求设置key的值,设置成功 5、 客户端B执行代码完成,删除锁 $rs =$redis->set($key, $value, array('nx', 'ex' => $ttl)); //ex表示秒
if ($rs) { //处理更新缓存逻辑

// ......
//删除锁
$redis->del($key);

}

复制代码; "复制代码")

到这一步其实还是有问题的,如果一个请求更新缓存的时间比锁的有效期还要长,导致在缓存更新过程中锁就失效了,此时另一个请求就会获取到锁,但前一个请求在缓存更新完毕的时候,直接删除锁的话就会出现误删其它请求创建的锁的情况。所以要避免这种问题,可以在创建锁的时候需要引入一个随机值并在删除锁的时候加以判断。

复制代码; "复制代码")

$random=mt\_rand(); $rs = $redis->set($key, $random, array('nx', 'ex' => $ttl)); if ($rs) { //处理更新缓存逻辑

// ......
//先判断随机数,是同一个则删除锁
if ($redis->get($key) == $random) { $redis->del($key);
}

}

复制代码; "复制代码")

原文地址:https://www.cnblogs.com/hzcya1995/p/13309192.html