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的分布式锁,也可以实现上述特性

相关推荐
小草儿7991 小时前
GBase 8s CLOB → VARCHAR(32739) 数据迁移方案文档
数据库
秋田君1 小时前
QT_QColorDialog颜色对话框
java·数据库·qt
涛声依旧393162 小时前
运维项目:InnoAI SQL 助手
运维·数据库·sql
万悉科技2 小时前
时尚品牌GEO技术选型指南:LLM原生优化与AI记忆构建全解析
数据库·人工智能·搜索引擎
lb29172 小时前
在window上安装orcale数据库流程(快速,简洁)
数据库
IT小盘2 小时前
09-使用Redis保存大模型多轮对话上下文
数据库·redis·缓存
秋田君2 小时前
QT_QT布局常用类QSplitter窗口分割类与QDockWidget窗口停靠类
开发语言·数据库·qt
小果因子实验室2 小时前
策略研究--qmt实现新股自动申购卖出策略
数据库
Darling噜啦啦2 小时前
从零实战Milvus向量数据库:用RAG打造你的AI日记助手
数据库