【Redis知识点总结】(五)——Redis实现分布式锁

news2024/10/7 12:27:33

Redis知识点总结(五)——Redis实现分布式锁

  • setnx
  • setnx + expire
  • setnx + expire + lua脚本
  • set nx ex
  • set nx ex + 随机值
  • set nx ex + 随机值 + lua脚本
  • set ex nx + 随机值 + lua脚本 + 锁续期
  • Redisson
  • RedLock

在Redis的众多应用场景中,分布式锁是Redis比较重要的一个应用场景,今天我们就来了解一下如何用Redis实现一个分布式锁。

setnx

Redis提供了一个setnx命令,它的作用是在给缓存设置值时,判断key是否存在,如果key已经存在,则不执行操作,只有当缓存中不存在指定key时,才设置该缓存。

使用setnx命令,就可以实现一个简单的分布式锁。我们指定一个特定的值作为setnx命令的key,然后当一个线程要获取分布式锁时,就要通过setnx命令在redis中设置该key,如果设置成功,代表获取分布式锁成功,如果设置失败,表示获取分布式锁失败,那么当前线程就要通过自旋加sleep的机制等待别的线程释放锁。

一个线程通过setnx命令获取到分布式锁后,通过del命令释放锁。释放锁之后,其他线程就可以再次尝试获取锁。

在这里插入图片描述

但是这种方案有一个严重的问题,就是当一个线程获取到锁后,在处理业务逻辑时发生了异常,或者直接宕机了,没能执行del命令释放锁,那么其他线程就会一直获取不到锁。

setnx + expire

单靠setnx命令实现的分布式锁,存在锁得不到释放的风险。因此,我们可以引入expire命令,给key设置一个过期时间。当一个线程执行setnx命令成功后,就通过expire命令给该key设置一个过期时间。假如该线程执行业务逻辑出错,没能执行del命令,那么等该key的过期时间到后,该key也会被失效清理,锁最终还是能得到释放的。

在这里插入图片描述

这样问题就解决了吗?

显然是没有的,因为setnx和expire是两条命令,是非原子性的,如果setnx执行成功,expire命令执行失败,那么锁还是得不到释放。

setnx + expire + lua脚本

setnx命令与expire命令的非原子性问题,可以通过lua脚本去解决,把setnx和expire两条命令包在一个lua脚本里面,就可以保证这两条命令同时成功或者同时失败,这就就具备了原子性。

在这里插入图片描述

除了使用lua脚本,Redis还可以通过一条命令实现setnx加expire两条命令的功能。

set nx ex

我们可以通过以下命令实现setnx命令加expire命令的功能,并且可以保证原子性:

set key value nx ex {过期时间}

在这里插入图片描述

这样,分布式锁得不到释放的问题就彻底解决了。

但是,这样就没问题了吗?显然不是的,还会出现以下这种情况:

在这里插入图片描述

线程1获取分布式锁成功,但是业务处理时间过长,锁早已过期。而线程2随后获取分布式锁成功,进行业务处理。然后这时候线程1处理完了,于是执行del命令释放锁,而此时它释放的是线程2加的锁,并且线程2还没有处理完。线程2的锁被线程1释放后,如果线程3到来,也获取了分布式锁,那么等线程2处理完毕,又会释放掉线程3获取的锁,以此类推,这个分布式锁就失效了。

set nx ex + 随机值

锁错误释放的问题,可以通过给value指定一个随机值来解决,实现方案是这样:

在这里插入图片描述

执行set命令时生成一个随机值作为value值,并且当前线程要保存该随机值。等到当前线程处理完业务逻辑要准备释放锁时,先拿着自己保存的随机值跟redis中的value比对一下,如果相等,才执行del命令释放锁,否则就不能执行del命令。

这样看似可以解决锁误删的问题,一般情况下也确实如此,但是在高并发场景下,有可能会出现以下这种情况:

在这里插入图片描述

线程1执行完业务逻辑后,判断自己的value随机值与redis中的value相等,正准备释放锁,此时正好锁过期了,然后线程2又刚好获取到了锁,然后线程1才执行del命令,那么线程2的锁又被误释放了。

这是低概率事件,非高并发场景其实不用考虑这个问题,但是在高并发场景,还是有可能出现的。问题的原因是if判断与del命令是两个操作,非原子性。

set nx ex + 随机值 + lua脚本

if判断与del命令的非原子性问题,可以通过lua脚本解决,把if判断与del命令用lua脚本包起来,就可以彻底解决锁误释放的问题。

在这里插入图片描述

现在获取锁与释放锁的操作都是原子性的,我们的分布式锁也逐步变得完善,但是还有一个问题,那就是锁过期,也就是业务处理还未结束,锁就过期的这种情况。

