滚雪球学Redis[6.3讲]:Redis分布式锁的实战指南:从基础到Redlock算法
全文目录:
- 🎉前言
- 🚦Redis分布式锁的概念与应用场景
- 🍃1.1 什么是分布式锁?
- 🍂1.2 应用场景
- ⚙️使用Redis实现分布式锁
- 🌼2.1 基本思路
- 🌻2.2 示例代码
- 🥀2.3 代码解析
- 🧩Redlock算法的原理与实现
- 🌴3.1 Redlock算法原理
- 🌲3.2 Redlock的实现示例
- 🌾3.3 代码解析
- ☘️3.4 Redlock的优势与局限性
- 🍀3.4.1 优势
- 🍁3.4.2 局限性
- 🛠️Redis分布式锁的最佳实践
- 🔧下期预告
- ✨结论
🎉前言
在上期内容【6.2 Redis脚本与Lua】中,我们探讨了如何通过Lua脚本在Redis中实现复杂的原子性操作。通过使用Lua脚本,我们能够将多个Redis命令封装为一个操作,以确保在高并发环境下的数据一致性。这种机制极大地方便了开发者在处理复杂逻辑时的需求。
然而,尽管使用Lua脚本可以在一定程度上保证操作的原子性,分布式环境下的并发操作依然会引发数据冲突与不一致的问题。为了更好地控制并发访问,我们引入了分布式锁的概念。分布式锁不仅能确保在多个进程或服务器中只有一个实例能够访问特定资源,还能有效地解决由于并发操作引发的数据不一致问题。
在本期【6.3 Redis分布式锁】中,我们将深入探讨分布式锁的概念及其应用场景,讲解如何利用Redis实现分布式锁,并介绍广泛应用于业界的Redlock算法的原理与实现。通过这些知识,您将能够在实际项目中合理地使用分布式锁,保证系统的安全与稳定。
接下来的章节【6.4 Redis消息队列】将进一步探讨如何利用Redis构建高效的消息队列系统。在分布式系统中,消息队列是实现解耦和异步处理的重要工具,我们将讨论如何使用Redis实现高可用的消息队列,并介绍一些最佳实践。
🚦Redis分布式锁的概念与应用场景
🍃1.1 什么是分布式锁?
分布式锁是一种用于解决在分布式系统中多个进程或线程并发访问共享资源时的数据一致性问题的机制。其主要目标是确保同一时间只有一个进程能够访问某个资源,从而避免因并发引发的数据错误。例如,在处理用户注册、订单创建等业务时,如果多个请求同时尝试修改同一资源,就可能导致数据的不一致。
🍂1.2 应用场景
分布式锁广泛应用于以下几种场景:
-
资源控制:在多个服务实例中,需要确保只有一个实例可以修改共享资源,如更新配置文件或修改数据库记录。
-
防止重复操作:如用户注册或订单创建,避免由于重复请求导致的业务逻辑错误。
-
任务调度:在分布式任务调度中,使用分布式锁确保某个任务在多个工作节点中只被一个节点执行。
-
限流控制:在高并发场景下,可以使用分布式锁控制访问频率,确保系统的稳定性。
⚙️使用Redis实现分布式锁
Redis作为一个高性能的键值数据库,因其快速的读写速度和丰富的数据结构,成为实现分布式锁的理想选择。接下来,我们将介绍如何使用Redis实现一个简单的分布式锁。
🌼2.1 基本思路
实现分布式锁的基本思路如下:
-
加锁:通过设置一个键(如
lock:resource
)来表示对某个资源的锁定状态。设置的键值通常是一个UUID,用于标识请求的唯一性。我们需要设置一个过期时间,防止因锁未释放导致的死锁问题。 -
检测锁:在尝试获取锁之前,先检查该键是否存在。如果不存在,则可以加锁;如果存在,则表示资源被其他进程占用。
-
释放锁:在业务逻辑执行完成后,及时释放锁,即删除之前设置的键。
🌻2.2 示例代码
以下是一个简单的Python示例,展示了如何使用Redis实现分布式锁:
import redis
import time
import uuid# 连接Redis
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)def acquire_lock(lock_name, acquire_time=10, lock_timeout=5):identifier = str(uuid.uuid4()) # 生成一个唯一的标识符lock_key = f'lock:{lock_name}'# 获取当前时间end = time.time() + acquire_timewhile time.time() < end:# 尝试设置锁,只有在锁不存在的情况下设置成功if redis_client.set(lock_key, identifier, ex=lock_timeout, nx=True):return identifier # 返回标识符,表示锁获取成功time.sleep(0.001) # 等待一段时间再重试return False # 获取锁失败def release_lock(lock_name, identifier):lock_key = f'lock:{lock_name}'# 释放锁,只有锁的持有者才能释放if redis_client.get(lock_key) == identifier:redis_client.delete(lock_key)# 使用示例
lock_id = acquire_lock('my_resource')
if lock_id:try:# 业务逻辑处理print("Lock acquired! Performing critical operation...")time.sleep(2) # 模拟处理时间finally:release_lock('my_resource', lock_id)
else:print("Could not acquire lock. Resource is in use.")
🥀2.3 代码解析
-
acquire_lock:该函数尝试获取分布式锁,若获取成功则返回锁的唯一标识符;若获取失败,则返回
False
。 -
release_lock:该函数用于释放锁,只有持有锁的进程才能释放。通过比较锁的标识符,确保不会错误释放其他进程的锁。
-
使用示例:通过调用
acquire_lock
和release_lock
,我们实现了对共享资源的安全访问。
🧩Redlock算法的原理与实现
虽然上面的实现能够在简单场景中正常工作,但在复杂的分布式环境中,单个Redis实例的锁机制可能无法满足高可用性的需求。为了解决这一问题,开发者发明了Redlock算法。
🌴3.1 Redlock算法原理
Redlock算法的主要思想是通过多个Redis实例来实现分布式锁的高可用性。具体步骤如下:
-
准备多个Redis节点:一般推荐使用5个Redis实例,以便提供足够的容错能力。
-
请求加锁:客户端向所有Redis实例发送加锁请求,并记录成功加锁的节点数。为了确保一致性,客户端需要在特定的时间窗口内成功获得多数节点的锁(通常为超过N/2+1)。
-
设置锁的过期时间:同样地,为每个加锁请求设置合理的过期时间,防止死锁的发生。
-
释放锁:当锁的持有者完成操作后,客户端会向所有Redis实例释放锁。
🌲3.2 Redlock的实现示例
以下是Redlock算法的简单实现示例,代码以Python为例:
import redis
import time
import uuid# 创建多个Redis连接
redis_nodes = [redis.StrictRedis(host='localhost', port=6379, db=0),redis.StrictRedis(host='localhost', port=6380, db=0),redis.StrictRedis(host='localhost', port=6381, db=0),redis.StrictRedis(host='localhost', port=6382, db=0),redis.StrictRedis(host='localhost', port=6383, db=0)
]def redlock_acquire(lock_name, acquire_time=10, lock_timeout=5):identifier = str(uuid.uuid4())lock_key = f'lock:{lock_name}'lock_count = 0end = time.time() + acquire_timefor node in redis_nodes:if time.time() > end:breakif node.set(lock_key, identifier, ex=lock_timeout, nx=True):lock_count += 1# 判断是否获得了多数锁if lock_count > len(redis_nodes) // 2:return identifier # 返回标识符else:# 如果未获得足够的锁,释放已获得的锁for node in redis_nodes:if node.get(lock_key) == identifier:node.delete(lock_key)return Falsedef redlock_release(lock_name, identifier):lock_key = f'lock:{lock_name}'for node in redis_nodes:if node.get(lock_key) == identifier:node.delete(lock_key)# 使用Redlock的示例
lock_id = redlock_acquire('my_resource')
if lock_id:try:print("Redlock acquired! Performing critical operation...")time.sleep(2) # 模拟处理时间finally:redlock_release('my_resource', lock_id)
else:print("Could not acquire Redlock. Resource is in use.")
🌾3.3 代码解析
-
redlock_acquire:该函数尝试在多个Redis节点中获取锁。如果成功获得的锁数量超过一半,则认为锁获取成功。
-
redlock_release:该函数负责释放在多个节点中获得的锁,确保不会因未及时释放锁而导致其他进程或节点的阻塞。如果锁的持有者与原加锁请求的标识符匹配,则允许释放锁。
-
使用Redlock的示例:我们尝试通过
redlock_acquire
获取锁,执行重要的业务逻辑后,通过redlock_release
释放锁。Redlock保证了在多个Redis实例中,只有一个客户端能够在分布式环境中安全地持有锁。
☘️3.4 Redlock的优势与局限性
🍀3.4.1 优势
-
高可用性:Redlock通过多个Redis实例确保即使部分节点宕机或发生网络分区,系统仍然能够正常工作。
-
一致性保障:它能够确保在分布式环境下的强一致性,避免了单点故障的问题。
🍁3.4.2 局限性
-
网络延迟:由于需要与多个Redis节点交互,Redlock的锁获取和释放过程可能受到网络延迟的影响,在极端情况下可能导致锁获取失败。
-
实现复杂度:相比单节点锁,Redlock的实现复杂度较高,可能需要额外的维护成本。
🛠️Redis分布式锁的最佳实践
在实际的生产环境中,使用分布式锁时需要注意以下几点:
-
合理设置锁的超时时间:锁的超时时间过短可能导致任务未完成锁就自动释放;而过长的锁超时可能会引发资源长时间被占用的风险。
-
避免锁死:在可能发生锁死的场景中,建议结合看门狗机制(如通过定期刷新锁的过期时间)来确保锁在长时间执行的任务中不会意外释放。
-
锁的可重入性:如果同一进程需要多次加锁操作,可考虑实现可重入锁,即允许持有锁的进程再次请求锁。
-
适当的回退策略:如果无法获取到锁,合理的回退策略(如指数回退)可以减少高并发下的资源争抢和锁获取失败的情况。
🔧下期预告
在下一节【6.4 Redis消息队列】中,我们将深入讨论Redis作为消息队列的应用。消息队列在分布式系统中起着至关重要的作用,它能够实现不同系统之间的解耦与异步通信,提升系统的可扩展性。Redis的列表和发布/订阅机制为实现高效的消息队列提供了强有力的支持。我们还将探讨如何使用Redis的队列模型处理大规模并发和流量激增场景。
✨结论
Redis分布式锁为分布式系统中的数据一致性和资源控制提供了可靠的解决方案。通过Redis自带的set
命令和UUID机制,我们可以快速实现一个简单的分布式锁。同时,Redlock算法则为多节点环境下提供了更高可用性和一致性保证,是应对复杂分布式场景的有效工具。
Redis分布式锁应用广泛,如任务调度、资源控制、限流等,并且在实际应用中,合理的锁超时设置和合适的回退策略能够进一步提升系统的稳定性和效率。希望通过本文的示例和解析,能够帮助您更好地理解并掌握Redis分布式锁的使用。
在接下来的Redis学习之旅中,消息队列将是我们继续探索的重要内容,敬请期待!
相关文章:

