文章

如何使用 Redis 实现分布式锁?

如何使用 Redis 实现分布式锁?

Redis 实现分布式锁,需要考虑如下几个方面:

1、正确的获得锁

set 指令附带 nx 参数,保证有且只有一个进程获得到。

2、正确的释放锁

使用 Lua 脚本,比对锁持有的是不是自己。如果是,则进行删除来释放。

3、超时的自动释放锁

set 指令附带 expire 参数,通过过期机制来实现超时释放。

4、未获得到锁的等待机制

sleep 或者基于 Redis 的订阅 Pub/Sub 机制。

一些业务场景,可能需要支持获得不到锁,直接返回 false ,不等待。

5、【可选】锁的重入性

通过 ThreadLocal 记录是第几次获得相同的锁。

1)有且第一次计数为 1 && 获得锁时,才向 Redis 发起获得锁的操作。2)有且计数为 0 && 释放锁时,才向 Redis 发起释放锁的操作。

6、锁超时的处理

一般情况下,可以考虑告警 + 后台线程自动续锁的超时时间。通过这样的机制,保证有且仅有一个线程,正在持有锁。

7、Redis 分布式锁丢失问题

具体看「方案二:Redlock」。

下面,我们来详细说下每个方案。

🦅 方案一:set 指令

先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。

  • 这时候对方会告诉你说你回答得不错,然后接着问如果在 setnx 之后执行 expire 之前进程意外 crash 或者要重启维护了,那会怎么样?
  • 这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得 set 指令有非常复杂的参数,这个应该是可以同时把 setnx 和 expire 合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

所以,我们可以使用 set 指令,实现分布式锁。指令如下:

plain text SET key value [EX seconds] [PX milliseconds] [NX|XX]

可以使用 SET key value EX seconds NX 命令,尝试获得锁。具体的实现,可以参考如下文章:

  • 《精尽 Redisson 源码分析 —— 可重入分布式锁 ReentrantLock》
  • 《Redis 分布式锁进化史解读 + 缺陷分析》
  • 《Redis 分布式锁的正确实现方式(Java 版)》

🦅 方案二:Redlock

set 指令的方案,适合用于在单机 Redis 节点的场景下,在多 Redis 节点的场景下,会存在分布式锁丢失的问题。所以,Redis 作者 Antirez 基于分布式环境下提出了一种更高级的分布式锁的实现方式:Redlock 。

具体的源码解析,可以看看 《精尽 Redisson 源码分析 —— 可靠分布式锁 RedLock》 文章。

具体的方案,胖友可以看看老友飞哥的两篇博客:

  • 《Redlock:Redis分布式锁最牛逼的实现》
  • 《Redisson 实现 Redis 分布式锁的 N 种姿势》

最近艿艿画了一个 Redisson 实现分布式锁的流程图,胖友可以点击传送门阅读。

🦅 对比 Zookeeper 分布式锁

  • 从可靠性上来说,Zookeeper 分布式锁好于 Redis 分布式锁。
  • 从性能上来说,Redis 分布式锁好于 Zookeeper 分布式锁。

所以,没有绝对的好坏,可以根据自己的业务来具体选择。如果想要更简单,甚至可以考虑基于 MySQL 行锁来实现分布式锁。

本文由作者按照 CC BY 4.0 进行授权