Redis典型应用 - 分布式锁

news2024/11/24 19:29:18

文章目录

目录

文章目录

1. 什么是分布式锁

2. 分布式锁的基本实现

3. 引入过期时间

4. 引入校验Id

5. 引入 watch dog(看门狗)

6. 引入redlock算法

工作原理

Redlock的优点:

总结


1. 什么是分布式锁

在一个分布式系统中,也可能会出现多个节点访问一个共享资源的情况,此时就需要通过互斥锁来进行控制。

而像是Java中的synchronized 或者 C++ 的 std::mutex,这样的锁只能在当前线程生效,无法作用于分布式系统。

本质上就是使=用一个公共的服务器, 来记录加锁状态。这个公共的服务器可以是 Redis, 也可以是其他组件(比如 MySQL 或者 ZooKeeper 等), 还可以 是我们自己写的一个服务。

2. 分布式锁的基本实现

思路非常简单. 本质上就是通过一个键值对来标识锁的状态。

引入一个场景(如下图)来帮助理解分布式锁:在高并发的购票系统中,多个用户可能同时请求购买同一张票。为了避免超卖(即同一张票被多次售出),需要对购票操作进行控制。

上面这张图所示显然是没有任何安全保障的,我们使用Redis来存储键值对来作为分布式锁来使用。

此时, 如果 买票服务器1 尝试买票, 就需要先访问 Redis, 在 Redis 上设置⼀个键值对。 比如 key 就是车次。如果这个操作设置成功, 就视为当前没有节点对该 001 车次加锁, 就可以进行数据库的读写操作。 操作完成之后, 再把 Redis 上刚才的这个键值对给删除掉。

如果在买票服务器1 操作数据库的过程中, 买票服务器2 也想买票, 也会尝试给 Redis 上写一个键值对, key 同样是车次。 但是此时设置的时候发现该车次的 key 已经存在了, 则认为已经有其他服务器正在持有锁, 此时 服务器2 就需要等待或者暂时放弃。

Redis 中提供了 setnx 操作, 正好适合这个场景。 即: key 不存在就设置, 存在则直接失败。

3. 引入过期时间

如果正在买票的过程中, Redis服务器直接挂了, 就会导致解锁操作无法进行,可能会导致其他服务器一直无法获取到锁。

为了解决这个问题,可以在设置Key的时候引入过期时间,即这把锁最多持有多久就会被释放。

可以使用 set ex nx 的方式, 在设置锁的同时把过期时间设置进去。

4. 引入校验Id

对于 Redis 中写入的加锁键值对, 其他的节点也是可以删除的。

为了解决这个问题,我们可以引入一个校验Id, 用来判断加锁和解锁是不是一个服务器进行的,如果不是就无法进行解锁。比如设置key的时候将Key的值设置为服务器Id。

String key = [要加锁的资源 id];
String serverId = [服务器的编号];

// 加锁, 设置过期时间为 10s
redis.set(key, serverId, "NX", "EX", "10s");

// 执⾏各种业务逻辑, ⽐如修改数据库数据. 
doSomeThing();

// 解锁, 删除 key. 但是删除前要检验下 serverId 是否匹配. 
if (redis.get(key) == serverId) {
 redis.del(key);
}

5. 引入 watch dog(看门狗)

上述方案仍然存在一个重要问题。当我们设置了 key 过期时间之后 (比如 10s), 仍然存在一定的可能性, 当任务还没执行完成, key 就先过期了。 这就导致锁提前失效。

所谓 watch dog, 本质上是加锁的服务器上的一个单独的线程, 通过这个线程来对锁过期时间进行 "续 约"

举个具体的例子:

初始情况下设置过期时间为 10s. 同时设定看门狗线程每隔 3s 检测⼀次。那么当 3s 时间到的时候, 看门狗就会判定当前任务是否完成。

  • 如果任务已经完成, 则直接通过 lua 脚本的方式, 释放锁(删除 key)。
  • 如果任务未完成, 则把过期时间重写设置为 10s. (即 "续约")。

Redis的Lua脚本是一种在Redis服务器上执行的脚本,允许用户通过Lua语言编写自定义命令,以实现复杂的操作。使用Lua脚本可以提高性能,因为它们在Redis服务器端执行,减少了客户端与服务器之间的网络往返。

  1. 原子性:Lua脚本在Redis中是原子执行的,这意味着在脚本执行期间,其他命令不会干扰它。这对于需要保证数据一致性的操作非常重要。

  2. 性能:通过在服务器端执行脚本,可以减少网络延迟和数据传输,提高性能。

  3. 灵活性:用户可以编写复杂的逻辑,结合多个Redis命令,处理数据并返回结果。

6. 引入redlock算法

实践中的 Redis 一般是以集群的方式部署的 (至少是主从的形式, 而不是单机)。那么就可能出现以下极端情况:

