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

相关推荐
夕除3 小时前
redis--UV
redis
JosieBook3 小时前
【数据库】MySQL 实战精通系列 · 第8篇:慢查询治理与性能优化实战
数据库·mysql·性能优化
TDengine (老段)4 小时前
TDengine TSDB 实战排障四(升级与兼容)
android·java·大数据·数据库·物联网·时序数据库·tdengine
小小龙学IT4 小时前
Go 泛型(Generics)深度解析:从类型参数到生产实践
开发语言·数据库·golang
这个DBA有点耶4 小时前
分区表深入:分区裁剪失效的6种场景、分区锁机制与维护实战
数据库·mysql·dba
蓝速科技4 小时前
政务自助终端信创选型与无人值守落地方案
android·大数据·数据库·人工智能·科技·技术分享·政务
粤鼎恒业4 小时前
惠州工厂电子料回收厂家推荐:从交接单反推筛选标准
大数据·数据库·算法·硬件架构·硬件工程·pcb工艺·材料工程
ShineWinsu5 小时前
对于Redis:Redis的认识以及分布式系统的解析
linux·数据库·c++·redis·缓存·消息队列·分布式架构
灯澜忆梦5 小时前
【RabbitMQ #3】 | Go 客户端 + SpringAMQP
分布式·golang·rabbitmq
SelectDB5 小时前
ELK 做不了的分析,我用 Doris 物化视图补上了:配置、开关与四个排错现场
大数据·数据库·数据分析