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

Redis锁到底有多少种形式和分类,搞清楚这些你才不会乱用

Redis锁,看起来简单,就是用个命令设个值,但真要细究起来,里面的门道可多了,用错了轻则性能受损,重则数据出错,要搞清楚怎么选、怎么用,首先得明白Redis锁到底有多少种花样。

从最基础的“是否阻塞”来分:非阻塞锁与阻塞锁

这是最直观的分类方式,知乎专栏“三太子敖丙”在讲分布式锁时提到,最原始的锁想法就是“占坑”,非阻塞锁,顾名思义,就是尝试一次,成功了就拿到锁,失败了就立刻返回失败,告诉客户端“对不起,资源正忙,请稍后再试”,这通常通过Redis的SETNX(SET if Not eXists)命令实现,它的优点是逻辑简单,不会导致请求堆积,缺点是如果锁被长期占用,客户端需要自己实现重试逻辑,比如循环去抢,这被称为“自旋”,会消耗不必要的CPU和网络资源。

而阻塞锁则更符合我们对“锁”的常规认知,就像一把真正的锁,拿不到的时候我就等着,掘金上的一篇高赞文章指出,单纯的Redis命令无法实现真正的阻塞,需要客户端配合,常见的做法是,在获取锁失败后,订阅Redis的一个频道,然后进入等待,当锁被释放时,通过Pub/Sub机制通知所有等待的客户端,让它们醒来重新竞争锁,Redisson这样的客户端库就内置了这种机制,使得获取锁的方法可以一直阻塞,直到成功或超时,这种锁的好处是避免了客户端的无效轮询,但实现复杂度高,对Redis服务器的连接压力也更大。

从锁的“安全性”与“可靠性”核心来分:非可重入锁与可重入锁

这是区分“玩具锁”和“生产级锁”的关键,很多初学者自己写的Redis锁都是非可重入的,什么叫可重入?Redisson官方文档用一个生动的例子解释:假设一个方法A内部调用了方法B,而A和B都需要获取同一把锁,如果线程已经持有了A的锁,在进入B时,如果锁是可重入的,那么该线程可以再次成功获取锁;如果是非可重入的,那么线程在B这里就会卡住,形成死锁。

自己用SETNX实现的简单锁,通常就是非可重入的,因为它只判断key是否存在,而不管是谁持有的,生产环境中,一个复杂的业务逻辑很可能出现多层调用都需要同一把锁的情况,所以可重入是分布式锁的一个几乎必备的特性,实现可重入,通常需要在锁的值(value)上做文章,不仅要存一个随机值作为锁的标识,还要记录该线程重入的次数,Redisson就是用Hash结构来存储锁的客户端ID和重入计数。

从锁的“架构模式”来分:单机锁与多机锁(红锁)

这个分类直接关系到锁的可靠性,单机锁是指锁只存在于一个Redis实例上,知乎“三太子敖丙”的文章明确指出,这种锁在Redis主从架构下存在致命风险:如果客户端在Master上拿到了锁,但锁数据还没同步到Slave时,Master宕机了,Slave被提升为新的Master,此时另一个客户端就能从新Master上获取到同一把锁,导致锁失效,数据出现混乱。

为了解决这个问题,Redis的作者提出了一个叫做RedLock的算法,由此实现的锁就是多机锁,也叫红锁,掘金的技术博文详细描述过其原理:它不依赖单个Redis实例,而是要求客户端依次向多个(通常是5个)独立的Redis主节点申请锁,只有当超过半数的节点(比如3个)都成功获取锁,并且总耗时小于锁的过期时间,才算真正获取成功,这样,即使个别节点宕机,只要大多数节点存活,锁就是安全的,RedLock的实现非常复杂,性能开销也大,而且其安全性在业界仍有争议,所以通常只在极端要求可靠性的场景下使用。

从锁的“自动释放”机制来分:非公平锁与公平锁

这关乎等待锁的客户端的“排队”顺序,我们平时用的最多的是非公平锁,Redisson的文档里提到,其内置的锁默认就是非公平的,意思是,当锁被释放,所有等待的客户端会同时收到通知,然后一起冲向Redis去抢锁,谁的网络好、手速快,谁就能抢到,这种方式吞吐量高,但可能存在“饥饿”现象,即某个客户端永远抢不到锁。

公平锁则像现实生活中排队,先来后到,第一个等待的客户端拥有优先获取锁的权利,实现公平锁更复杂,通常需要维护一个等待队列,Redisson也提供了公平锁的实现,它通过Redis的列表结构来模拟队列,确保等待时间最长的客户端最先被唤醒,公平锁保证了公平性,但性能通常低于非公平锁。

其他特殊形式的锁

除了以上主流分类,还有一些为特定场景设计的锁。

  • 读写锁:Redisson文档中强调,读写锁允许多个读锁同时存在,但写锁是排他的,这在读多写少的场景下能极大提升并发性能。
  • 信号量:它不保护具体资源,而是限制同时访问某个资源的线程数量,只允许10个线程同时进行某个操作。
  • 闭锁:类似于Java中的CountDownLatch,用于等待其他线程完成一系列操作后再执行。

Redis锁的分类是多维度的,一个生产环境可用的锁,比如Redisson提供的,往往是可重入的、阻塞的、非公平的单机锁,而是否需要升级到红锁取决于你对数据一致性的要求有多苛刻,是否需要公平锁则取决于业务对公平性的敏感度,搞清楚这些分类和它们各自的适用场景,你才能在选择时不再迷茫,避免因为用错锁而带来的各种坑。

Redis锁到底有多少种形式和分类,搞清楚这些你才不会乱用