Redis里那些死锁的关键点到底是啥,四个条件你得知道才行
- 问答
- 2026-01-18 06:48:42
- 4
你问Redis里的死锁关键点是啥,还特别提到四个条件,这确实是理解这个问题的核心,Redis本身作为一个单线程(核心内存操作是单线程)的服务器,在绝大多数场景下是不会出现传统意义上那种“死锁”的,因为所有的命令都是排着队一个一个执行的,前一个命令没执行完,后一个命令就得等着,根本不存在多个线程互相抢资源然后卡住的情况,这就好比只有一个收银台的超市,大家老老实实排队,不可能出现两个顾客同时抢着付钱又把对方卡住的情形。
别以为这就高枕无忧了,当我们说“Redis死锁”时,通常指的是客户端层面或者分布式锁应用场景中出现的类似死锁的现象,这时候,问题就不在Redis服务器内部,而在于我们如何使用它,这时候,传统操作系统里的死锁四个必要条件(来源:计算机操作系统经典理论,由Coffman等人提出)就派上用场了,我们可以用它来当镜子,照一照我们在用Redis时容易掉进去的坑。
第一个条件:互斥条件。 这个条件是说,一个资源每次只能被一个进程(或客户端)使用,这在Redis里是天生的,尤其是当你使用SETNX(SET if Not eXists)命令或者带有NX参数的SET命令去实现一个锁的时候,你设置了一个键,比如lock:order_123,这个键在某一时刻只能被一个客户端成功设置,其他客户端再来设置都会失败,互斥性是我们要实现锁的根本目的,所以这个条件是我们主动创造的,是基础,但它也是通往死锁之路的起点。

第二个条件:请求与保持条件。 意思是说,一个进程(客户端)在持有至少一个资源(锁)的同时,又试图去请求另一个被其他进程占有的资源(另一把锁),在复杂的业务逻辑里,这太常见了,你的程序需要同时锁定两个资源才能完成一个操作,假设有个业务要先扣减商品A的库存,再扣减商品B的库存,你的代码可能先成功锁定了商品A(拿到了lock:stock_A),然后紧接着去尝试锁定商品B(lock:stock_B),如果这时候,正好有另一个程序,它以相反的顺序操作,先锁定了商品B,然后来请求商品A的锁,那么噩梦就开始了。
第三个条件:不剥夺条件。 这是指进程已经获得的资源,在未使用完之前,不能被其他进程强行夺走,只能由该进程自己释放,在Redis锁的典型用法里,除非你设置了过期时间,否则一旦一个客户端拿到了锁,在它主动用DEL命令删除这个锁键之前,这个锁会一直归它所有,其他客户端只能干等着,没有任何办法能强行把这个锁“抢”过来,这就为死锁的最终形成创造了关键环境,如果持有锁的客户端因为某种原因(比如程序崩溃、网络中断、发生了Full GC导致进程卡住)一直不释放锁,那其他等待的客户端就会永远等下去,这就是一种死锁状态。

第四个条件:循环等待条件。 这就像上面第二个条件里举的那个例子,客户端1拿着锁A,等着锁B;客户端2拿着锁B,等着锁A,两个人你等我、我等你,形成了一个闭环,谁都进行不下去,也释放不了自己手里的锁,这个循环链一旦形成,死锁就实实在在地发生了。
你看,虽然Redis单线程服务器本身不死锁,但我们这些程序员通过客户端操作,完全有可能“人造”出满足以上四个条件的场景,关键点到底在哪呢?其实就在于我们如何打破这四个条件中的一个或多个,既然互斥条件是我们需要的不能打破,那我们就从另外三个下手:

-
打破“请求与保持”:这有点难,因为业务逻辑可能就需要同时持有多个锁,但我们可以尝试优化设计,比如对所有需要加锁的资源定义一个全局的、严格的顺序,无论哪个客户端,无论执行什么业务,都必须按照这个顺序(比如按资源ID从小到大)去申请锁,这样就能避免出现循环等待,在上面的例子里,如果规定必须先锁A再锁B,那么客户端2就不会先去锁B了,循环就被打破了。
-
打破“不剥夺”:这是最常用、最有效的办法,就是给你设置的锁加一个过期时间!通过
SET lock_name random_value NX PX 30000这样的命令,在获取锁的同时设置一个30秒的过期时间,这样,即使持有锁的客户端崩溃了,顶多30秒后,Redis也会自动把这个锁删除,让其他客户端有机会获得锁,从而避免系统永久死锁,这是Redis分布式锁实践中的金科玉律。 -
打破“循环等待”:如上所述,通过规定申请顺序,可以从根源上防止循环等待的发生。
还有一个非常重要的关键点来自于客户端的行为:设置合理的锁超时时间后,还要确保业务的执行时间不能超过锁的超时时间,否则锁会提前失效,导致数据不一致,要保证释放锁的原子性,即只能用自己设置的随机值(random_value)去删除锁,避免误删其他客户端的锁,这通常需要Lua脚本来保证原子性操作。
Redis场景下的“死锁”关键点在于:我们在客户端应用中,由于不当的资源(锁)申请顺序和缺乏超时释放机制,人为地创造了死锁的四个必要条件,解决问题的核心思路就是为锁引入超时机制(打破不剥夺条件) 和规范资源的申请顺序(打破循环等待条件),理解了这四个条件,你就能像拿着解剖刀一样,精准地分析和避免Redis使用中遇到的各类“卡住”的问题。
本文由瞿欣合于2026-01-18发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/82886.html
