当前位置:首页 > 问答 > 正文

Redis锁到底会不会阻塞啊,感觉大家说法不太一样,到底是阻塞还是非阻塞呢

关于Redis锁会不会阻塞这个问题,感觉大家说法不一样,主要是因为“阻塞”这个词在不同的语境下有不同的含义,而且Redis锁本身也有不同的实现方式,我们不能简单地用一个“是”或“否”来回答,需要分情况来看。Redis锁本身的核心机制是非阻塞的,但我们在使用它时,为了实现特定的业务目标,经常会采用一种“循环重试”的策略,这种策略在效果上就表现为“等待”,也就是我们感觉上的“阻塞”。

下面我们来详细拆解一下。

第一种情况:单次尝试获取锁——这是非阻塞的

这是最基础的情况,我们来看一个最简单的Redis锁命令:SET lock_key unique_value NX PX 30000,这个命令的意思是,尝试设置一个键为lock_key的锁,只有在这个键不存在(NX选项)的时候才设置成功,并且给这个锁设置30秒的过期时间(PX 30000)。unique_value是一个唯一值,比如UUID,用来确保只有锁的持有者才能释放它,避免误删别人的锁。

假设线程A先执行了这个命令,成功设置了锁,紧接着,线程B也来执行完全相同的命令,会发生什么呢?

线程B的命令也会发往Redis服务器,Redis服务器会检查lock_key是否存在,因为此时线程A已经创建了这个键,所以Redis会告诉线程B:SET操作失败了,锁获取不成功。

这个过程是瞬间完成的,线程B发出命令,几乎立刻就能收到一个结果(成功或失败),线程B并没有傻傻地在那里“等”锁被释放,它只是进行了一次尝试,然后立刻得到了失败的回应,从线程B的角度看,它的执行流程并没有被暂停,它可以立刻决定接下来该怎么做:是直接返回给用户“系统繁忙”的提示,还是去执行别的逻辑。这种一次性的尝试行为,是典型的非阻塞操作。 很多资料在说Redis锁是非阻塞的时候,指的就是这个最底层的原子命令操作。

第二种情况:循环重试获取锁——这在效果上是阻塞的

在大多数实际业务场景中,当一个线程发现锁已经被别人占有时,我们通常不希望它直接放弃,用户在秒杀商品,或者提交一个重要订单,如果仅仅因为一瞬间的锁冲突就返回失败,用户体验会非常差,我们更希望这个请求能“等一等”,等到锁释放了再继续执行。

我们就需要在代码逻辑中引入一个循环,伪代码大概是这样的:

while (true) {
    result = redis.set("lock_key", uuid, "NX", "PX", 30000)
    if (result == success) {
        break; // 获取锁成功,跳出循环
    } else {
        // 获取锁失败,休息一小段时间(比如100毫秒)再试
        Thread.sleep(100);
    }
}
// 跳出循环后,执行需要加锁的业务代码

你看,线程B虽然每一次尝试获取锁的操作是非阻塞的,但它陷入了一个循环里,在这个循环中,它不断地“尝试-失败-等待-再尝试”,从线程B的宏观行为来看,它并没有继续执行后续的业务逻辑,而是卡在了这个循环里,直到某一次尝试成功为止。这种行为,从我们程序员的视角和用户的感知上来看,这个线程就是被“阻塞”住了,它在等待一个资源(锁)可用。

当很多文章和开发者说Redis锁是阻塞锁的时候,他们指的是这种通过循环重试机制实现的、在应用层表现出的阻塞效果,这其实是分布式锁的一个常见模式,有时也被称为“自旋锁”。

总结一下矛盾的来源:

  1. 技术实现层面:Redis锁基于的SET NX命令是非阻塞的原子操作,这是从单个命令的语义和网络IO的角度来看的。
  2. 业务应用层面:为了确保业务逻辑最终能被执行,我们通常会包装一个循环重试逻辑,这使得获取锁的线程在效果上表现为阻塞等待。

双方的说法都有道理,只是所处的层次和角度不同。

还有一个重要的点会影响“阻塞”的体验:锁的过期时间。

即使我们采用了循环重试,如果占有锁的线程因为某种原因(比如GC停顿、机器宕机、网络问题)没有主动释放锁,那么其他线程岂不是要永远等下去?这就是死锁风险,为了解决这个问题,我们一定要给锁设置一个过期时间(就像上面命令中的PX 30000)。

这样,即使线程A崩溃了,最多30秒后,Redis也会自动删除这个锁,让其他线程有机会获取到,这相当于给“阻塞”等待设置了一个最长时限,避免了无限期的阻塞,如果没有这个过期时间,一旦发生异常,整个系统可能真的会陷入永久阻塞的灾难性局面。

最终的结论是:Redis锁的底层操作是非阻塞的,但通过配合循环重试逻辑,它可以实现一种带有超时机制的、应用层面的阻塞效果,以满足我们实际的业务需求。 我们平常说“用Redis实现一个阻塞锁”,指的就是后面这种模式,下次再听到不同的说法时,你就可以从这两个层面去理解他们真正想表达的意思了。

Redis锁到底会不会阻塞啊,感觉大家说法不太一样,到底是阻塞还是非阻塞呢