我们可以给锁设置一个较长的过期时间,但是我们无法百分之一百保证所有的业务处理都在锁过期前得到释放,如果在某种特殊情况下处理时间比平常的要长,锁还是过期了,那么还是有问题的。

set ex nx + 随机值 + lua脚本 + 锁续期

锁过期的解决办法,就是开一个“看门狗”线程,进行锁续期,这个线程会定时地不断的检测主线程释放处理完毕并且释放了锁,如果主线程还没有处理完毕,那么这个“看门狗”线程就会执行expire命令进行锁续期。

在这里插入图片描述

此时,我们的分布式锁就完美了。但是,这样太复杂了吧?我们为了实现一个分布式锁,居然要处理这么多问题,使得原本简单的代码变得非常复杂,那有没有一个开源框架,可以帮我们实现这一切,我们只需要简单的调用一下就完事了呢?

当然是有的,那就是使用Redisson的分布式锁。

Redisson

Redisson是一个用Java语言实现的用于操作Redis的客户端工具包,比起原生的Jedis工具包,Redisson是更好用,功能更强大的。

Redisson就自带了分布式锁的实现,把我们上面说到的获取锁的原子性、释放锁的原子性、利用看门狗线程进行锁续期等一系列的操作都包装起来,对外暴露了简单的获取锁和释放锁的方法,我们无需手动编码实现这复杂的获取锁与释放锁的逻辑,只要使用Redisson提供的分布式锁的方法,就可以实现分布式锁的功能。

在这里插入图片描述

使用Redisson,我们就可以轻松简单的实现分布式锁的功能,但是如果Redis是单节点的话,Redis宕机了,那么所有线程都无法获取到锁了。

在这里插入图片描述

解决Redis单节点的问题,自然是上多个Redis节点。但是上了多个Redis节点之后,分布式锁又怎么实现呢?

RedLock

Redis的作者提出了一个多redis节点的分布式锁实现方案,那就是RedLock。

首先,我们要启5个redis节点,但是这5个节点是独立的节点,互相之间没有主从关系,也没有组成集群。

当一个线程加锁时,需要按顺序的访问这五个节点,还是利用set nx ex或set nx px命令进行加锁。

当一个线程在至少3个以上的redis节点成功执行了set命令,那么就表示获取锁成功,此时该线程要计算锁的有效期,需要用锁的有效期减去加锁耗费的时间,如果还有剩余时间,表示获取锁成功,如果没有剩余时间了,那么表示获取锁失败。

如果一个线程获取锁失败,要按相反的顺序向所有redis节点执行del命令释放锁。

在这里插入图片描述

这个RedLock的功能,同样可以通过Redisson实现,只需要调用Redisson提供的简单的API方法即可,无需我们编写复杂的RedLock代码。

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

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

相关文章

Ypay源支付2.8.8免授权聚合免签系统

本帖最后由 renleixiaoxu 于 2024-3-15 09:46 编辑 产品介绍 XPay是专为个人站长打造的聚合免签系统,拥有卓越的性能和丰富的功能。采用全新轻量化的界面UI,让您可以更加方便快捷地解决 知识付费和运营赞助的难题。同时,它基于高性能的Thin…

算法练习:前缀和

目录 1. 一维前缀和2. 二维前缀和3. 寻找数组中心下标4. 除自身以外数组的乘积5. !和为k的子数字6. !和可被k整除的子数组7. !连续数组8. 矩阵区域和 1. 一维前缀和 题目信息: 题目链接: 一维前缀和思路:求前缀和数组,sum dp[r] …

预应力锚杆测力计安装埋设

锚杆测力计用于观测预应力锚杆预应力的形成与变化。当前,预应力锚杆广泛地应用于岩土工程的锚固结构中,通过安装测力计观测锚杆,可以了解锚固力的形成与变化,从而保证监测工程的质量与安全。测力计的安装包括安装测力计和观测锚杆…

【LeetCode热题100】 226. 翻转二叉树(二叉树)

一.题目要求 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 二.题目难度 简单 三.输入样例 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1] 示例 2: 输入:…

餐饮店引流活动方案与最佳营销方案揭秘

想开实体店或正在创业的朋友们,大家好!我是一名资深的实体店创业者,本人经营鲜奶吧5年时间,做的是社区店,今天我将分享一些餐饮店引流活动和营销方案的干货,希望能给大家带来一些启发和帮助。 一、引流活动…

【人工智能】英文学习材料02(每日一句)

🌻个人主页:相洋同学 🥇学习在于行动、总结和坚持,共勉! 目录 Supervised Learning(监督学习) Unsupervised Learning(无监督学习 ) Semi-supervised Learning&#…

【视频图像取证篇】模糊图像增强技术之色彩空间类滤波器场景应用小结

