分布式锁演进-基本原理与使用

文章目录

  • 前言
  • 一、分布式锁演进
    • 1.1 分布式锁特点
    • 1.2 阶段一
    • 1.3 阶段二
    • 1.4 阶段三
    • 1.5 阶段四

前言

在单体应用下当多线程去竞争某一共享资源时,我们通常会用一把锁来保证只有一个线程获取到资源。如加上 synchronize 关键字或 ReentrantLock 锁等操作。
在分布式应用中,多个应用服务要同时对同一条数据做修改,单靠本地锁并不能解决分布式情况下多进程竞争共享资源带来的数据安全性问题。我们可以使用 redis 来实现分布式锁,来确保数据的正确性。

一、分布式锁演进

1.1 分布式锁特点

  1. 互斥性:在任意时刻,对于同一个锁,只有一个客户端能持有,从而保证一个共享资源同一时间只能被一个客户端操作;

  2. 安全性:即不会形成死锁,当一个客户端在持有锁的期间崩溃而没有主动解锁的情况下,其持有的锁也能够被正确释放,并保证后续其它客户端能加锁;

  3. 可用性:当提供锁服务的节点发生宕机等不可恢复性故障时,“热备” 节点能够接替故障的节点继续提供服务,并保证自身持有的数据与故障节点一致。

  4. 对称性:对于任意一个锁,其加锁和解锁必须是同一个客户端,即客户端 A 不能把客户端 B 加的锁给解了。

在这里插入图片描述
如上众多的商品服务分别部署在不同机器上同时去一个地方占坑,如果占到,就执行逻辑。否则就必须等待,直到释放锁。占坑可以去redis,可以去数据库,可以去任何大家都能访问的地方。等待可以自旋的方式。

1.2 阶段一

在这里插入图片描述

阶段一所对应的代码

/*** 使用redis实现分布式锁* @return*/
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {//1.占分布式锁:去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111");if (lock) {//加锁成功,执行业务Map<String, List<Catelog2Vo>> dataFromDB = getDataFromDB();//执行业务后删除锁redisTemplate.delete("lock");return dataFromDB;}else {//加锁失败,重试自旋,休眠100msreturn getCatalogJsonFromDBWithRedisLock();}
}

1.1.1 出现问题
setnx占好了位,业务代码异常或者程序在页面过程中宕机。没有执行删除锁逻辑,这就造成了死锁。

1.1.2 改进方案
设置锁的自动过期,即使没有删除,会自动删除,而且得使用set key value EX 300 NX 确保设置 key 的同时设置 key 的过期时间是一个原子性操作。改进后的代码如下。

/*** 使用redis实现分布式锁* @return*/
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {//1.占分布式锁:去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111",300,TimeUnit.SECONDS);if (lock) {//2.确保加锁和设置key的过期时间是个原子性操作//加锁成功,执行业务Map<String, List<Catelog2Vo>> dataFromDB = getDataFromDB();//执行业务后删除锁redisTemplate.delete("lock");return dataFromDB;}else {//加锁失败,重试自旋,休眠100msreturn getCatalogJsonFromDBWithRedisLock();}
}

1.3 阶段二

在这里插入图片描述
1.2.1 出现问题
假设锁的过期时间是10s业务代码执行12s线程A执行完了业务代码之后,此时锁已经过期,而且被其他线程所抢占,线程A正准备执行删锁操作,此时删除的时别的线程的锁。

1.2.2 改进方案
删除锁的时候得需要判断是否是自己的锁。

/**
* 使用redis实现分布式锁
* @return
*/
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {String uuid = UUID.randomUUID().toString().toLowerCase();//1.占分布式锁:去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,300,TimeUnit.SECONDS);if (lock) {// 确保加锁和设置key的过期时间是个原子性操作//加锁成功,执行业务Map<String, List<Catelog2Vo>> dataFromDB = getDataFromDB();//确保是自己的锁才能删除String value = redisTemplate.opsForValue().get("lock");if (value.equals(uuid)) {//执行业务后删除锁redisTemplate.delete("lock");}return dataFromDB;}else {//加锁失败,重试自旋,休眠100msreturn getCatalogJsonFromDBWithRedisLock();}
}

1.4 阶段三

在这里插入图片描述

