【Redis】Redis分布式锁的基本原理和具体实现

Redis 分布式锁是一种在分布式系统中使用 Redis 实现的锁机制,用于确保多个进程或线程在某个时间段内只有一个能够访问共享资源。它可以用于解决分布式环境下的并发问题。下面详细介绍 Redis 分布式锁的实现方法,包括其基本原理和具体实现。

基本原理

Redis 分布式锁通常基于以下几个关键点:

  1. 互斥性:在任何时候,只有一个客户端可以持有锁。
  2. 避免死锁:锁必须有一个自动过期时间,以避免客户端在持有锁期间崩溃而导致的死锁。
  3. 容错性:锁的实现应当支持 Redis 集群或主从模式,确保在某些 Redis 节点故障时锁机制依然有效。

实现方法

使用 SETNX 和 EXPIRE 命令

这是最简单的实现方法,通过 Redis 的 SETNX(SET if Not eXists)命令和 EXPIRE 命令来实现分布式锁。

  1. 获取锁

    • 使用 SETNX 尝试设置一个键,如果成功,则表示获取锁成功。
    • 使用 EXPIRE 设置键的过期时间,防止死锁。
  2. 释放锁

    • 客户端在操作完成后,删除该键以释放锁。

示例代码

import redis.clients.jedis.Jedis;public class RedisDistributedLock {private Jedis jedis;private String lockKey;private int expireTime; // 锁的过期时间(秒)public RedisDistributedLock(Jedis jedis, String lockKey, int expireTime) {this.jedis = jedis;this.lockKey = lockKey;this.expireTime = expireTime;}public boolean acquireLock(String requestId) {String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);return "OK".equals(result);}public boolean releaseLock(String requestId) {// 使用 Lua 脚本释放锁,确保操作的原子性String script ="if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else " +"return 0 " +"end";Object result = jedis.eval(script, 1, lockKey, requestId);return "1".equals(result.toString());}
}

在上述代码中:

  • acquireLock 方法尝试获取锁。如果成功,则返回 true;否则返回 false
  • releaseLock 方法使用 Lua 脚本保证删除操作的原子性,只有在键的值与请求标识符匹配时才会删除键。
Redlock 算法

为了增强容错性,Redis 官方提供了 Redlock 算法。它通过在多个 Redis 实例上获取锁来实现分布式锁。

Redlock 算法的步骤如下:

  1. 获取当前时间。
  2. 依次尝试在 N 个 Redis 实例上创建锁,使用相同的键和随机值,并设置相同的过期时间。
  3. 客户端计算在多个实例上成功获取锁的时间。如果总时间小于锁的过期时间且至少有 N/2+1 个实例成功获取锁,则认为锁获取成功。
  4. 如果锁获取失败,则在所有实例上删除锁。
  5. 锁持有者在操作完成后删除锁。

示例代码

import redis.clients.jedis.Jedis;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;public class RedisDistributedLock {private List<Jedis> jedisList;private String lockKey;private int expireTime; // 锁的过期时间(毫秒)private int quorum; // 至少需要成功获取锁的实例数public RedisDistributedLock(List<Jedis> jedisList, String lockKey, int expireTime) {this.jedisList = jedisList;this.lockKey = lockKey;this.expireTime = expireTime;this.quorum = (jedisList.size() / 2) + 1;}public String acquireLock() {String requestId = UUID.randomUUID().toString();long startTime = System.currentTimeMillis();int successCount = 0;for (Jedis jedis : jedisList) {if ("OK".equals(jedis.set(lockKey, requestId, "NX", "PX", expireTime))) {successCount++;}}long elapsedTime = System.currentTimeMillis() - startTime;if (successCount >= quorum && elapsedTime < expireTime) {return requestId;} else {for (Jedis jedis : jedisList) {jedis.del(lockKey);}return null;}}public void releaseLock(String requestId) {for (Jedis jedis : jedisList) {String script ="if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else " +"return 0 " +"end";jedis.eval(script, 1, lockKey, requestId);}}
}

在上述代码中:

  • acquireLock 方法尝试在多个 Redis 实例上获取锁。如果成功获取的实例数达到 quorum(至少需要成功获取锁的实例数)且总耗时小于锁的过期时间,则认为锁获取成功。
  • releaseLock 方法使用 Lua 脚本在所有实例上删除锁。

