用Redis搞分布式锁其实没那么难,聊聊那些坑和套路
- 问答
- 2025-12-24 01:31:05
- 2
主要参考自互联网技术社区常见讨论,特别是开发者博客如阿里云栖社区、开源中国OSChina、个人技术博客如crossoverJie等,以及Redis官方文档中关于分布式锁的Redlock算法部分)
用Redis实现分布式锁,听起来挺高大上,其实核心思想很简单,想象一下,在一个分布式系统里,多个服务实例要同时操作一个共享资源,比如秒杀场景下扣减库存,或者同时只能有一个任务执行器去跑一个定时任务,这时候就需要一把“锁”,让这些分散在不同机器上的服务能像单机程序那样,排着队一个一个来,避免数据错乱,Redis因为速度快、单线程特性(命令执行是串行的),自然就成了实现这把锁的热门选择。
最基础的实现方法,大家可能都知道,就是用Redis的SET命令,配合NX和PX参数,简单说就是:SET lock_key my_random_value NX PX 30000,这条命令的意思是,只有当lock_key不存在时(NX),才设置它的值为my_random_value,并且给这个键设置一个30秒的过期时间(PX 30000),如果设置成功了,就表示当前客户端成功抢到了锁;如果返回nil,说明锁已经被别人占着,你就得等着或者放弃。
你看,代码写起来可能就几行,感觉一点都不难对吧?但坑往往就藏在这些看似简单的步骤后面,下面我们就来聊聊那些最容易踩的坑和常见的套路。
第一个大坑:死锁。 你可能会想,我设置了过期时间,怎么会死锁呢?问题出在极端情况下,客户端A成功加锁,开始处理业务逻辑,处理的时间可能超过了我们设定的30秒过期时间,这时候,Redis会自动把锁释放掉,紧接着,客户端B看到锁没了,就成功加锁了,客户端A处理完业务,它要去释放锁了,如果它直接用的是DEL lock_key,那么它删除的其实是客户端B刚刚创建的锁!这就乱套了。套路一来了:锁的值必须是一个全局唯一的随机数(比如UUID),在释放锁的时候,要先判断一下当前锁的值是不是自己当初设置的那个随机数,如果是,才能删除,这个“判断+删除”的操作必须是原子的,所以要用Lua脚本来执行,不然,在判断之后、删除之前,锁可能又过期被别的客户端抢走了,还是会误删。
第二个大坑:锁提前过期,导致多个客户端同时持有锁。 这就是上面提到的场景,客户端A的业务逻辑执行时间超过了锁的过期时间,导致锁失效,客户端B趁机加锁成功,这时候,系统里实际上有两个客户端都认为自己持有锁,都在操作共享资源,为了解决这个问题,套路二是:合理设置锁的超时时间,这个时间不能太短,必须确保比你业务逻辑执行的最大可能时间还要长,但设置太长也不行,万一客户端崩溃了,锁要等很久才能自动释放,会影响系统可用性,一个常见的做法是,启动一个额外的“看门狗”线程,在业务逻辑执行期间,定期(比如在过期时间的三分之一时)去给锁续期,重置过期时间,这招比较高级,实现起来也更复杂。
第三个大坑:Redis节点宕机带来的问题。 如果我们只用一台Redis服务器(单实例),万一这台机器宕机了,那整个锁服务就不可用了,为了高可用,大家会想到用Redis哨兵(Sentinel)或者集群(Cluster)模式,但这又引入了新问题,比如在哨兵主从切换的时候:客户端A向主节点申请锁,主节点把锁写成功了,但在把数据同步给从节点之前,主节点宕机了,哨兵选举了一个从节点成为新的主节点,但这个新主节点上并没有客户端A的锁数据,这时,客户端B向新主节点申请锁,也能成功!这就又出现了两个客户端同时持有锁的情况,这个问题是单Redis实例和主从模式无法彻底解决的。
为了解决这个更棘手的可用性问题,Redis的作者Antirez提出了一个叫做Redlock的算法(套路三),这个算法的思想是,你不用只依赖一个Redis实例,而是同时向多个(比如5个)独立的Redis主节点(注意,不是主从集群,是多个完全独立、没有关联的Redis实例)发起加锁请求,只有当超过半数的节点(比如3个)都加锁成功,并且总耗时小于锁的过期时间,才算真正加锁成功,释放锁时,也要向所有节点发起释放请求,Redlock通过增加节点数,降低了单个节点故障导致锁失效的概率,但它也更重、更复杂,而且对于它是否绝对安全,业界也一直有争论(比如在发生网络分区或节点时钟不同步等极端情况下)。
用Redis搞分布式锁,入门确实不难,一个SET NX PX命令加一个Lua脚本释放就能搞定大部分简单场景,但要想在生产环境中用得稳,就必须深刻理解并处理好这三个核心问题:避免误删(用随机值+Lua脚本)、防止锁提前失效(合理超时或看门狗)、应对Redis节点故障(单点风险或采用Redlock等更复杂的方案),没有一劳永逸的银弹,选择哪种方案,取决于你的业务对数据一致性的要求到底有多高,以及你愿意为这种一致性付出多少复杂性和性能上的代价。

本文由符海莹于2025-12-24发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/67254.html
