使用 Redis 实现分布式锁:原理、实现与优化
在分布式系统中,分布式锁是确保多个进程或线程在同一时间内对共享资源进行互斥访问的重要机制。Redis 作为一个高性能的内存数据库,提供了多种实现分布式锁的方式。本文将详细介绍如何使用 Redis 实现分布式锁,包括基本原理、实现方法、示例代码以及优化建议。
一,分布式锁的基本原理
分布式锁的基本原理是通过一个中心化的存储(如 Redis)来记录锁的状态。当一个进程获取锁时,它会在存储中记录锁的持有者和过期时间。其他进程在尝试获取锁时,会检查存储中的锁状态,如果锁已经被其他进程持有,则需要等待或重试。
二,使用 Redis 实现分布式锁
1. 基本实现
使用 Redis 的 SET 命令可以实现一个简单的分布式锁。SET 命令支持设置键的过期时间和条件参数(NX:仅当键不存在时设置,XX:仅当键存在时设置)。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;public class RedisLock {private Jedis jedis;private String lockKey = "lockKey";private int lockTimeout = 30000; // 30秒public RedisLock() {jedis = new Jedis("localhost", 6379);}// 获取锁public boolean acquireLock(String requestId, int expireTime) {SetParams params = new SetParams();params.nx().px(expireTime);String result = jedis.set(lockKey, requestId, params);return "OK".equals(result);}// 释放锁public boolean releaseLock(String requestId) {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());}public static void main(String[] args) {RedisLock redisLock = new RedisLock();String requestId = String.valueOf(System.currentTimeMillis());// 尝试获取锁if (redisLock.acquireLock(requestId, 30000)) {System.out.println("Lock acquired");// 释放锁if (redisLock.releaseLock(requestId)) {System.out.println("Lock released");} else {System.out.println("Failed to release lock");}} else {System.out.println("Failed to acquire lock");}}
}
2. 优化实现
为了提高分布式锁的可靠性,可以使用 Redlock 算法。Redlock 算法通过在多个独立的 Redis 实例上获取锁来实现更高的容错性。
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.params.SetParams;import java.util.ArrayList;
import java.util.List;public class Redlock {private List<JedisPool> jedisPools;private String lockKey = "lockKey";private int lockTimeout = 30000; // 30秒public Redlock() {jedisPools = new ArrayList<>();jedisPools.add(new JedisPool(new JedisPoolConfig(), "localhost", 6379));jedisPools.add(new JedisPool(new JedisPoolConfig(), "localhost", 6380));jedisPools.add(new JedisPool(new JedisPoolConfig(), "localhost", 6381));}// 获取锁public boolean acquireLock(String requestId, int expireTime) {int n = jedisPools.size();int quorum = n / 2 + 1;int count = 0;for (JedisPool pool : jedisPools) {try (Jedis jedis = pool.getResource()) {SetParams params = new SetParams();params.nx().px(expireTime);String result = jedis.set(lockKey, requestId, params);if ("OK".equals(result)) {count++;}}}return count >= quorum;}// 释放锁public boolean releaseLock(String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) else return 0 end";int count = 0;for (JedisPool pool : jedisPools) {try (Jedis jedis = pool.getResource()) {Object result = jedis.eval(script, 1, lockKey, requestId);if ("1".equals(result.toString())) {count++;}}}int n = jedisPools.size();int quorum = n / 2 + 1;return count >= quorum;}public static void main(String[] args) {Redlock redlock = new Redlock();String requestId = String.valueOf(System.currentTimeMillis());// 尝试获取锁if (redlock.acquireLock(requestId, 30000)) {System.out.println("Lock acquired");// 释放锁if (redlock.releaseLock(requestId)) {System.out.println("Lock released");} else {System.out.println("Failed to release lock");}} else {System.out.println("Failed to acquire lock");}}
}
通过以上优化实现,Redlock 算法可以在多个独立的 Redis 实例上获取锁,从而提高分布式锁的可靠性和容错性。
3. 优缺点分析
优点
- 高性能: Redis 是内存数据库,读写速度极快,适用于高并发场景。
- 易于实现: 使用 Redis 实现分布式锁的代码相对简单,容易上手。
- 社区支持广泛: Redis 拥有庞大的用户群体和丰富的文档资源。
缺点
- 单点故障 :如果 Redis 实例宕机,可能导致锁失效,需要额外的高可用配置(如 Redis Sentinel 或 Redis Cluster)。
- 锁的过期和续期问题: 需要合理设置锁的过期时间,并处理锁的续期问题,防止业务逻辑执行时间过长导致锁失效。
最佳实践
- 使用 Redis 集群 :为防止单点故障,可以使用 Redis Sentinel 或 Redis Cluster 来提高高可用性。
- 合理设置锁的过期时间:根据业务逻辑的执行时间,合理设置锁的过期时间,防止死锁。
- 使用 Lua 脚本:为了保证操作的原子性,可以使用 Lua 脚本来实现获取锁和设置过期时间的操作。
三,分布式锁组件
以下是一些可以直接使用的分布式锁组件,它们提供了高效、可靠的分布式锁实现,适用于各种分布式系统和应用场景:
1. Redisson
简介:Redisson 是一个基于 Redis 的 Java 客户端,它提供了许多高级功能,包括分布式锁、分布式集合、分布式队列等。
特点:
- 支持公平锁、非公平锁、读写锁等多种锁类型。
- 提供异步和同步 API。
- 内置过期时间和自动续期功能。
使用示例:
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;public class RedissonExample {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379");RedissonClient redisson = Redisson.create(config);RLock lock = redisson.getLock("myLock");lock.lock();try {// 业务逻辑System.out.println("Lock acquired");} finally {lock.unlock();System.out.println("Lock released");}redisson.shutdown();}
}
2. Apache Curator
简介:Apache Curator 是一个用于 Apache ZooKeeper 的 Java 客户端库,提供了许多高级功能,包括分布式锁、领导选举、分布式队列等。
特点:
- 基于 ZooKeeper 实现,适用于需要高可用性和一致性的场景。
- 提供可重入锁、共享锁等多种锁类型。
- 支持会话超时和自动重试机制。
使用示例:
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;public class CuratorExample {public static void main(String[] args) throws Exception {CuratorFramework client = CuratorFrameworkFactory.newClient("localhost:2181", new ExponentialBackoffRetry(1000, 3));client.start();InterProcessMutex lock = new InterProcessMutex(client, "/myLock");lock.acquire();try {// 业务逻辑System.out.println("Lock acquired");} finally {lock.release();System.out.println("Lock released");}client.close();}
}
3. Etcd
简介:Etcd 是一个分布式键值存储系统,常用于服务发现和配置管理。它也可以用于实现分布式锁。
特点:
- 提供原子操作和强一致性保证。
- 支持租约机制,用于实现锁的自动过期。
- 提供 HTTP+JSON API 和 gRPC API。
使用示例(基于 Java Etcd 客户端):
import io.etcd.jetcd.Client;
import io.etcd.jetcd.Lock;
import io.etcd.jetcd.lock.LockResponse;public class EtcdExample {public static void main(String[] args) throws Exception {Client client = Client.builder().endpoints("http://localhost:2379").build();Lock lockClient = client.getLockClient();LockResponse lockResponse = lockClient.lock(ByteSequence.from("/myLock", UTF_8), 0).get();try {// 业务逻辑System.out.println("Lock acquired");} finally {lockClient.unlock(lockResponse.getKey()).get();System.out.println("Lock released");}client.close();}
}
4. Consul
简介:Consul 是一个支持服务发现和配置管理的分布式系统,也可以用于实现分布式锁。
特点:
- 提供强一致性的 KV 存储。
- 支持会话机制,用于实现锁的自动过期。
- 提供 HTTP API 和多个语言的客户端库。
使用示例(基于 Java Consul 客户端):
import com.orbitz.consul.Consul;
import com.orbitz.consul.model.kv.Value;
import com.orbitz.consul.option.PutOptions;public class ConsulExample {public static void main(String[] args) throws Exception {Consul consul = Consul.builder().build();String sessionId = consul.sessionClient().createSession("myLock").getId();boolean lockAcquired = consul.keyValueClient().acquireLock("myLock", sessionId);if (lockAcquired) {try {// 业务逻辑System.out.println("Lock acquired");} finally {consul.keyValueClient().releaseLock("myLock", sessionId);System.out.println("Lock released");}} else {System.out.println("Failed to acquire lock");}consul.sessionClient().destroySession(sessionId);}
}
通过使用这些分布式锁组件,开发者可以轻松实现高效、可靠的分布式锁机制,确保在分布式系统中对共享资源的互斥访问。
四,结语
在分布式系统中,分布式锁是确保多个进程或线程在同一时间内对共享资源进行互斥访问的重要机制。本文介绍了使用 Redis、Zookeeper、Etcd 和 Consul 实现分布式锁的方法,并提供了相应的示例代码。
每种分布式锁实现方式都有其独特的优缺点和适用场景。开发者可以根据具体的业务需求和系统架构选择最合适的分布式锁方案。同时,在实现分布式锁时,需要注意锁的过期时间设置、锁的续期问题以及高可用配置等,以确保系统的稳定性和可靠性。
希望本文对您在分布式系统中实现分布式锁有所帮助。如果您有任何问题或需要进一步的解释,请随时联系我。
相关文章:
使用 Redis 实现分布式锁:原理、实现与优化
在分布式系统中,分布式锁是确保多个进程或线程在同一时间内对共享资源进行互斥访问的重要机制。Redis 作为一个高性能的内存数据库,提供了多种实现分布式锁的方式。本文将详细介绍如何使用 Redis 实现分布式锁,包括基本原理、实现方法、示例代…...
Android常用C++特性之std::make_pair
声明:本文内容生成自ChatGPT,目的是为方便大家了解学习作为引用到作者的其他文章中。 std::make_pair 是 C 标准库中的一个函数模板,用于创建一个 std::pair 对象。std::pair 是一种可以存储两个不同类型值的简单数据结构,类似于二…...
Kafka-参数详解
一、上下文 从《Kafka-初识》中可以看到运行kafka-console-producer和 kafka-console-consumer来生产和消费数据时会打印很多参数,这些参数给我们应对多种场景提供了遍历,除了producer和consumer的提供了参数外,Kafka服务器集群中的broker也…...
Docker Overlay2 空间优化
目录 分析优化数据路径规划日志大小限制overlay2 大小限制清理冗余数据 总结 分析 overlay2 目录占用磁盘空间较大的原因通常与 Docker 容器和镜像的存储机制以及它们的长期累积相关,其实我之前在 Docker 原理那里已经提到过了。 通常时以下几种原因导致ÿ…...
第 3 章:使用 Vue 脚手架
1. 初始化脚手架 1.1 说明 Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)。最新的版本是 5.x。文档: https://cli.vuejs.org/zh/ 1.2 具体步骤 第一步(仅第一次执行):全局安装vue/cli。 npm install -g vu…...
Spring 循环依赖详解:问题分析与三级缓存解决方案
在Spring框架中,循环依赖(Circular Dependency)是指多个Bean相互依赖,形成一个循环引用。例如,Bean A依赖于Bean B,而Bean B又依赖于Bean A。这种情况在Bean创建时可能导致Spring容器无法正常完成初始化&am…...
爬虫prc技术----小红书爬取解决xs
知识星球:知识星球 | 深度连接铁杆粉丝,运营高品质社群,知识变现的工具知识星球是创作者连接铁杆粉丝,实现知识变现的工具。任何从事创作或艺术的人,例如艺术家、工匠、教师、学术研究、科普等,只要能获得一…...
uni-app之旅-day06-加入购物车
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言8.0 创建 cart 分支8.1 配置 vuex8.2 创建购物车的 store 模块8.3 在商品详情页中使用 Store 中的数据8.4 实现加入购物车的功能8.5 动态统计购物车中商品的总数…...
【Kubernetes】常见面试题汇总(五十六)
目录 123. pod 创建失败? 124. kube-flannel-ds-amd64-ndsf7 插件 pod 的 status 为 Init:0/1 ? 特别说明: 题目 1-68 属于【Kubernetes】的常规概念题,即 “ 汇总(一)~(二十二&#x…...
LabVIEW激光诱导击穿光谱识别与分析系统
LabVIEW激光诱导击穿光谱(LIBS)分析系统利用高能量脉冲激光产生高温等离子体,通过分析等离子体发出的光谱来定性分析样品中的元素种类。该系统的开发集成了软件与硬件的设计,实现了自动识别和定性分析功能,适用于环境监…...
Redis的基础篇
Redis的基础篇 1.在CentOs7上安装Redis(最好不要在windows上装,版本少) 1.安装gcc --> yum install gcc tcl(可能会报错,重新安装yum就行了) 2.下载redis --> 最好是6.2上的版本 3.解压redis --> tar -zxvf redis-6.2.…...
java和python哪个好
Java和Python各有优缺点,适合不同的应用场景,具体看你需要在哪种情况下使用编程语言。以下是Java和Python的一些对比,帮助你决定哪种更适合你的需求: 性能 Java:编译型语言,编译成字节码运行在Java虚拟机&…...
Electron + ts + vue3 + vite
正常搭建脚手架:npm create vitelatest 项目名称 安装electron的相关依赖:注:安装时终端url要项目名那一层 安装npm install electron -D安装打包工具:npm install electron-builder -D开发工具:npm install electron-…...
《大规模语言模型从理论到实践》第一轮学习--分布式训练
基础知识 5分钟看懂电脑硬件配置 - 知乎 (zhihu.com) 显存 定义:显存是显卡上的专用高速缓存,用于存储图形处理器(GPU)在处理图像和视频数据时所需的临时数据。 功能:显存的主要作用是提供GPU快速访问的数据存储&a…...
多模态智能
研究背景: 深度学习从1.0的端到端走向2.0的预训练,通过大规模预训练来记忆多模态数据中共性知识,增强对下游任务的学习能力。 深度学习1.0:特定任务有标注训练数据->随机初始化训练->最终模型 深度学习2.0:大规…...
【机器学习(十三)】机器学习回归案例之股票价格预测分析—Sentosa_DSML社区版
文章目录 一、背景描述二、Python代码和Sentosa_DSML社区版算法实现对比(一) 数据读入(二) 特征工程(三) 样本分区(四) 模型训练和评估(五) 模型可视化 三、总结 一、背景描述 股票价格是一种不稳定的时间序列,受多种因素的影响。影响股市的外部因素很多,主要有经济因素、政治因…...
大模型微调
概述 什么是模型微调? 模型微调是通过微调工具,使用独特的场景数据对平台的基础模型进行调整,帮助你快速定制一个更符合业务需求的大型模型。其优势在于对基础模型进行小幅调整以满足特定需求,相比于训练一个新模型,…...
240607 继承
面向对象三大特性:封装、继承、多态 RE: 封装 C把数据和方法封装在类里面迭代器和适配器 继承 1 基类 & 派生类 一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表…...
轻松应对意外丢失:高效电脑数据恢复指南!
有时候由于误操作、硬件故障、病毒攻击等原因,电脑里的重要文件可能会突然消失不见。面对这样的情况,很多人会感到手足无措。其实,借助专业的电脑数据恢复软件,我们可以较为轻松地找回丢失的数据。今天,我们就来介绍几…...
vue项目中播放rtsp视频流
一、下载webrtc-streamer 下载地址:https://github.com/mpromonet/webrtc-streamer/releases 根据设备型号下载对应的版本到本地直接解压就行,我下载的是webrtc-streamer-v0.8.6-dirty-Windows-AMD64-Release.tar版本。 双击webrtc-streamer.exe可执行文…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
