0

0

Redis如何实现分布式锁?聊聊实现方法

青灯夜游

青灯夜游

发布时间:2021-11-30 19:25:07

|

1986人浏览过

|

来源于掘金社区

转载

如何使用 redis 实现分布式锁?下面本篇文章给大家介绍一下基于 redis 实现分布式锁的方法,希望对大家有所帮助!

Redis如何实现分布式锁?聊聊实现方法

在一个分布式系统中,会遇到一些需要对多个节点共享的资源加锁的情况,这个时候需要用到分布式锁。分布式锁通常保存在一个共享的存储系统中,可以被多个节点共享和访问。【相关推荐:Redis视频教程

锁的本质

简单来讲,锁可以用一个变量来表示。比如,在一个单机多线程的程序来说,某个资源的锁用一个 bit 的数据就可以表示。即 0 表示没有资源可以访问,1 表示资源的锁已被别的线程获取,不能访问。

获取和释放特定资源的锁,本质上就是为获取和修改这个变量的值。如果值是 0 则将其修改为 1,就完成了获取的过程,如果访问到的值不是 0,则获取锁失败;如果之前获取了锁,将表示锁的变量的值修改为 0 的操作,其实就是释放锁的操作。

在一个分布式场景中,实现锁的方式也是一样的,只不过这个表示资源锁的变量,需要保存在一个共享的存储系统中。这个共享的存储系统,可以是 Redis,也可以是其他的任何可以提供数据存储的系统。

基于 Redis 的分布式锁实现

第一步:初步实现功能

对于将 Redis 作为这个共享存储系统的情况来说,代表某个资源的锁的变量,就是 Redis 中的一个键值对。假如,需要添加分布式锁的资源叫 resource_a,我们可以将 Redis 中 resource_a 的锁变量的 key 叫做 lock_a。

例如,节点一需要获取锁,它会访问 Redis 中 lock_a 的值,假设获取到的值为 0,则节点一将这个值设置为 1 后,就完成了加锁操作。此时,节点二也需要获取 resource_a 的锁,它去访问 Redis 中 lock_a 的值,发现值是 1,说明锁已经被别的节点获取,并且还没有释放,因此,节点二对资源 resource_a 加锁失败。

当节点一需要释放锁的时候,只需要将 Redis 中的 lock_a 的值设置为 0 就完成了锁的释放,之后,其他的节点就可以再次获取资源的锁。

第二步:加锁操作原子化

以上的描述中,加锁的并不是一个单一的操作,而是包含了多个步骤:读取锁变量、判断变量的值、修改锁变量。这三个操作需要原子化。

在 Redis 中,有一个 SETNX 命令,用于设置键值对的值,与 SET 命令不同,它在是先事会判断键值对是否存在,只有当指定的 KEY 不存在的时候,才会执行值的设定,否则什么都不执行。SETNX 就是「SET if Not eXist」的意思。它的用法与 SET 相同:

SETNX lock_a 1

这样,当需要获取锁的时候,使用 SETNX 命令为 lock_a 设置一个值,如果设置成功则获取到了锁,如果失败则没有获取到锁;当需要释放锁的时候,使用 DEL 操作删除键值对即可。

这样就实现了获取和释放锁的原子化操作。

第三步:防止加锁后不释放

接下来早考虑一个问题,如果节点一获取到锁之后,由于程序异常等原因,导致一直么有释放锁,此时,锁会一直被它持有,无法释放,其他节点也无法访问资源。

唱鸭
唱鸭

音乐创作全流程的AI自动作曲工具,集 AI 辅助作词、AI 自动作曲、编曲、混音于一体

下载

为了避免这种情况的发生,我们必须给锁变量设置过期时间,当锁变量过期后,就可以重新请求加锁,这样就可以避免这个问题。

SETNX 的命令并没有设置过期时间的选项,所幸的是,Redis 为 SET 命令提供了模拟 SETNX 的 NX 选项,我们可以这样设置过期时间:

SET lock_a 1 NX PX 10000

以上命令代表,如果 lock_a 不存在,则将它的值设置为 1,并且在 10 秒后过期。

第四步:谁加锁谁释放

最后一个问题是,如果节点一获取了锁,而由于某种原因,节点二执行了 DEL 操作,那么,其他节点又可以获取锁了。

为了解决这个问题,我们可以修改一下锁变量保存的内容。在前面的逻辑中,我们申请锁的时候,是去判断锁变量是否存在,而与其中保存的值关系不大,因此,我们可以把这个值利用起来。

在加锁的时候,如果把值保存为每个节点唯一的标识,那么,在释放锁执行 DEL 之前再对这个值进行判断,那么,就可以先判断锁是否是当前节点加上的,是的话再进行释放,这样就实现了「谁加锁谁是放」。

这一部分,没有一个单一的指令可以完成读取锁变量、判断、删除的操作,因此,可以使用 Lua 脚本实现。在脚本中获取到当前锁变量的值,与给定的节点标识进行比对,符合的话才进行删除操作,否则不操作。

在释放锁时,执行 Lua 脚本即可。

第五步:实现高可用

完善了功能之后,最后再来实现高可用。如果我们使用单一的 Redis 作为分布式锁的共享存储系统,那么,如果这个 Redis 不可用了,那涉及到分布式锁的部分都不可用了,这样是很脆锁的,这是高可用非常有必要的原因。

此时,需要搬出 Redis 的作者 Antirez 提出的分布式锁算法 Redlock。简而言之,是让锁的申请者,想多个独立的 Redis 实例请求加锁,如果能在半数以上的 Redis 完成枷锁操作,那么就成功的获取了锁,反之获取失败。

在释放锁的操作时,同样只要在超过半数的实例上执行成功删除锁变量的 Lua 脚本,即可视为成功。

更多编程相关知识,请访问:编程入门!!

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

319

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

228

2023.10.07

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

712

2023.08.22

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

471

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

107

2025.12.24

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

387

2023.08.14

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

954

2023.11.02

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

620

2023.11.14

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

Redis+MySQL数据库面试教程
Redis+MySQL数据库面试教程

共72课时 | 6.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号