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

滚雪球学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 基本思路

实现分布式锁的基本思路如下:

  1. 加锁:通过设置一个键(如lock:resource)来表示对某个资源的锁定状态。设置的键值通常是一个UUID,用于标识请求的唯一性。我们需要设置一个过期时间,防止因锁未释放导致的死锁问题。

  2. 检测锁:在尝试获取锁之前,先检查该键是否存在。如果不存在,则可以加锁;如果存在,则表示资源被其他进程占用。

  3. 释放锁:在业务逻辑执行完成后,及时释放锁,即删除之前设置的键。

🌻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 代码解析

  1. acquire_lock:该函数尝试获取分布式锁,若获取成功则返回锁的唯一标识符;若获取失败,则返回False

  2. release_lock:该函数用于释放锁,只有持有锁的进程才能释放。通过比较锁的标识符,确保不会错误释放其他进程的锁。

  3. 使用示例:通过调用acquire_lockrelease_lock,我们实现了对共享资源的安全访问。

🧩Redlock算法的原理与实现

虽然上面的实现能够在简单场景中正常工作,但在复杂的分布式环境中,单个Redis实例的锁机制可能无法满足高可用性的需求。为了解决这一问题,开发者发明了Redlock算法。

🌴3.1 Redlock算法原理

Redlock算法的主要思想是通过多个Redis实例来实现分布式锁的高可用性。具体步骤如下:

  1. 准备多个Redis节点:一般推荐使用5个Redis实例,以便提供足够的容错能力。

  2. 请求加锁:客户端向所有Redis实例发送加锁请求,并记录成功加锁的节点数。为了确保一致性,客户端需要在特定的时间窗口内成功获得多数节点的锁(通常为超过N/2+1)。

  3. 设置锁的过期时间:同样地,为每个加锁请求设置合理的过期时间,防止死锁的发生。

  4. 释放锁:当锁的持有者完成操作后,客户端会向所有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 代码解析

  1. redlock_acquire:该函数尝试在多个Redis节点中获取锁。如果成功获得的锁数量超过一半,则认为锁获取成功。

  2. redlock_release:该函数负责释放在多个节点中获得的锁,确保不会因未及时释放锁而导致其他进程或节点的阻塞。如果锁的持有者与原加锁请求的标识符匹配,则允许释放锁。

  3. 使用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算法

全文目录&#xff1a; &#x1f389;前言&#x1f6a6;Redis分布式锁的概念与应用场景&#x1f343;1.1 什么是分布式锁&#xff1f;&#x1f342;1.2 应用场景 ⚙️使用Redis实现分布式锁&#x1f33c;2.1 基本思路&#x1f33b;2.2 示例代码&#x1f940;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 中&#xff0c;类型推断&#xff08;Type Inference&#xff09;是一种编译器自动确定变量或表达式类型的能力。这大大减少了需要显式声明类型的代码量&#xff0c;使得代码更加简洁和易读。TypeScript 的类型推断机制非常强大&#xff0c;可以在很多情况下自动推…...

linux 隐藏文件

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

【网络协议栈】Tcp协议(上)结构的解析 和 Tcp中的滑动窗口(32位确认序号、32位序号、4位首部长度、6位标记位、16为窗口大小、16位紧急指针)

绪论​ “没有那么多天赋异禀&#xff0c;优秀的人总是努力翻山越岭。”本章主要讲到了再五层网络协议从上到下的第二层传输层中使用非常广泛的Tcp协议他的协议字段结构&#xff0c;通过这些字段去认识其Tcp协议运行的原理底层逻辑和基础。后面将会再写一篇Tcp到底是通过什么调…...

手表玻璃盖板视觉贴合

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

电信和互联网行业数据安全评估师CCRC-DSA人才强基计划

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

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.其他项目必须的软件环境安装等&#xff08;数据库...&#xff09; }第一次准备: 1.将打包好的jar包放到premiumServices.exe…...

Django请求响应对象

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

DevExpress中文教程 - 如何在静态SSR模式下使用Blazor Drawer组件?

Microsoft的 .NET 8 UI框架引入了静态服务器端呈现模式&#xff08;静态SSR&#xff09;——组件在服务器端呈现&#xff0c;然后返回到客户端&#xff0c;没有任何交互&#xff0c;DevExpress Blazor Drawer组件需要交互式呈现模式来动态地改变其IsOpen状态。 在本文中&#…...

商汤科技十周年公布新战略,将无缝集成算力、模型及应用

10月18日&#xff0c;恰逢商汤科技十周年庆典&#xff0c;“2024商汤十周年国际论坛&#xff1a;迈向AI 2.0共融新时代”在香港科学园成功举办。 据「TMT星球」了解&#xff0c;来自全球的行业领袖、政府代表、AI专家共聚于此&#xff0c;共同探讨AI行业的未来。 活动上&…...

【如何获取股票数据07】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深A股历史分时MA数据获取实例演示及接口API说明文档

最最近一两年内&#xff0c;股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步&#xff0c;就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息&#xff0c;这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要…...

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导出

安装依赖&#xff1a; 首先&#xff0c;你需要安装 jspdf 和 html2canvas 这两个库。 npm install jspdf html2canvas创建Vue组件&#xff1a; 创建一个Vue组件&#xff0c;用于显示表格并提供导出PDF的功能。 <template> <div> <div id"table-contain…...

20240818 字节跳动 笔试

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

在Debian上安装向日葵

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

13.2 Linux_网络编程_UNIX域套接字

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

10.22 多进程间通信-共享内存、信号量集

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

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

push [特殊字符] present

push &#x1f19a; present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中&#xff0c;push 和 present 是两种不同的视图控制器切换方式&#xff0c;它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...