帮助中心/最新通知

质量为本、客户为根、勇于拼搏、务实创新

< 返回文章列表

【服务器相关】redis分布式锁及会出现的问题解决

发表时间:2025-09-24 16:09:00 小编:主机乐-Yutio

一、redis实现分布式锁的主要原理:

1.加锁

最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加锁,可以给key命名为 “lock_sale_商品ID” 。而value设置成什么呢?我们可以姑且设置成1。加锁的伪代码如下:
setnx(key,1)

当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁;当一个线程执行setnx返回0,说明key已经存在,该线程抢锁失败。

2.解锁

有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行del指令,伪代码如下:

del(key)

释放锁之后,其他线程就可以继续执行setnx命令来获得锁。

3.锁超时

锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程再也别想进来。

所以,setnx的key必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。setnx不支持超时参数,所以需要额外的指令,伪代码如下:

expire(key, 30)

二、加锁的代码

二、解锁的代码三、续约问题

上面加锁最后的代码就完美了吗?假想这样一个场景,如果过期时间为30S,A线程超过30S还没执行完,但是自动过期了。这时候B线程就会再拿到锁,造成了同时有两个线程持有锁。这个问题可以归结为”续约“问题,即A没执行完时应该过期时间续约,执行完成才能释放锁。怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续约”。

其实,后面解锁出现的删除非自己锁,也属于“续约”问题。

四、集群同步延迟问题

用于redis的服务肯定不能是单机,因为单机就不是高可用了,一量挂掉整个分布式锁就没用了。

在集群场景下,如果A在master拿到了锁,在没有把数据同步到slave时,master挂掉了。B再拿锁就会从slave拿锁,而且会拿到。又出现了两个线程同时拿到锁。

基于以上的考虑,Redis 的作者也考虑到这个问题,他提出了一个 RedLock 的算法。

这个算法的意思大概是这样的:假设 Redis 的部署模式是 Redis Cluster,总共有 5 个 Master 节点。

通过以下步骤获取一把锁:

  • 获取当前时间戳,单位是毫秒。
  • 轮流尝试在每个 Master 节点上创建锁,过期时间设置较短,一般就几十毫秒。
  • 尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点(n / 2 +1)。
  • 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了。
  • 要是锁建立失败了,那么就依次删除这个锁。
  • 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。

但是这样的这种算法还是颇具争议的,可能还会存在不少的问题,无法保证加锁的过程一定正确。

这个问题的根本原因就是redis的集群属于AP,分布式锁属于CP,用AP去实现CP是不可能的。

五、Redisson

Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。

Redisson通过lua脚本解决了上面的原子性问题,通过“看门狗”解决了续约问题,但是它应该解决不了集群中的同步延迟问题。

总结

redis分布式锁的方案,无论用何种方式实现都会有续约问题与集群同步延迟问题。总的来说,是一个不太靠谱的方案。如果追求高正确率,不能采用这种方案。

但是它也有优点,就是比较简单,在某些非严格要求的场景是可以使用的,比如社交系统一类,交易系统一类不能出现重复交易则不建议用。

到此这篇关于redis分布式锁及会出现的问题解决的文章就介绍到这了,更多相关redis分布式锁内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


联系我们
返回顶部