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

【Redis】Redis实现分布式锁

分布式锁

分布式锁是一种在分布式系统中实现同步机制的技术。它允许多个进程或节点在访问共享资源时进行同步,以确保它们按照预期的顺序执行。

这篇文章使用Redis来分布式锁,通俗的来说,分布式锁本质上要实现的目标就是在Redis里面占一个"茅坑",当别的进程也要来占时,发现已经有人蹲在那里了,就只好放弃或者稍后重试。

接下来我们来循序渐进的实现一个成功的分布式锁。

一、使用setnx指令

Redis分布式锁一般使用setnx(setifnotexists)指令,只允许被一个客户端占坑。先来先占,用完了,再调用del指令释放"茅坑"。

setnx lock:codehole true...do something critical ...del lock:codehole

简单的使用setnx指令肯定会出现一些问题

问题一:如果逻辑执行到中间出现异常了,可能会导致del指令没有被调用,这样就会陷入死锁,锁永远得不到释放。

我们首先想到的肯定是:“加一个过期时间就行了”。那我们接着往下看。

二、使用setnx+expire指令

给锁加上过期时间后,如果程序执行到中间出现异常或超时了,到了过期时间后就自动释放锁。

setnx lock:codehole true
expire lock:codehole 5... do something critical ...
del lock:codehole

问题二:如果在setnx和expire之间服务器进程突然挂掉了,可能是因为机器掉电或者是被人杀掉的,就会导致expire得不到执行,也会造成死锁。

解决方案: 使用Redis中setnx和expire组合原子命令。

三、使用setnx和expire组合原子命令

为了解决这个问题,Redis在2.8版本中加入了set指令的扩展参数,使得setnx和expire指令可以一起执行,解决了这个问题。

set lock:codehole true ex 5 nx... do something crutical ...del lock:codehole

问题三:释放锁错乱问题,当前锁可能释放的不是自己的锁。

如果在加锁和释放锁之间的逻辑执行的太长,以至于超出了锁的超时限制,就会出现问题。因为这时候锁过期了,第二个线程重新持有了这把锁,但是紧接着第一个线程执行完了业务逻辑,就把锁给释放了。

场景:如果业务逻辑的执行时间是7s。执行流程如下

  1. index1业务逻辑没执行完,3秒后锁被自动释放。

  2. index2获取到锁,执行业务逻辑,3秒后锁被自动释放。

  3. index3获取到锁,执行业务逻辑

  4. index1业务逻辑执行完成,开始调用del释放锁,这时释放的是index3的锁,导致index3的业务只执行1s就被别人释放。

最终等于没锁的情况。

解决方案:获取锁时,设置一个指定的唯一值value(例如:雪花Id),释放前获取这个锁,判断是否是自己的锁。

四、指定唯一值value

获取锁时,设置一个自己的唯一值,释放锁之前先判断这个value唯一值是否是自己的锁,如果是自己的锁才可以释放。

set lock:codehole 唯一值 ex 5 nx... do something crutical ...释放之前判断唯一值是否是自己加锁前的唯一值
del lock:codehole

问题:判断加删除操作缺乏原子性,也会造成问题。

场景:

  1. index1执行删除时,查询到的lock值确实和uuid相等

  2. index1执行删除前,lock刚好过期时间已到,被redis自动释放,在redis中没有了lock,没有了锁。

  3. index2获取了lock,index2线程获取到了cpu的资源,开始执行方法

  4. index1执行删除,此时会把index2的lock删除。index1 因为已经在方法中了,所以不需要重新上锁。index1有执行的权限。index1已经比较完成了,这个时候,开始执行删除的index2的锁!

解决方案:使用lua脚本,lua脚本可以保证连续多个指令的原子性执行。

五、使用Redis+Lua脚本

使用 Lua 脚本来处理,Lua 脚本可以保证连续多个指令的原子性执行。

if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0

最终,分布式锁的最终版本就是Redis中setnx 和 Lua脚本了。

相关文章:

  • COCOS2DX3.17.2 Android升级targetSDK30问题解决方案
  • 【技术干货】开源库 Com.Gitusme.Net.Extensiones.Core 的使用
  • c++类和对象
  • 算法与数据结构之链表
  • Web前端—网页制作(以“学成在线”为例)
  • python- 学生信息管理系统
  • Go Gin中间件
  • 什么情况造成互斥锁死锁
  • 3.25每日一题(知线性常系数方程的特解求线性方程)
  • JDBC简单流程
  • Unreal PythonScriptPlugin
  • iOS App Store上传项目报错 缺少隐私政策网址(URL)解决方法
  • SpringBoot项目多环境开发
  • 4.3 传送门
  • 2024最新免费的mac电脑清理垃圾的软件有哪些?
  • [译] React v16.8: 含有Hooks的版本
  • 【347天】每日项目总结系列085(2018.01.18)
  • 【Amaple教程】5. 插件
  • Apache Pulsar 2.1 重磅发布
  • AWS实战 - 利用IAM对S3做访问控制
  • bootstrap创建登录注册页面
  • ComponentOne 2017 V2版本正式发布
  • docker python 配置
  • Just for fun——迅速写完快速排序
  • Material Design
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • vuex 学习笔记 01
  • WePY 在小程序性能调优上做出的探究
  • 复习Javascript专题(四):js中的深浅拷贝
  • ------- 计算机网络基础
  • 为什么要用IPython/Jupyter?
  • 学习Vue.js的五个小例子
  • 用quicker-worker.js轻松跑一个大数据遍历
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • FaaS 的简单实践
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • ​渐进式Web应用PWA的未来
  • (16)Reactor的测试——响应式Spring的道法术器
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (论文阅读26/100)Weakly-supervised learning with convolutional neural networks
  • (五)网络优化与超参数选择--九五小庞
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • ****Linux下Mysql的安装和配置
  • .Net 6.0 处理跨域的方式
  • .NET Core MongoDB数据仓储和工作单元模式封装
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .Net6 Api Swagger配置