1.3.1 出现问题
1.如果正好判断是当前值,正要删除锁的时候,锁已经过期,别人已经设置到了新的值。那么我们删除的是别人的锁。
2.锁的过期时间小于业务执行时间,导致提前删锁

1.3.2 改进方案
1.删除锁必须保证原子性,使用redis+Lua脚本完成。
2.锁时间自动续期

1.5 阶段四

在这里插入图片描述

1.4.1 Lua 脚本

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

1.4.2 最终方案

/*** 使用redis实现分布式锁* @return*/
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDBWithRedisLock() {String uuid = UUID.randomUUID().toString().toLowerCase();//1.占分布式锁:去redis占坑Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,300,TimeUnit.SECONDS);Map<String, List<Catelog2Vo>> dataFromDB;if (lock) {log.info("获取分布式锁成功!");try{// 确保加锁和设置key的过期时间是个原子性操作//加锁成功,执行业务dataFromDB = getDataFromDB();}finally {String script = "lua 脚本";Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock", uuid));}}else {//加锁失败,重试自旋,休眠100mslog.info("获取分布式锁失败!");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}return getCatalogJsonFromDBWithRedisLock();}return dataFromDB;
}

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

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

相关文章

一篇文章让你学会什么是哈希

一篇文章让你学会什么是哈希 哈希概念哈希冲突哈希函数1. 直接定址法2. 除留余数法3. 平方取中法4. 折叠法5. 随机数法6. 数学分析法 哈希冲突解决1. 闭散列1.1 线性探测1.2 二次探测 2. 开散列 开散列和闭散列对比 哈希概念 哈希在C中有广泛的应用&#xff0c;它是一种用于快…

pytest框架前后置设置,以及pytest默认规则

一、pytest框架的默认规则 1、模块名默认必须以test开头或者以test结尾 2、测试类必须以Test开头&#xff0c;并且不能有__init__方法 3、测试方法默认必须以test开头 当然以后的一些默认规则除测试类不能使用__init__方法外其余的都是可配置的&#xff0c;当然一般情况下我们…

【算法思想】排序

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

【二叉树魔法:链式结构与递归的纠缠】

本章重点 二叉树的链式存储二叉树链式结构的实现二叉树的遍历二叉树的节点个数以及高度二叉树的创建和销毁二叉树的优先遍历和广度优先遍历二叉树基础oj练习 1.二叉树的链式存储 二叉树的链式存储结构是指&#xff0c;用链表来表示一棵二叉树&#xff0c;即用链来指示元素的逻辑…

Redis集群架构搭建——主从、哨兵、集群

上一篇文章Ubuntu上通过源码方式安装Redis已经介绍了如何安装redis&#xff0c;在这篇文章中&#xff0c;将会教大家搭建Redis的几种高可用的架构&#xff1a;主从架构、哨兵集群、Cluster集群。 本篇文章使用的redis版本为6.2.13&#xff0c;不同版本的配置可能有略微的区别&a…

如何将内网ip映射到外网?快解析内网穿透

关于内网ip映射到外网的问题&#xff0c;就是网络地址转换&#xff0c;私网借公网。要实现这个&#xff0c;看起来说得不错&#xff0c;实际上是有前提条件的。要实现内网ip映射到外网&#xff0c;首先要有一个固定的公网IP&#xff0c;可以从运营商那里得到。当你得到公网IP后…

比特币上的可验证延迟函数

可验证延迟函数 (VDF) 是一种需要大量 顺序计算 来评估但可以快速验证的函数。我们首次在比特币上实现了它。VDF 作为密码学技术可用于构建大量新应用程序&#xff0c;例如公共随机信标、计算时间戳和数据复制证明。 VDF 场景 链上随机信标 在区块链中很难实现随机性&#xf…

李航老师《统计学习方法》第2章阅读笔记

感知机&#xff08;perceptron&#xff09;时二类分类的线性分类模型&#xff0c;其输入为实例的特征向量&#xff0c;输出为实例的类别&#xff0c;取1和-1二值。感知机对应于输入空间&#xff08;特征空间&#xff09;中将实例划分为正负两类的分离超平面 想象一下在一个平面…

人工智能机器学习-飞桨神经网络与深度学习

