Redis分布式锁

1. 什么是分布式锁

在一个分布式系统中,也会涉及到多个节点同时访问一个公共资源的情况,此时就需要通过锁来做互斥控制,避免出现类似于"线程安全"的问题

java中的 synchronized 这样的锁只能在当前的进程中生效,在分布式这种多个进程多个主机的场景下就无能为力了,此时就需要使用到分布式锁

2. 分布式锁的基础实现

思路很简单,本质上是通过一个键值对来标识锁的状态

所谓的分布式锁也是一个/一组单独的服务器程序,给其他的服务器提供"加锁"这样的业务

想要执行业务代码的时候就需要先访问Redis,在Redis上设置一个键值对

如果这个操作设置成功,就视为当前没有被锁,可以执行,执行完后将键值对删除

如果设置键值对时已经存在就说明"加锁失败"",需要等待或放弃

3. 引入过期时间

当服务器加锁之后开始执行业务,如果服务器1意外宕机了,就会导致解锁操作不能执行,就可能引起其他服务器始终无法获取到锁的情况

为了解决这个问题,可以在设置key的同时引入过期时间,即这个锁最多持有多少就应该被释放

只能使用 set ex nx,如果分开多个操作,由于Redis的多个指令之间不存在关联,并且即使使用了事务也不能保证这两操作一定都成功

4. 引入校验id

对于Redis中写入的加锁键值对,其他节点也是可以删除的

为了解决上述问题,可以引入一个校验id

在删除key的时候,先校验当前删除key的服务器是否是当初加锁的服务器,如果是才可以删除

复制代码
String key = [要加锁的资源 id];
String serverId = [服务器的编号];

// 加锁, 设置过期时间为 10s
redis.set(key, serverId, "NX", "EX", "10s");

// 执⾏各种业务逻辑, ⽐如修改数据库数据. 
doSomeThing();

// 解锁, 删除 key. 但是删除前要检验下 serverId 是否匹配
if (redis.get(key) == serverId) {
      redis.del(key);
}

但是很明显,解锁逻辑是两步操作,并非是原子性

5. 引入lua

为了使解锁操作原子性,可以使用Redis的Lua脚本功能

复制代码
if redis.call('get',KEYS[1]) == ARGV[1] then
   return redis.call('del',KEYS[1]) 
else
   return 0
end;

上述代码可以编写成一个.lua后缀的文件,有redis-cli或者jedis等客户端加载,并且发送给Redis服务器,由Redis服务器来执行这段逻辑

一个 lua 脚本会被Redis服务器一原子的方式来执行

6. 引入看 watch dog

上述方案仍然存在一个重要问题,当我们设置了key过期时间之后,仍然存在一定的可能性,当任务还没有完成,key就过期了,这就导致了提前失效

所谓的watch dog 本质上是加锁服务器上的一个单独的线程,通过这个线程来对锁过期时间进行"续约"

7. 引入 Redlock算法

实践中 Redis 一般是以集群的方式部署的,那么就可能出现以下问题:

服务器1向master节点进⾏加锁操作.这个写⼊key的过程刚刚完成,master挂了;slave节 点升级成了新的master节点.但是由于刚才写⼊的这个key尚未来得及同步给slave呢,此时 就相当于服务器1的加锁操作形同虚设了,服务器2仍然可以进⾏加锁(即给新的master写 ⼊key.因为新的master不包含刚才的key).

为了解决这个问题,Redis作者提出了 Redlock算法

我们引入一组Redis节点,其中每一组Redis都包含一个主节点和若干从节点,并且组合组之间存储的数据都是一致的,相互之间是备份关系(并非数据集合的一部分)

加锁的时候,按照一定的顺序,写多个emaster节点,在写锁的时候需要设定操作的"超时时间"

如果给某个节点加锁失败,就立即尝试下一个节点

当加锁成功的接地那数超过总数一般,才视为加锁成功

同理,释放锁的时候,也需要把所有接地那都进行解锁操作

简而言之,Redlck算法的核心就是,加锁操作不能只写给一个Redis节点,而是写给多个,分布式系统中,任何一个节点都是不可靠的,最终的加锁成功结论是"少数服从多数"

由于一个分布式系统不至于大部分节点都同时发生故障,因此这样的可靠性要比但隔节点来书靠谱

8. 其他功能

上述的锁只是一个简单的互斥锁,但是实际上一些特定场景中,还有一些其他特殊的锁

  • 可重入锁
  • 公平锁
  • 读写锁
  • .......

基于Redis的分布式锁,也可以实现上述特性

相关推荐
许彰午11 分钟前
# 数据库配拦截器,不用改BPMN
数据库
lv__pf33 分钟前
redis【msb 2026金三银四redis上】
数据库·redis·缓存
布莱克6051 小时前
理解数据库聚簇索引:原理、优势与适用场景
数据库·mysql
Nturmoils2 小时前
只面对一张表:KingbaseES 超表如何简化海量时序数据管理
数据库
starrocks_stella3 小时前
StarRocks 如何查询 Paimon 半结构化数据?Variant、Shredding 与 SQL 实践
数据库
这个DBA有点耶3 小时前
MySQL迁移实战:从mysqldump到专业工具的完整选型指南
数据库·mysql·dba
小白说大模型3 小时前
LLM集成数据库的幻觉治理:当AI给出的SQL建议是错的
数据库·人工智能·sql·oracle·重构·开源
spter。4 小时前
一次 Redis SETNX 的小插曲,我重新理解了原子性
前端·redis·bootstrap
zcmodeltech4 小时前
工程机械与矿山机械沙盘模型多系统协同控制系统设计:基于STM32与Modbus RTU的露天开采-井下掘进-智慧矿山全场景联动方案
数据库·stm32·单片机·嵌入式硬件·制造·多分类