【视频图像取证篇】模糊图像增强技术之色彩空间类滤波器场景应用小结 模糊图像增强技术之色彩空间HSI滤波器—【蘇小沐】 (一)色彩空间滤波器 1、HSI滤波器 HSI(色调、饱和度和强度)滤镜用于将彩色图像转换为 HSI 图像。这意味…

当贝X5 Ultra坚果N1S Ultra哪个好?2024年旗舰投影仪哪家强

家用投影仪在目前年轻用户新家装修的娱乐选择上,有着越来越大的占比。不过虽然投影仪的体验已经逐渐弯道超越传统电视,画面和沉浸感已经抹去了曾经的一些小问题;但仍然有很多雷区和选购的迷惑,以致于在同价位往往陷入不知道哪款更…

展厅设计提高人气的妙招

1、清晰的标识 展厅门口的标识是消费者第一眼能了解到的信息,所以一定要做到简单清楚,在开设专卖店时,就要将标识做到清晰醒目,方便客户寻找。 2、舒适的灯光 灯光是展厅装修中最为重要的一部分,灯光太亮会让人感觉不舒…

基于spring boot的钢材销售管理系统的设计与实现

钢材销售管理系统 摘 要 本系统为用户而设计制作钢材销售管理系统,旨在实现钢材销售智能化、现代化管理。本钢材销售管理自动化系统的开发和研制的最终目的是将钢材销售的运作模式从手工记录数据转变为网络信息查询管理,从而为现代管理人员的使用提供更…

【Docker】使用Docker部署IT运维管理平台CAT

作者怀揣着一个美好的愿景,旨在提升管理效率、推动开源项目的蓬勃发展。 来一杯咖啡与茶,为 IT 运维从业者减轻管理负担,提升管理效率,从繁重无序的工作中解压出来,利用剩余时间多喝一杯休息一下。 这是一个专为 IT 运…

亮点抢先看!4月16-17日,百度Create大会开设“AI公开课”,大咖带你打造赚钱工具

3月16日,2024百度Create AI开发者大会正式开放售票,嘉宾套票定价399元。据悉,本次大会以“创造未来(Create the Future)”为主题,设有20深度论坛、超30节AI公开课、3000平AI互动体验区和AI音乐节等精彩环节…

AJAX-Promise

Promise 基本使用 定义:Promise对象用于表示一个异步操作的最终完成(或失败)及其结果值 好处: 1.逻辑更清晰 2.了解axios函数内部运作机制 3.能解决回调函数的问题 // 1.创建Promise对象 const p new Promise((resolve,reject…

【Web】浅聊Hessian反序列化之Resin的打法——远程类加载

目录 前言 原理分析 XString:触发恶意类toString QName的设计理念? 远程恶意类加载Context:ContinuationContext QName:恶意toString利用 hash相等构造 EXP 前言 精神状态有点糟糕,随便学一下吧 首先明确一个…

现代化的轻量级Redis桌面客户端Tiny RDM

​欢迎光临我的博客查看最新文章: https://river106.cn 1、简介 Tiny RDM(全称:Tiny Redis Desktop Manager)是一个界面现代化的轻量级Redis桌面客户端,支持Linux、Mac和Windows。它专为开发和运维人员设计,使得与Red…

2.shell中的echo命令

目录 概述实践shell结果 结束 概述 echo 命令详解 实践 shell #!/bin/bash # 输出一些变量或打印一些字符串 # 加双引号,空格是会保留的 echo "hello , world" # 不加,不会保留 echo hello , worldvar11 "a b c d&…

基于spring boot实现接口管理平台

数据库结构 /* Navicat MySQL Data TransferSource Server : localhost_3306 Source Server Version : 50724 Source Host : localhost:3306 Source Database : interfaceTarget Server Type : MYSQL Target Server Version : 50724 File Encoding…

Java毕业设计-基于SpringBoot的CSGO赛事管理系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示(源代码在文末)二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、管理员功能模块3、参赛战队功能模块4、合作方功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于Spring…

Java代码基础算法练习-判断素数-2024.03.17

任务描述: 输入一个数x,判断它是否是素数。 提示:素数是只能被1和它本身整除的数,1不是素数。 任务要求: 代码示例: package march0317_0331;import java.util.Scanner;public class March0317 {public …

【鸿蒙HarmonyOS开发笔记】常用组件介绍篇 —— Toggle切换按钮组件

概述 Toggle为切换按钮组件,一般用于两种状态之间的切换,例如下图中的蓝牙开关。 参数 Toggle组件的参数定义如下 Toggle(options: { type: ToggleType, isOn?: boolean })● type type属性用于设置Toggle组件的类型,可通过ToggleType枚举…