飞桨神经网络与深度学习-机器学习 目录 飞桨神经网络与深度学习-机器学习 1.机器学习概述 2.机器学习实践五要素 2.1.数据 2.2.模型 2.3.学习准则 2.4.优化算法 2.5.评估标准 3.实现简单的线性回归模型 3.1.数据集构建 3.2.模型构建 3.3.损失函数 3.4.模型优化 3…

怎么实现一个登录时需要输入验证码的功能

今天给项目换了一个登录页面&#xff0c;而这个登录页面设计了验证码&#xff0c;于是想着把这个验证码功能实现一下吧。 这篇文章就如何实现登录时的验证码的验证功能结合代码进行详细地介绍&#xff0c;以及介绍功能实现的思路。 目录 页面效果 实现思路 前端页面代码 lo…

conda常用指令

常用conda指令 查看当前有哪些环境&#xff0c;有base环境 conda env list 创建环境 # conda create -n 你的环境名 python版本号 # 创建python3.10&#xff0c;名为env虚拟环境 conda create -n env python3.10 激活环境 conda activate env

SpringCloud Alibaba - Sentinel篇

一、Sentinel快速入门 Sentinel官网地址&#xff1a;https://sentinelguard.io/zh-cn/index.html Sentinel项目地址&#xff1a;https://github.com/alibaba/Sentinel Sentinel是阿里巴巴开源的一款微服务流量治理组件&#xff0c;主要以流量为切入点&#xff0c;从流量限流、熔…

Linux MyFile

在之前&#xff0c;我们应该都多少接触过了C语言的文件管理&#xff0c;fopen&#xff0c;fclose&#xff0c;fputs....等函数的用法&#xff0c;也分析了系统层面上C语言是如何实现文件管理的。 回顾 上一个文章&#xff0c;我们讲解了十分重要的知识&#xff0c;在文件被打…

成集云 | 用友NC集成聚水潭ERP(用友NC主管供应链)| 解决方案

源系统成集云目标系统 方案介绍 用友NC是用友NC产品的全新系列&#xff0c;是面向集团企业的世界级高端管理软件。它以“全球化集团管控、行业化解决方案、全程化电子商务、平台化应用集成”的管理业务理念而设计&#xff0c;采用J2EE架构和先进开放的集团级开发平台…

平板用的触控笔什么牌子好?性价比高的触控笔推荐

随着平板电脑的普及&#xff0c;越来越多用户为了方便都选择了电容笔&#xff0c;电容笔已经完全代替了我们的手指&#xff0c;并且使我们的书写速度得到了极大的提升。然而&#xff0c;因为其的独特的重力压感功能与芯片技术&#xff0c;导致了原装笔的售价一直居高不下&#…

JavaScript - canvas - 放大镜

效果 示例 项目结构&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>放大镜</title><style type"text/css">div {width: 200px;height: 200px;display: inline-bl…

2023-09-20 Android CheckBox 让文字显示在选择框的左边

一、CheckBox 让文字在选择框的左边 &#xff0c;在布局文件里面添加下面一行就可以。 android:layoutDirection"rtl" 即可实现 android:paddingStart"10dp" 设置框文间的间距 二、使用的是left to right <attr name"layoutDirection">&…

RGB格式

Qt视频播放器实现&#xff08;目录&#xff09; RGB的使用场景 目前&#xff0c;数字信号源&#xff08;直播现场的数字相机采集的原始画面&#xff09;和显示设备&#xff08;手机屏幕、笔记本屏幕、个人电脑显示器屏幕&#xff09;使用的基本上都是RGB格式。 三原色 RGB是…

网络协议层次模型

OSI 七层模型 在 OSI 网络分层模型中&#xff0c;每个分层都接收由它下一层所提供的特定服务&#xff0c;并且负责为自己的上一层提供特定的服务。上下层之间进行交互时所遵循的约定叫做接口 &#xff1b;同一层之间的交互所遵循的约定叫做协议&#xff0c; 下图是 OSI 七层…

2023华为杯研究生数学建模竞赛CDEF题思路+模型代码

全程更新华为杯研赛CDEF题思路模型及代码&#xff0c;大家查看文末名片获取 华为杯C题思路分析 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比性&#xff0c;不同专家评审的作…