# Redis 实现分布式锁

# 0. 分布式锁使用场景

一般我们使用分布式锁有两个场景:

  • 效率:使用分布式锁可以避免不同节点重复相同的工作,这些工作会浪费资源。比如用户付了钱之后有可能不同节点会发出多封短信。

  • 正确性:加分布式锁同样可以避免破坏正确性的发生,如果两个节点在同一条数据上面操作,比如多个节点机器对同一个订单操作不同的流程有可能会导致该笔订单最后状态出现错误,造成损失。

Redis 因为其性能好,实现起来分布式锁简单,所以让很多人都对基于 Redis 实现的分布式锁十分青睐。

# 1. 实现原理

Redis 实现『分布式锁』功能的原子操作主要是 SETEXPIRE 操作,从 Redis 的 2.6.x 版本开始,其提供的 SET 命令格式如下:

SET <key> <value> [EX seconds] [PX milliseconds] [NX | XX]

EX 值的是 key 的存活时间,单位为秒。PXEX 作用一样,唯一的不同就是后者的单位是微秒(使用较少)

NXXX 作用是相反的。NX 表示只有当 key『不存在时』才会设置其值;XX 表示当 key 存在时才设置 key 的值。

对于使用 NX 选项的 SET 命令,Redis 提供了一个别名命令:SETNX

在使用 SETNX 操作实现分布式锁功能时,需要注意以下几点:

  • 这里的『锁』指的是 Redis 中的一个认为约定的键值对。谁能创建这个键值对,就意味着谁拥有这整个『锁』。

  • 使用 SETNX 命令获取『锁』时,如果操作返回结果是 0(表示 key 已存在,设值失败),则意味着获取『锁』失败(该锁被其它线程先获取),反之,则设值成功,表示获取『锁』成功。

    • 如果这个 key 不存在,SETNX 才会设置该 key 的值。此时 Redis 返回 1 。

    • 如果这个 key 存在,SETNX 则不会设置该 key 的值。此时 Redis 返回 0 。

  • 为了防止其它线程获得『锁』之后,有意或无意,长期持有『锁』而不释放(导致其它线程无法获得该『锁』)。因此,需要为 key 设置一个合理的过期时间。

  • 当成功获得『锁』并成功完成响应操作之后,需要释放『锁』(可以执行 DEL 命令将『锁』删除)

在代码层面,与 Setnx 命令对应的接口是 ValueOperations 的 setIfAbsent 方法。

# 2. 工具类

见其它笔记。