当前位置: 首页 > article >正文

深入Redis:分布式锁

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

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

分布式锁,本质上就是使用一个公共的服务器来记录加锁状态,这个公共的服务器可以是Redis,也可以是其他的组件(MySql或者Zookeeper等),也可以是自己写的一个服务。

基础实现

所有蓝色的模块,都是原先的服务器/客户端。现在加入一个橙色的Redis服务器作为分布式锁的管理器。

此时如果某客户端需要访问数据库,就需要先访问Redis,在Redis上设置一个键值对。设置value为1,而不是0。作此操作就可以对当前key加锁,操作完成后再把这个key删除。别的客户端需要访问这个key,发现Redis上已经有了这个键值对,就会阻塞等待或者放弃。

Redis中提供了setnx操作,正好适合这个场景:key不存在就设置,存在则直接失败。

过期时间

当服务器1加锁之后,意外宕机了,就会导致解锁操作一直不能执行,就可能引起其他服务器始终无法获取到锁的情况。为了解决这个问题,可以在设置key的同时引入过期时间,即这个锁最多持有多久就应该被释放。

Redis中提供了 set ex nx操作,可以在设置锁的时候同时把过期时间给设置进去。

校验ID

对于Redis中写入的加锁键值对,其他的节点也是可以删除的。(虽然不会主动删除,但是不排除遇到bug)。

为了解决上述问题,我们再引入一个校验ID,可以把设置的键值对的值,不在简单的设置成为一个1,可以设置成服务器的编号,如服务器1就设置成001。这样就可以在删除key的时候,先校验当前删除key的服务器是否是当初加锁的服务器,如果是才能真正删除。

看门狗(watch dog)

上述方案仍然存在一个重要的问题:如果如果任务还没执行完,key就过期了怎么办?

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

初始情况下设置过期时间为 10s,同时设定看门狗线程每隔 3s 检测一次
那么当 3s 时间到的时候,看门狗就会判定当前任务是否完成:

  • 如果任务已经完成,则直接通过 lua 脚本的方式,释放锁(删除 key)
  • 如果任务未完成,则把过期时间重写设置为 10s

这样就不担心锁提前失效的问题了,而且另一方面,如果服务器挂了,看门狗线程也就随之挂了,此时无人续约,这个key自然就可以迅速过期,让其他服务器能够获取到锁。

Redlock算法

实践中的Redis一般是以集群的方式部署的,就可能出现某些极端情况:

服务器1向master节点进行加锁操作,这个写入key的过程刚刚完成,master挂了。

slave节点升级成了新的master节点,但是由于刚刚写入的这个key还没有同步给slave,此时相当于服务器1的加锁操作形同虚设,服务器2仍然可以进行加锁。

我们引入一组 Redis 节点.其中每一组 Redis 节点都包含一个主节点和若干从节点,并且组和组之间存
储的数据都是一致的,相互之间是"备份"关系(而并非是数据集合的一部分,这点有别于 Redis cluster).
加锁的时候,按照一定的顺序,写多个master节点,在写锁的时候需要设定操作的"超时时间".比如
50ms.即如果 setnx 操作超过了 50ms 还没有成功,就视为加锁失败。

如果给某个节点加锁失败,就立即再尝试下一个节点。当加锁成功的节点数超过总节点数的一半,才视为加锁成功。
同理,释放锁的时候,也需要把所有节点都进行解锁操作。(即使是之前超时的节点,也要尝试解锁,尽量保证逻辑严密)。简而言之,Redlock算法的核心就是,加锁操作不能只写给一个Redis节点,而要写个多个。分布式系统中任何一个节点都是不可靠的。最终的加锁成功结论是"少数服从多数的"。由于个布式系统不至于大部分节点都同时出现故障,因此这样的可靠性要比单个节点来说靠谱不少。

至此,Redis告一段落~18篇文章不是Redis的完结,是新的篇章的开始~


http://www.kler.cn/a/309772.html

相关文章:

  • Python如何用正则表达式匹配并处理文件名
  • python数据写入excel文件
  • JavaScript——函数、事件与BOM对象
  • Linux入门:环境变量与进程地址空间
  • 提升法律文书处理效率的秘密武器:开源文档比对工具解析
  • SPIRE: Semantic Prompt-Driven Image Restoration 论文阅读笔记
  • [语言月赛 202408] 因友情而终结
  • 深圳国际VR/AR博览会圆满落下帷幕
  • 【C++ Primer Plus习题】16.8
  • yaml注入配置文件
  • 【ZYNQ】OV5640 图像采集与显示
  • 【Linux】权限理解(超详细)
  • 【与C++的邂逅】--- C++的IO流
  • sqlgun靶场训练
  • 【机器学习】--- 自然语言推理(NLI)
  • SPI软件模拟读写W25Q64
  • Qt常用控件——QLCDNumber
  • scantf
  • Linux--守护进程与会话
  • Java 中 List 常用类和数据结构详解及案例示范
  • 基于python+django+vue的外卖管理系统
  • java循环遍历树状结构对象并转换成另外树状结构对象
  • 6.安卓逆向-安卓开发基础adb工具
  • 学习贵在善假于物
  • 什么是科技与艺术相结合的异形创意圆形(饼/盘)LED显示屏
  • YooAsset基础操作及热更