快看我在Redis分布式锁上,栽的8个跟头!

作者:微信小助手

发布时间:2022-03-02T13:39:39

在分布式系统中,由于 redis 分布式锁相对于更简单和高效,成为了分布式锁的首先,被我们用到了很多实际业务场景当中。


但不是说用了 redis 分布式锁,就可以高枕无忧了,如果没有用好或者用对,也会引来一些意想不到的问题。


今天我们就一起聊聊 redis 分布式锁的一些坑,给有需要的朋友一个参考:

非原子操作


使用 redis 的分布式锁,我们首先想到的可能是 setNx 命令。

if (jedis.setnx(lockKey, val) == 1) {
   jedis.expire(lockKey, timeout);
}


容易,三下五除二,我们就可以把代码写好。这段代码确实可以加锁成功,但你有没有发现什么问题?


加锁操作和后面的设置超时时间是分开的,并非原子操作。假如加锁成功,但是设置超时时间失败了,该 lockKey 就变成永不失效。


假如在高并发场景中,有大量的 lockKey 加锁成功了,但不会失效,有可能直接导致 redis 内存空间不足。


那么,有没有保证原子性的加锁命令呢?答案是:有,请看下面。


忘了释放锁


上面说到使用 setNx 命令加锁操作和设置超时时间是分开的,并非原子操作。


而在 redis 中还有 set 命令,该命令可以指定多个参数。
String result = jedis.set(lockKey, requestId, "NX""PX", expireTime);
if ("OK".equals(result)) {
    return true;
}
return false;


其中:

  • lockKey:锁的标识

  • requestId:请求 id

  • NX:只在键不存在时,才对键进行设置操作。

  • PX:设置键的过期时间为 millisecond 毫秒。

  • expireTime:过期时间


set 命令是原子操作,加锁和设置超时时间,一个命令就能轻松搞定。nice!


使用 set 命令加锁,表面上看起来没有问题。但如果仔细想想,加锁之后,每次都要达到了超时时间才释放锁,会不会有点不合理?加锁后,如果不及时释放锁,会有很多问题。