用Redis红锁搞定分布式竞争,真没那么复杂你试试吧
- 问答
- 2026-01-10 04:23:30
- 2
程序员济癫公众号文章《用Redis红锁搞定分布式竞争,真没那么复杂你试试吧》)
好的,咱们就直接开讲,今天聊的这个东西,听起来挺唬人,叫“Redis红锁”,但说白了,它就是用来解决一个特别实际的问题:在分布式系统里,好多个服务实例都想干同一件事的时候,怎么保证只有一个能干成,别的都得乖乖排队等着。
你想啊,比如有个秒杀活动,库存就100件,结果你有10台服务器同时处理下单请求,要是没有个靠谱的机制,很可能这10台服务器一检查,哎,库存都还有啊,咔咔”一顿操作,卖了1000件出去,这不就超卖了吗?直接破产的节奏,再比如,你有个定时任务,比如每天凌晨给用户发通知,你部署了3个节点,要是不加控制,每个节点都跑一遍,用户就得收到3条一模一样的消息,不烦死才怪。
我们需要一把“锁”,这把锁必须是分布式的,大家都认,单机环境下,我们用Java里的synchronized或者ReentrantLock就行,但那只能管住自己进程里的线程,管不了网络另一边另一台机器上的服务,我们得找一个大家都能访问的地方来存这把锁,Redis,因为速度快、单线程命令原子性,自然就成了首选。
最早大家是怎么做的呢?很简单,就是用一个Redis命令,比如SETNX(SET if Not eXists),意思是,如果这个key不存在,我就设置它,返回1表示成功;如果已经存在,我就不设置,返回0表示失败,这就像你去公共厕所,看见门把手上挂着“有人”的牌子(key存在),你就得等着;如果没牌子(key不存在),你就挂上牌子进去,成功抢占。
但这么搞,问题一大堆,你设置好锁之后,业务代码执行时间太长,超过了锁的过期时间,锁自动释放了,这时候第二个服务一看,没锁了,它就加锁成功进去了,结果呢,第一个服务干完活,顺手又把锁一删(它删的其实是第二个服务的锁),这下全乱套了,这就是为什么后来有了个标准用法:设置锁的时候,value用一个唯一的随机值(比如UUID),删除的时候,先比对这个值是不是自己设置的,是才删,这就像你挂的牌子有个特殊记号,你出来的时候,只摘掉有你自己记号的牌子,别人的你别动。
但这还不够,万一Redis是单机的,它宕机了,锁就全没了,整个系统就不可靠了,就得搞主从、搞集群,但主从同步有延迟啊,可能你在主节点上加锁成功了,数据还没同步到从节点,主节点“啪叽”宕机了,从节点顶上来,但上面没你的锁,别的服务又能加锁了,又会出现两个服务同时干一件事的情况。 来源:程序员济癫公众号文章《用Redis红锁搞定分布式竞争,真没那么复杂你试试吧》)

这时候,Redis红锁(Redlock)就出场了,它的核心思想特别直观:别把鸡蛋放在一个篮子里,我不指望一个Redis实例绝对可靠,我搞多个(比如5个)独立的Redis主节点(注意,是主节点,不是主从,这些节点之间相互独立,避免同时宕机)。
红锁的算法步骤,说白了就是三步:
第一步,抢锁,客户端用同样的key和那个唯一的随机值,依次向这5个Redis实例发送加锁命令,它有个超时时间,比如总共允许5秒内完成所有操作,防止某个Redis实例响应慢把整个流程拖死,如果客户端在大多数实例上(比如5个里面成功设置了3个以上)都加锁成功,并且总耗时没超过超时时间,那就算它整体加锁成功,反之,如果成功数没过半,或者超时了,就算失败。
第二步,干活,加锁成功了,你就去执行你的核心业务逻辑,比如扣减库存。

第三步,释放锁,干完活,客户端再向所有Redis实例(包括那些当初加锁失败的)发送删除命令,删除那个带唯一值的锁。
你琢磨一下,这招为啥更靠谱?因为它容忍了部分节点故障,就算有一两个Redis实例宕机了,只要大多数(3个)是好的,并且你成功拿到了它们上面的锁,你这个锁就是有效的,因为同时宕掉3个的概率,总比宕掉1个主从集群的概率小得多吧?这就大大提高了可靠性。
红锁也不是完美的银弹,它也有争议,比如有人会争论机器时钟不一致会不会出问题(所以官方建议别用自动过期,而是自己设置一个较短的超时时间),但在大多数实际场景下,它已经比单Redis实例或者简单的主从模式要可靠太多了。 来源:程序员济癫公众号文章《用Redis红锁搞定分布式竞争,真没那么复杂你试试吧》)
实现起来也不复杂,你不用自己从头写,像Java里用Redisson这个客户端,它已经把红锁封装好了,你几行代码就能用,大概长这样:
// 创建多个Redis客户端配置
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://192.168.1.10:6379");
// ... 类似创建config2, config3, config4, config5
// 用这些配置创建RedissonClient实例
RedissonClient client1 = Redisson.create(config1);
// ... 类似创建client2, client3, client4, client5
// 把客户端实例放进一个List
List<RLock> locks = new ArrayList<>();
locks.add(client1.getLock("myLock"));
locks.add(client2.getLock("myLock"));
// ...
// 创建红锁
RLock redLock = new RedissonRedLock(locks.toArray(new RLock[0]));
try {
// 尝试加锁,waitTime是等待时间,leaseTime是锁的持有时间
boolean isLocked = redLock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
// 恭喜你,抢到锁了,放心大胆地干你的核心业务吧
doSomethingCritical();
}
} catch (InterruptedException e) {
// 处理异常
} finally {
// 无论如何,最后都要释放锁
redLock.unlock();
}
你看,其实真没多复杂,核心思想就是“少数服从多数”,用多个独立实例的多数派来保证锁的可靠性,下次你的分布式系统里遇到那种“只能干一次”的竞争问题时,别犹豫,试试红锁这个方案,它可能不是理论上百分百无懈可击的,但在工程实践上,足够应对绝大多数场景了,能让你睡个安稳觉,关键是,理解了这个思路,你就掌握了解决分布式竞争问题的一把利器。
本文由帖慧艳于2026-01-10发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/77847.html
