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

浅析Redis分布式锁的实现方法

浅析Redis分布式锁的实现方法

背景

在分布式系统的开发中,我们往往需要解决多进程间的并发安全性问题,此时就需要实现一个分布式锁。

想要解决分布式系统的锁问题,我们就需要让 不单依赖于某个系统,需要将 给提取出来,独立的为所有系统服务。

在一个分布式系统中,缓存往往是必不可少的,故而我们常用的Redis就很好的接过了这个任务!

SETNX + EXPIRE

在Key-Value形式的Redis数据结构中,Key具有唯一性,我就可以用一个Key来唯一标识一个锁,

当一个服务需要加锁时,就在Redis中尝试获取这个锁的唯一Key,如果这个Key存在(获取到了Key),就代表锁已经被其它服务持有,加锁失败。

如果这个Key不存在(没有获取到Key),就代表锁还未被持有,该服务就可以在Redis中设置一个Key,代表已经持有锁,成功加上锁了。

其中,尝试获取锁的Key,若Key已存在,则加锁失败,否则加锁成功 的流程,在Redis中提供了一个命令:SETNX,可以一步完成:

SETNX在设置Key之前会先判断Key是否已经存在,若已存在则返回设置失败,否则就成功设置Key。

如果要释放锁,直接删除这个Key即可。

此时,我们已经利用Redis的唯一Key性质简单实现了分布式锁!

但是!

当服务成功加上锁后,必须要服务在Redis中删除这个锁才能释放掉锁,让其它服务继续获取锁。

如果服务在成功加上锁后,在释放锁之前宕机了,或者释放锁失败了!锁就永远存在Redis中了,其它服务永远也获取不到锁了!

其实在Redis中,我们可以通过EXPIRE命令,给某个Key加上过期时间,达到过时间,Key就会自动删除,也就是锁会自动释放。这样就解决了上面的问题。

原子性

但是!

SETNXEXPIRE命令是两个命令,它们在一起不具备原子性 ,也就是说:

SETNX加锁成功后,EXPIRE给锁设置过期时间可能会失败,这样还是会产生上面的问题!

可重入性

其实还有一个问题:

每个服务都可以访问Redis,那么,就有可能把其他服务加的锁,给删除了!

此时,我们可以在设置锁的Key时,加入一个唯一标识,用以标识这个锁是哪个服务持有得到,

当其它在操作这个锁之前,要先比较一下唯一标识是否是自己的,如果不是,就不能操作,

否则,就知道这个锁是自己的,可以操作。

但是,这个方法“防君子,不防小人”,其它服务还是可以不管不顾,直接把锁给删了!

此外,如果想要给锁提供可重入性

可以在锁的Key中设置一个整数,该数用以表明锁被重入的次数,当释放锁时,先对该数值减一,如果还是大于零,就代表还有本服务的其它线程持有该锁,还不能直接删除。

这样就简单实现了锁的重入,但是可用性较差。

发布订阅

此外,我们还可以利用上Redis的发布订阅功能:

当一个服务成功加上锁后,会发布一个主题,其它加锁失败的服务,会订阅这个主题。

当服务释放锁后,会在主题上广播这个信息,其它服务收到信息后就可以重新尝试获取锁了!

自动续约

此外,当服务成功获取锁,设置好过期时间后,如果程序执行时间太长,锁要失效了怎么办?

如果锁失效了,而程序还没有执行完,其它服务获取到锁后也来执行,就会出现并发问题。

我们除了给锁设置一个合理的过期时间外,还可以提供一个自动续约的机制:

设置合适的周期(要小于锁的过期时间),循环检测程序是否执行完成,若没有执行完成,则给锁刷新过期时间。

RedLock

此外,如果Redis崩了,分布式锁就无法服务了,我们就需要部署Redis集群以提高可用性。

对于Redis集群,加锁的时候,是单节点加锁,还是部分节点加锁,还是全部节点加锁?

如果是单节点加锁,加锁的节点如果宕机了,锁的信息就会丢失了。

如果是全部节点加锁,效率太低,绝大多数情况下,大部分节点是不会同时宕机的,所以只需要在大部分节点上加锁就行了,也就是部分节点加锁。

RedLock选择在N/2+1个节点加锁,只有在N/2+1个节点上都加锁成功了,才算最终加锁成功。

RedLock下,服务需要依次尝试在集群中的节点上加锁,需要设置一个尝试超时时间,超时还未加锁成功的话,就换下一个节点尝试加锁,直到N/2+1个节点加锁成功,并且,加锁耗时小于锁的超时时间(也就是尝试一圈加完锁后,锁还没有过期),才算最终加锁成功。

如果加锁耗时超过锁的超时时间,必须要主动释放所有已经加上的节点锁。

Lua

在Redis中可以使用Lua脚本,这是一个原子性且高效的操作,

通过使用Lua脚本来设置Key和过期时间,可以解决SETNXEXPIRE非原子性的问题。

Redisson

上面提到的所有技术方案,如果都要让我们自己来实现,未免有些太过麻烦!

Redisson为我们提供了更高效,更高可用的实现,我们可以直接使用:

结合Lua可以原子性的加锁和设置过期时间;

通过给Key中添加服务的唯一标识,以及设置整数值,实现锁的可重入性

通过看门狗机制,定期检测程序如果没有执行完,就自动续约;

结合Redis的发布订阅机制来唤醒其它服务重试获取锁;

结合RedLock算法,提供RedissonRedLock解决单点失败问题。

相关文章:

  • 使用OpenAI Python库探索新一代AI接口:从基础到智能体开发
  • 文本组件+Image组件+图集
  • 数学复习(8)连续性
  • STM32F407ZGT6--工程模版
  • 10.2linux内核定时器实验(详细编程)_csdn
  • Windows环境下安装部署dzzoffice+onlyoffice的私有网盘和在线协同系统
  • 【USTC 计算机网络】第一章:计算机网络概述 - Internet、网络边缘、网络核心、接入网与物理媒体
  • 基于变分推理与 Best‑of‑N 策略的元 Prompt 自动生成与优化框架
  • 学习路之TP6 --重写vendor目录下的文件(服务覆盖command---优点:命令前后一致)
  • 在线 SQL 转 SQLAlchemy:一键生成 Python 数据模型
  • 谷歌Gemini 3大模型发布,AI领域再掀波澜!(2)
  • 如何通过 Airbyte 将数据摄取到 Elasticsearch
  • 【Linux我做主】基础命令完全指南上篇
  • 如何在 VS编译器上使用 C99规定的变长数组------使用Clang工具
  • 如何用URDF文件构建机械手模型并与MoveIt集成
  • LeetCode 解题思路 17(Hot 100)
  • 【GB28181】H265-Nalu的封装
  • 让双向链表不在云里雾里
  • 个人博客系统测试报告
  • AI 数字人短视频源码开发:开启虚拟世界的创意引擎
  • 做个什么类型网站/网推是什么意思
  • 在局网站 作风建设/企业网站建设
  • 加盟的网站建设/怎么弄一个自己的网站
  • 加人引流加人网站怎么做/百度知道个人中心
  • 厚街网站建设公司/百度网站大全
  • 福州模板做网站/个人友情链接推广