服务器1 向 master 节点进行加锁操作. 这个写入 key 的过程刚刚完成, master 挂了; slave 节 点升级成了新的 master 节点. 但是由于刚才写入的这个 key 尚未来得及同步给 slave 呢, 此时 就相当于 服务器1 的加锁操作形同虚设了, 服务器2 仍然可以进行加锁 (即给新的 master 写 入 key。 因为新的 master 不包含刚才的 key。(倒霉 O(∩_∩)O哈哈~)

Redlock是一种分布式锁算法,旨在解决在分布式系统中管理锁的挑战。它是由Redis的创始人Antirez提出的,主要用于确保在多个Redis实例之间的互斥访问。

工作原理

  1. 获取锁

    • 客户端向多个Redis实例请求获取锁。
    • 客户端在每个实例上设置一个唯一的锁键,并设置一个过期时间(TTL)。
    • 客户端需要在大多数实例上成功设置锁(例如,如果有5个实例,则需要在3个实例上成功设置)。
  2. 锁的有效性

    • 锁的有效性由设置的过期时间决定。如果客户端在持有锁期间未能完成任务,锁会在过期后自动释放,避免死锁。
  3. 释放锁

    • 客户端完成任务后,可以释放锁。释放锁时,客户端需要确保只释放自己持有的锁,以避免其他客户端的锁被错误释放。

Redlock的优点:

  • 高可用性:通过在多个Redis实例上获取锁,Redlock可以容忍部分实例的故障。
  • 简单易用:Redlock的实现相对简单,易于集成到现有的Redis应用中。
  • 避免死锁:通过设置过期时间,Redlock可以避免因客户端崩溃或网络问题导致的死锁。

工作原理图解:

有5个实例,则需要在3个实例上成功设置: 高可用,可以容忍部分实例的故障。

设置一个过期时间(TTL):避免因客户端崩溃或网络问题导致的死锁。


总结

以上就是这篇博客的主要内容了,大家多多理解,下一篇博客见!

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

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

相关文章

QT 编译报错:C3861: ‘tr‘ identifier not found

问题: QT 编译报错:C3861: ‘tr’ identifier not found 原因 使用tr的地方所在的类没有继承自 QObject 类 或者在不在某一类中, 解决方案 就直接用类名引用 :QObject::tr( )

关于易优cms自定义字段不显示的问题

今天在该易优cms自定义字段&#xff0c;折腾了大半天没显示出来&#xff0c;原来是改错对方了。 主要引用的时候 要放在list标签内&#xff0c;不要看文档&#xff0c;把addfields 放在list标签上 例如 {eyou:list loop8} <li><a href"{$field.arcurl}">…

基于yolov8的电动车佩戴头盔检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的电动车佩戴头盔检测系统利用了YOLOv8这一先进的目标检测模型&#xff0c;旨在提高电动车骑行者的安全意识&#xff0c;减少因未佩戴头盔而导致的交通事故风险。YOLOv8作为YOLO系列的最新版本&#xff0c;在检测速度和精度上均进行了优化&#xff0c;…

✨机器学习笔记(一)—— 监督学习和无监督学习

1️⃣ 监督学习&#xff08;supervised learning&#xff09; ✨ 两种主要类型的监督学习问题&#xff1a; 回归&#xff08;regression&#xff09;&#xff1a;predict a number in infinitely many possible outputs. 分类&#xff08;classification&#xff09;&#xff1…

C#串口助手初级入门

1.创建项目 修改项目名称与位置&#xff0c;点击创建 2.进入界面 在视图中打开工具箱&#xff0c;鼠标拖动&#xff0c;便可以在窗口添加控件&#xff0c;右边可以查看与修改属性 3.解决方案资源管理器 发布之前&#xff0c;需要修改相关的信息&#xff0c;比如版本号&#x…

Lombok jar包引入和用法

大家好&#xff0c;今天分享一个在编写代码时的快捷方法。 当我们在封装实体类时&#xff0c;会使用set、get等一些方法。如下图&#xff0c;不但费事还影响代码的美观。 那么如何才能减少代码的冗余呢&#xff0c;首先lib中导入lombok的jar包并添加库。 此处我已导入&#xf…

插件:清理maven错误缓存.bat

插件&#xff1a;https://pan.baidu.com/s/1nHIxHoo1C4MvFlW7QbZe5Q?pwd7zenhttps://pan.baidu.com/s/1nHIxHoo1C4MvFlW7QbZe5Q?pwd7zen没错误缓存时&#xff1a; 有错误缓存时&#xff1a;

真实案例分享:零售企业如何避免销售数据的无效分析?

在零售业务的数据分析中&#xff0c;无效分析不仅浪费时间和资源&#xff0c;还可能导致错误的决策。为了避免这种情况&#xff0c;企业必须采取策略来确保他们的数据分析工作能够产生实际的商业价值。本文将通过行业内真实的案例&#xff0c;探讨零售企业如何通过精心设计的数…

springboot数据库连接由localhost改成IP以后访问报错500(2024/9/7

步骤很详细&#xff0c;直接上教程 情景复现 一.没改为IP之前正常 二.改完之后报错 问题分析 SQL没开启远程连接权限 解决方法 命令行登入数据库 mysql -u root -p切换到对应数据库 use mysql;设置root用户的连接权限允许其他IP连接数据库 update user set host % whe…

jmeter执行python脚本,python脚本的Faker库

jmeter安装 jython的插件jar包 通过如下地址下载jython-standalone-XXX.jar包并放到jmeter的XXX\lib\ext目录下面 Downloads | JythonThe Python runtime on the JVMhttps://www.jython.org/download.html 重启jmeter在JSR223中找到jython可以编写python代码执行 python造数据…

一种快速生成CSV的方法

事情是这个样子的 在QQ群在聊把如何100万数据导出成CSV文件&#xff1f;会不会很慢&#xff1f; 俺回了一句“现在的机器性能好&#xff0c;没啥问题”。 然后大家开始谈论机器的配置了。哎&#xff0c;俺的机器配置有点差。 然后俺就进行了一个测试。 测试数据 数据定义…

【C++二分查找】2439. 最小化数组中的最大值

本文涉及的基础知识点 C二分查找 LeetCode2439. 最小化数组中的最大值 给你一个下标从 0 开始的数组 nums &#xff0c;它含有 n 个非负整数。 每一步操作中&#xff0c;你需要&#xff1a; 选择一个满足 1 < i < n 的整数 i &#xff0c;且 nums[i] > 0 。 将 num…

C++ | Leetcode C++题解之第392题判断子序列

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {int n s.size(), m t.size();vector<vector<int> > f(m 1, vector<int>(26, 0));for (int i 0; i < 26; i) {f[m][i] m;}for (int i m - 1; …

.Net6/.Net8(.Net Core) IIS中部署 使用 IFormFile 上传大文件报错解决方案

描述 最近使用.Net6 WebAPI IFormFile对象接收上传文件时大于30MB(兆)的文件就会报错 原因分析 IIS上传文件有大小默认限制大约28.6MB 解决办法 .无论是Net6还是.Net8写法都一样 方法一&#xff1a;IIS可视化操作 1.打开Internet Information Services (llS)管理器&…

Banana Pi BPI-SM9 AI 计算模组采用算能科技BM1688芯片方案设计

产品概述 香蕉派 Banana Pi BPI-SM9 16-ENC-A3 深度学习计算模组搭载算能科技高集成度处理器 BM1688&#xff0c;功耗低、算力强、接口丰富、兼容性好。支持INT4/INT8/FP16/BF16/FP32混合精度计算&#xff0c;可支持 16 路高清视频实时分析&#xff0c;灵活应对图像、语音、自…

LeetCode --- 413周赛

题目列表 3274. 检查棋盘方格颜色是否相同 3275. 第 K 近障碍物查询 3276. 选择矩阵中单元格的最大得分 3277. 查询子数组最大异或值 一、检查棋盘方格颜色是否相同 题目给定两个字符串来表示两个方格的坐标&#xff0c;让我们判断这两个方格的颜色是否相同&#xff0c;这…

C++——关联式容器(2):AVL树(平衡二叉树)

2.AVL树 2.1 AVL树的概念 在学习了二叉搜索树后&#xff0c;我们发现了二叉搜索树可以根据大小比较来进行类似于折半查找的操作&#xff0c;使得搜索时间复杂度达到logn的水准。但是在面对极端情况下&#xff0c;如近似有序的序列&#xff0c;那么整棵树的时间复杂度就有可能退…

【Godot4.3】多边形的斜线填充效果基础实现

概述 图案&#xff08;Pattern&#xff09;填充是一个非常常见的效果。其中又以斜线填充最为简单。本篇就探讨在Godot4.3中如何使用Geometry2D和CanvasItem的绘图函数实现斜线填充效果。 基础思路 Geometry2D类提供了多边形和多边形以及多边形与折线的布尔运算。按照自然的思…

Spring-@Bean的处理流程

Bean前置知识 1 需要再Configuration Class中才能被解析 2 静态Bean也就是标注在static方法上的 实例Bean标注在普通方法上的 所有的Bean在创建之前都会变成BeanDefinition,其中有这样两个属性&#xff1a; setFactoryMethodName&#xff1a;静态方法 setFactoryBeanName&…

【详解 Java 注解】

前言&#xff1a; 注解&#xff08;Annotation&#xff09;是Java中的一种特殊符号&#xff0c;用来为代码提供额外的信息。它不会改变程序的逻辑&#xff0c;只是用来给编译器或工具提供指示。例如&#xff0c;Override 表示一个方法是重写了父类的方法&#xff0c;Deprecated…