注意事项

  1. 时钟漂移:Redlock 算法依赖于各个 Redis 实例的时间同步。如果实例的时钟漂移较大,可能会导致锁机制失效。
  2. 网络延迟:在分布式环境下,网络延迟可能会影响锁的获取和释放。应尽量选择低延迟的网络环境。
  3. 过期时间设置:锁的过期时间应设置合理,既要保证在正常操作时间内锁不会失效,又要防止长时间持有锁导致资源争用。

总结

Redis 分布式锁可以通过多种方式实现,最简单的是使用 SETNXEXPIRE 命令,确保锁的自动过期和互斥性。为了增强容错性,可以使用 Redis 官方推荐的 Redlock 算法,通过在多个 Redis 实例上获取锁来实现分布式锁。实际应用中,需要根据具体需求选择合适的实现方式,并注意处理时钟漂移、网络延迟和锁的过期时间等问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/850718.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3模板语法总结

1. 响应式数据 Vue 3中的数据是响应式的&#xff0c;即当数据发生变化时&#xff0c;视图会自动更新。这是通过使用JavaScript的getter和setter来实现的。 2. 组件化 Vue 3采用组件化开发方式&#xff0c;允许创建可复用的组件。 每个组件都有自己的作用域&#xff0c;并且…

KEIL5如何打开KEIL4的GD工程

GD官方提供的很多KEIL例程为KIEL4的版本&#xff0c;读者使用的时候可能会碰到使用KEIL5打开KEIL4的工程会报错以及无法找到芯片选型的问题&#xff0c;具体表现如下图所示。 我们该怎么办呢&#xff1f; 下面为大家介绍两种方法&#xff1a; 第一种方法是在keil4的工程后缀u…

C# Math.Round() 四舍六入五取偶

文章目录 1.重载列表2. 示例 Math.Round() 为四舍六入五取偶 1.重载列表 API说明Round(Double)将小数值舍入到最近的整数值Round(Double, Int32)将小数值按指定的小数位数舍入Round(Double, Int32, MidpointRounding)将小数值按指定的小数位数舍入&#xff0c;MidpointRoundin…

基于不确定性的相互学习 用于联合医学图像分类和分割

文章目录 Uncertainty-Informed Mutual Learning for Joint Medical Image Classification and Segmentation摘要方法实验结果 Uncertainty-Informed Mutual Learning for Joint Medical Image Classification and Segmentation 摘要 该论文提出了一种基于不确定性的相互学习…

《Linux运维总结:常用操作系统下载地址》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;Linux运维实战总结 一、系统下载 操作系统 x86_64 arm64 Centos 7.6.1810- Centos 7.9.2009- Centos 8-stream 8-stream Ubuntu x86_64 arm64 ope…

React antd 怎么封装枚举字典组件

在 React 中使用 Ant Design (antd) 封装枚举字典组件可以帮助你更方便地管理和使用枚举值。枚举字典通常用于将数据库中的数字或字符串代码映射为人类可读的标签或描述。 以下是一个简单的步骤和示例,说明如何封装一个枚举字典组件: 步骤 定义枚举字典数据:首先,你需要定…

eNSP学习——RIP故障处理

目录 主要命令 原理概述 实验目的 实验内容 实验拓扑 实验编址 实验步骤 1、导入设备预配置 2、排除R1与R2间的故障 3、排除R1与R3间的故障 需要eNSP各种配置命令的点击链接自取:华为eNSP各种设备配置命令大全PDF版_ensp配置命令大全资源-CSDN文库 主要命令 //检查…

【大模型】个人对大模型选择的见解

选择大模型产品时&#xff0c;需要考虑多个因素&#xff0c;包括但不限于以下几点&#xff1a; 需求匹配度&#xff1a;首先&#xff0c;要明确你的需求是什么。不同的大模型产品可能在功能、性能、应用场景等方面有所侧重。例如&#xff0c;有的模型擅长自然语言处理&#xff…

探索 Debian 常用命令:掌握 Linux 系统管理的重要一步

Debian 作为一个稳定、高效和安全的操作系统,广泛应用于服务器、桌面和嵌入式系统中。对于新手和经验丰富的系统管理员来说,熟练掌握 Debian 的常用命令是管理和维护系统的基础。本文将详细介绍一些在 Debian 系统中经常使用的命令,帮助读者更好地理解和操作这个强大的操作系…

