Redis中的分布式锁
目录
基于Redis的SetNx命令
实现:
为什么要判断锁是否是本线程加上的(为什么要引入校验id)?
为什么要使用用Lua脚本来完成(为什么要引入Lua)?
问题
基于Redisson
Redisson可重入锁原理
1.获取锁的逻辑
2.释放锁的逻辑
超时续约机制(看门狗机制)
Redisson分布式锁的数据未同步导致线程安全
主从问题:
解决方案:
Redlock 算法的具体过程
基于Redis的SetNx命令
SetNX 如果key已存在,则结果返回0。如果key不存在,则结果返回1。
实现:
1.加锁:set unique_key 机器码+线程ID NX EX TTL (unique_key 一般是业务唯一标识)
- 加锁成功:执行命令结果返回1
- 加锁失败:执行命令结果返回0
- 能实现互斥且设置了TTL,可以通过TTL到期删除key,自动释放锁,防止死锁情况
2.释放锁:执行 del 删除 key 的命令。
a.先 Get key 拿到 value 判断当前锁是否是本机器本线程加上的
- 若是,可以正常删除锁
- 若不是,则不需要处理
为什么要判断锁是否是本线程加上的(为什么要引入校验id)?
是为了防止锁误删的情况
举例: 线程1业务阻塞,锁的TTL到期,Redis key 自动删除,则锁释放,然后线程2获取到了锁。在线程2正常执行时,线程1恢复运行,如果线程1不判断当前锁是否是自己加上的,直接把锁删除了,就出现问题——线程1误删了线程2的锁。
为什么要使用用Lua脚本来完成(为什么要引入Lua)?
因为判断是否是当前线程的锁 + 删除key释放锁 两个操作,这两个操作如果不是原子的就会出现如图片上的问题,使用Lua脚本,使得这两个操作变为原子性的

问题
- 由于业务执行耗时太长,TTL到期,锁被迫释放,存在多个线程并行的情况
- SetNX 线程不可重入

-
TTL不好设置,业务到底要执行多长时间,我们不太确定(这个可以引入看门狗解决)
基于Redisson
Redisson 是市面上成熟的分布式锁组件,相对于用 SetNx 命令实现的分布式锁更加强大和完善。
Redisson可重入锁原理
即一个线程可以多次获取锁。利用Hash结构,记录获取锁的线程和获取锁的次数
1.获取锁的逻辑
a.判断 key(unique_value一般是业务唯一标识) 是否存在,即判断是否有线程持有锁
- 如果key不存在,代表没有线程持有锁,则获取锁,即设置一个Hash结构的 (field,value),field是机器码+线程ID,value是 次数 1
- 如果key存在,代表有线程持有锁,此时判断持有锁的线程是否是当前线程,如果是当前线程,则重入次数+1。
2.释放锁的逻辑
a.重入次数-1,如果重入次数为0,删除key,释放锁。
超时续约机制(看门狗机制)
为了解决TTL不好设置,如果分布式锁的key设置了TTL,又因为业务阻塞原因,导致当前线程的业务未完成,TTL到期了,导致被迫释放锁,此时就会有其他线程抢占锁,导致有线程并行执行,违背了加分布式锁的初心。
当 Redisson 获取锁没有指定锁的TTL时,默认会使用超时续约机制(看门狗WatchDog)机制。
续期规则:
- 默认 TTL 为 30 秒,后台线程(定时任务)每隔 10 秒 检查业务是否完成。
- 若未完成 → 通过 Lua 脚本重置锁 TTL(续期至 30 秒),保证锁不被释放,等到业务执行完。
Redisson分布式锁的数据未同步导致线程安全
主从问题:
- 主节点宕机时,若从节点未同步 锁 数据 (就是主节点还没来得及把数据给同步到从节点,就挂了,导致加锁失败)→ 新主节点可能允许其他线程重复加锁 → 锁失效。
解决方案:
- 此时需要部署多个 Redis 主节点,多主多从架构,向多个主节点都去获取锁
- MultiLock 联锁方案:要求 所有主节点加锁成功,否则立即失败并释放已获取的锁(要么全部成功要么全部失败)
- RedLock 红锁方案:要求 半数主节点以上(如 3/5)加锁成功,否则立即失败并释放已获取的锁。
Redlock 算法的具体过程
引⼊⼀组 Redis 节点. 其中每⼀组 Redis 节点都包含⼀个主节点和若⼲从节点. 并且组和组之间存 储的数据都是⼀致的, 相互之间是 "备份" 关系(⽽并⾮是数据集合的⼀部分, 这点有别于 Redis cluster). 加锁的时候, 按照⼀定的顺序, 写多个 master 节点. 在写锁的时候需要设定操作的 "超时时间". ⽐如 50ms. 即如果 setnx 操作超过了 50ms 还没有成功, 就视为加锁失败.

同理, 释放锁的时候, 也需要把所有节点都进⾏解锁操作. (即使是之前超时的节点, 也要尝试解锁, 尽量保 证逻辑严密)
Redlock 算法的核⼼就是, 加锁操作不能只写给⼀个 Redis 节点, ⽽要写个多个!! 分布式系统中任何⼀个节点都是不可靠的. 最终的加锁成功结论是 "少数服从多数的"