滚雪球学Redis[6.3讲]:Redis分布式锁的实战指南:从基础到Redlock算法
全文目录: 🎉前言🚦Redis分布式锁的概念与应用场景🍃1.1 什么是分布式锁?🍂1.2 应用场景 ⚙️使用Redis实现分布式锁🌼2.1 基本思路🌻2.2 示例代码🥀2.3 代码解析 &#…...

springboot二手汽车交易平台-计算机毕业设计源码82053
目录 1 绪论 1.1研究背景 1.2研究意义 1.3国内外研究现状 2 二手汽车交易平台系统分析 2.1 可行性分析 2.2 系统流程分析 2.3 功能需求分析 2.4 性能需求分析 3 二手汽车交易平台概要设计 3.1 系统体系结构设计 3.2总体功设计 3.3子模块设计设计 3.4 数据库设计 …...

typescript 中的类型推断
在 TypeScript 中,类型推断(Type Inference)是一种编译器自动确定变量或表达式类型的能力。这大大减少了需要显式声明类型的代码量,使得代码更加简洁和易读。TypeScript 的类型推断机制非常强大,可以在很多情况下自动推…...

linux 隐藏文件
在Linux中,隐藏文件以点(.)开头的文件或文件夹被认为是隐藏文件。隐藏文件通常用于存储系统配置文件或敏感文件。 以下是几种不同的方法来隐藏文件或文件夹: 方法1:在文件或文件夹名字前面加上点(.&#…...

【网络协议栈】Tcp协议(上)结构的解析 和 Tcp中的滑动窗口(32位确认序号、32位序号、4位首部长度、6位标记位、16为窗口大小、16位紧急指针)
绪论 “没有那么多天赋异禀,优秀的人总是努力翻山越岭。”本章主要讲到了再五层网络协议从上到下的第二层传输层中使用非常广泛的Tcp协议他的协议字段结构,通过这些字段去认识其Tcp协议运行的原理底层逻辑和基础。后面将会再写一篇Tcp到底是通过什么调…...

手表玻璃盖板视觉贴合
在手表生产过程中,贴合加工是一个至关重要的环节,它涉及将手表的盖板与LCM模组或各种功能片进行精准贴合。这一过程不仅要求高度的精度,还追求效率与稳定性,以满足现代可穿戴设备日益增长的市场需求。然而,当前行业在这…...

电信和互联网行业数据安全评估师CCRC-DSA人才强基计划
“电信和互联网行业数据安全人才强基计划”(以下简称“强基计划”)自 2022 年 4 月启动伊始,始终秉持以人才需求为导向,以体系化能力建设为重点,扎实铸就数据安全人才培养品牌,力促行业数据安全人才培养工作…...

MQTTnet 4.3.7.1207 (最新版)使用体验,做成在线客服聊天功能,实现Cefsharp的物联的功能(如远程打开新网址)
一、MQTTnet 4.3.x版本客户端 将客户端集成到 cefsharp 定制浏览器中,实现物联网功能 网上很多代码是3.x版本代码,和4.x版本差异性较大,介绍较为简单或不系统 二、部分代码说明 初始化,初始化》连接服务端》发布上线信息(遗嘱)ConnectAsync等 订阅主题:SubscribeAsync 接…...

将java项目jar包打包成exe服务
1.结构展示 2.注意事项 前提: 环境准备:jdk8 和 .net支持 { 1.控制面板》程序和功能》启用和关闭windows功能》.net的勾选》2.jdk8自行百度安装环境3.其他项目必须的软件环境安装等(数据库...) }第一次准备: 1.将打包好的jar包放到premiumServices.exe…...

Django请求响应对象
在 Django 中,请求(request)和响应(response)对象是处理 HTTP 请求和返回 HTTP 响应的核心。它们分别由 Django 的 HttpRequest 和 HttpResponse 类表示。 HttpRequest 对象 HttpRequest 对象包含了客户端发送的所有…...

DevExpress中文教程 - 如何在静态SSR模式下使用Blazor Drawer组件?
Microsoft的 .NET 8 UI框架引入了静态服务器端呈现模式(静态SSR)——组件在服务器端呈现,然后返回到客户端,没有任何交互,DevExpress Blazor Drawer组件需要交互式呈现模式来动态地改变其IsOpen状态。 在本文中&#…...

商汤科技十周年公布新战略,将无缝集成算力、模型及应用
10月18日,恰逢商汤科技十周年庆典,“2024商汤十周年国际论坛:迈向AI 2.0共融新时代”在香港科学园成功举办。 据「TMT星球」了解,来自全球的行业领袖、政府代表、AI专家共聚于此,共同探讨AI行业的未来。 活动上&…...

【如何获取股票数据07】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深A股历史分时MA数据获取实例演示及接口API说明文档
最最近一两年内,股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步,就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息,这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要…...

Rust语法基础
注释 所有的开发者都在努力使他们的代码容易理解,但有时需要额外的解释。在这种情况下,开发者在他们的源码中留下注释,编译器将会忽略掉这些内容,但阅读源码的人可能会发现有用。 和大多数的编程语言一样,主要有一下两种: 单行注释 // 多行注释 /* */ 基本数据类型 Ru…...

AWS WAF实现API安全防护
在当今的互联网环境中,API安全防护变得越来越重要。本文将介绍如何使用AWS WAF(Web Application Firewall)来实现有效的API安全防护策略。 背景 我们有一个API服务,其URL模式如下: https://dev.example.com/bff-app/sec/v1/module-a/feature-a/sub-feature-a我们需要使用AWS…...

vue将table转换为pdf导出
安装依赖: 首先,你需要安装 jspdf 和 html2canvas 这两个库。 npm install jspdf html2canvas创建Vue组件: 创建一个Vue组件,用于显示表格并提供导出PDF的功能。 <template> <div> <div id"table-contain…...

20240818 字节跳动 笔试
文章目录 1、编程题1.11.21.31.4岗位:BSP驱动开发工程师-OS 题型:4 道编程题 1、编程题 1.1 小红的三消游戏: 小红在玩一个三消游戏,游戏中 n 个球排成一排,每个球都有一个颜色。若有 3 个颜色相同的球连在一起,则消除这 3 个球,然后剩下的球会重新连在一起。在没有 …...

在Debian上安装向日葵
说明: 因为之前服务器上安装了 PVE (Proxmox VE),之前是用 Proxmox VE 进行服务器资源管理的。出于某些原因,现在不再通过 PVE构建的虚拟机来使用计算资源,而是通过 PVE 自带的 Debian 系统直接使用虚拟机资源(因为积…...

13.2 Linux_网络编程_UNIX域套接字
概述 什么是UNIX域套接字: UNIX域套接字是使用套接字进行本地通信,TCP/UDP是使用套接字进行网络通信。UNIX域套接字也有域流式套接字和域数据报套接字,这两种形式域TCP/UDP的含义类似,使用步骤也完全一致。 bind时绑定的结构体…...

10.22 多进程间通信-共享内存、信号量集
练习:通过信号量集完成对共享内存的同步操作 案例代码: 分文件编译:信号量集部分 sem.h #ifndef __SEM_H__ #define __SEM_H__ #include <myhead.h> union semun {int val; /* Value for SETVAL */struct semid_ds…...

输入输出管理器的使用
解释 InputMgr 是一个输入管理器,主要用于检测并管理用户的输入事件(例如键盘和鼠标输入)。它通过监听输入事件,并利用事件中心 (EventCenter) 来触发相应的事件。在这里,你可以管理多种输入类型,如按下、…...

windows连接linux服务器上的jupyter lab
文章目录 服务器上开启jupyter lab本地cmd将端口8888映射到服务器的8889上本地浏览器打开8888端口 服务器上开启jupyter lab jupyter-lab --ip 0.0.0.0 --port 8889 --no-browser --allow-root本地cmd将端口8888映射到服务器的8889上 ssh -N -f -L localhost:8888:localhost:…...

golang生成并分析cpu prof文件
1. 定义一个接口,请求接口时,生成cpu.prof文件 在主协程中新启一个协程,当请求接口时,生成一个60秒的cpu.prof文件 go func() {http.HandleFunc("/prof", startProfileHandler)http.ListenAndServe(":9092"…...

【Python爬虫实战】XPath与lxml实现高效XML/HTML数据解析
🌈个人主页:https://blog.csdn.net/2401_86688088?typeblog 🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、为什么学习xpath和lxml (一)高效解析和提取数据 …...

软件测试学习笔记丨Selenium学习笔记:元素定位与操作
本文转自测试人社区,原文链接:https://ceshiren.com/t/topic/22510 本文为霍格沃兹测试开发学社的学习经历分享,写出来分享给大家,希望有志同道合的小伙伴可以一起交流技术,一起进步~ 说明:本篇博客基于sel…...

在 HTML 中,<input> 元素支持的事件汇总
在 HTML 中,<input> 元素支持多种事件,这些事件可以在用户与输入字段交互时触发。以下是一些常见的 <input> 事件: input: 当 <input> 元素的值发生变化时触发。适用于文本、数字、日期等类型的输入。 change: 当 <inp…...

vue3【实战】 渲染 md 文件(markdown语法 .md后缀的文件)
1. 安装相关插件 npm i unplugin-vue-markdown markdown-it-prism prism unhead/vue2. 添加配置 src/main.ts // 给 md 文件创建头部 import { createHead } from unhead/vue // md 文件中代码高亮的样式 import prismjs/themes/prism.css // 自定义 md 文件的样式 import /as…...

Sora高端制造业WordPress外贸主题
Sora是一款专为高端制造业设计的WordPress主题,由国内知名wordpress开发团队简站wordpress主题开发,它以红色为主色调,适合外贸企业出海建独立站的模板。这个主题适用于WordPress 6.0及以上版本,并且只服务于真正有需要的用户。主…...

windows安装superset及各种问题解决
1,背景 先说说背景,之前在2月份已经安装过superset3.1.1,当时还没有提示SECRET_KEY异常,能正常运行,且已配置数据库连接. 2,报错信息及解决途径 1,创建admin时,提示Error! User already exists 这个是因为之前已经创建过admin用户,需要删除C:\Users\用户名\.superset下的.…...

JMeter模拟并发请求
PostMan不是严格意义上的并发请求工具,实际是串行的,如果需要测试后台接口并发时程序的准确性,建议采用JMeter工具。 案例:JMeter设置20个并发卖票请求,查看后台是否存在超卖的情况 方式一:一共10张票&…...