C++中避免内存泄漏的方法

在C++中,内存泄漏是一个常见的问题,它发生在程序申请了一块内存后,没有正确地释放它。这会导致程序运行时间越长,内存占用越大,最终可能导致系统崩溃。为了避免内存泄漏,你可以遵循以下一些策略: 正确使用new和delete:当你使用new操作符动态分配内存时,确保在不再需要…

SpringBoot集成缓存功能

1. 缓存规范 Java Caching定义了五个核心接口&#xff0c;分别是&#xff1a;CachingProvider、CacheManager、Cache、Entry和Expiry。 CachingProvider&#xff1a;定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。CacheM…

RAG技术在教育领域的应用

一、引言 点击可以查看最新资源 随着人工智能技术的飞速发展&#xff0c;教育领域正迎来一场深刻的变革。大型语言模型&#xff08;LLM&#xff09;和检索增强生成&#xff08;Retrieval-Augmented Generation&#xff0c;RAG&#xff09;技术的结合&#xff0c;为教育领域注入…

分治乘法详细讲解

我绝对不会告诉你我是因为太蒻了&#xff0c;不会 FFT 才搞这个的。 我用一下别人的图没什么问题吧 看得懂吧&#xff1f;比如 X 123456 , Y 987654 X123456,Y987654 X123456,Y987654&#xff0c;则 n 3 , A 123 , B 456 , C 987 , D 654 n3,A123,B456,C987,D654 n3…

C语言习题~day33

1.以下程序运行时&#xff0c;若输入1abcedf2df输出结果是&#xff08;&#xff09; #include <stdio.h> int main() { char a 0, ch; while ((ch getchar()) ! \n) { if (a % 2 ! 0 && (ch > a && ch < z)) ch ch - a A; a; putchar(ch); }…

Web前端电话咨询:深度解析与实用指南

Web前端电话咨询&#xff1a;深度解析与实用指南 在数字化时代&#xff0c;Web前端技术日新月异&#xff0c;对于许多企业和个人而言&#xff0c;通过电话咨询了解前端技术的最新动态和解决方案已成为一种高效且便捷的方式。本文将从四个方面、五个方面、六个方面和七个方面&a…

Web学习_SQL注入_联合查询注入

UNION 操作符用于合并两个或多个 SELECT 语句的结果集&#xff0c; UNION 结果集中的列名总是等于 UNION 中第一个 SELECT 语句 中的列名&#xff0c;并且UNION 内部的 SELECT 语句必须拥有相同数量的 列。 联合查询注入就是利用union操作符&#xff0c;将攻击者希望查询的语句…

jmeter性能优化之mysql监控sql慢查询语句分析

接上次博客&#xff1a;基础配置 多用户登录并退出jmx文件&#xff1a;百度网盘 提取码&#xff1a;0000 一、练习jmeter脚本检测mysql慢查询 随意找一个脚本(多用户登录并退出)&#xff0c;并发数设置300、500后分别查看mysql监控平台 启动后查看&#xff0c;主要查看mysql…

406. 根据身高重建队列(中等)

406. 根据身高重建队列 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;406. 根据身高重建队列 2.详细题解 做一道题之前先静心&#xff0c;默念三遍一切反动派都是纸老虎。已知一个队列&#xff0c;队列中每个数据表示一个属性&#xf…

【qt】启动窗口的玩法

启动窗口的玩法 一.应用场景二.界面类设计窗口三.main中创建四.窗口显示标识五.功能实现1.读取注册表2.md5加密3.登录实现4.保存注册表5.功能演示 六.鼠标事件拖动窗口1.找到鼠标事件的函数2.点击事件3.移动事件4.释放事件 七.总结 一.应用场景 一般我们的软件和应用都会一个登…

实战项目《负载均衡在线OJ系统》

一、项目灵感来源 在日常做题的过程中&#xff0c;我们总会去力扣和牛客网上去做题&#xff0c;但是从来没有想过网站是如何加载给用户的&#xff0c;以及在提交代码时&#xff0c;是如何得知我们的代码是否正确。基于这样的原因&#xff0c;也是学习到一定程度的知识后&#x…