分布式锁的产生以及使用

日常开发中,针对一些需要锁定资源的操作,例如商城的订单超卖问题、订单重复提交问题等。

都是为了解决在资源有限的情况限制客户端的访问,对应的是限流。

单节点锁问题

目前针对这种锁资源的情况采取的往往是互斥锁,例如 java 里的 synchronized 锁以及 ReentrantLock,其中 synchronized 的加锁操作在 jvm 层面实现,会有一个锁升级(偏向锁、轻量级锁、重量级锁)的问题,ReentrantLock 需要手写代码实现,底层是 AQS。但是 java 层面的锁有一个问题,就是只能在一个进程中使用,如果跨进程就无能为力了,例如应用的集群部署,客户端请求过来后通过负载均衡策略转发到对应的实例上。

分布式锁

鉴于以上单节点锁的问题,就需要通过一个中间介质来实现针对需要访问的资源进行一个资源加锁和释放操作的问题,目前有如下方式

数据库

将需要访问的数据可以放到数据库的表中,一般存储的是确保唯一性的业务主键,在访问资源时可将业务主键插入到表中,每次访问资源前先查询数据库判断数据是否存在,如果存在表明资源在被访问,否则就正常处理。

zookeeper

https://zookeeper.apache.org/doc/r3.9.1/recipes.html#sc_recipes_Locks

通过临时顺序节点(EPHEMERAL_SEQUENTIAL )来实现,这种类型的节点的好处是会话级别,如果会话结束节点会删除掉。

官方提供的是 curator 组件库,在项目的 pom.xml 中引入如下依赖

		<dependency>
			<groupId>org.apache.curator</groupId>
			<artifactId>curator-recipes</artifactId>
			<version>5.5.0</version>
		</dependency>

https://curator.apache.org/docs/getting-started#distributed-lock

内部提供了一个分布式锁接口 InterProcessLock,通过对应的实现类 InterProcessMutex 进行加锁和释放锁操作。

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.TimeUnit;

public class DistributedLock {

    public static void main(String[] args) throws InterruptedException {

        RetryPolicy retryPolicy = new ExponentialBackoffRetry(10000, 3);

        for (int i = 1; i <= 1; i++) {
            new Thread(() -> {
                try (CuratorFramework client = CuratorFrameworkFactory.newClient(Constants.CONNECTION_URL, retryPolicy);){
                    client.start();
                    InterProcessLock lock1 = new InterProcessMutex(client, "/dlock");
                    try {
                        lock1.acquire(5, TimeUnit.SECONDS);
                        System.out.println("lock1获取");
                        TimeUnit.SECONDS.sleep(5);
                        lock1.release();
                        System.out.println("lock1释放");
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
            }).start();

        }
    }
}

锁的组成如下

/父节点/_c_+UUID+-lock-+10位数(从0开始自增,不足10位用0补足)

如果在执行过程中进行了多次加锁,具体如下

/dlock/_c_0d4db7a9-5c21-4a57-9904-e42cd970d774-lock-0000000000
/dlock/_c_d3588ec1-981a-41aa-a420-89f7949f94d6-lock-0000000001

这样就会有一个问题,前面的前缀 PROTECTED_PREFIX 和 UUID 会对节点产生干扰。

https://github.com/apache/curator/blob/apache-curator-5.5.0/curator-framework/src/main/java/org/apache/curator/framework/imps/ProtectedUtils.java#L65

可以将 ProtectedUtils 整个类复制一遍在项目中,将 getProtectedPrefix() 的内容进行修改,如下

修改前

public static String getProtectedPrefix(final String protectedId)
{
	return PROTECTED_PREFIX + protectedId + PROTECTED_SEPARATOR;
}

修改后

public static String getProtectedPrefix(final String protectedId)
{
	return protectedId;
}

这样生成的节点格式为

/父节点/lock-+10位数(从0开始自增,不足10位用0补足)

最终生成的节点如下

/dlock/lock-0000000000

其中对应的节点值为当前请求的 ip

[zk: localhost:2181(CONNECTED) 2] get /dlock/lock-0000000000
192.168.106.109

如果有多个客户端针对同一个节点进行加锁请求,会按序创建多个节点,但是持有锁的只是最小的节点,后面的节点会向获取锁的节点注册 Watcher 来监听持有锁的节点是否存在。

redis

作为一个在内存层次的数据库,用处多多,其中可以用于分布式锁。

redis 提供了 lua 脚本支持,lua 脚本可以做到将操作进行打包,确保整个操作的原子性。其中分布式锁就用到了 lua 脚本。

redis 官方介绍

https://redis.io/docs/manual/patterns/distributed-locks/

该锁被命名为 Redlock,在不同的语言中有对应的实现,在 java 中对应的是 redisson。

在项目的 pom.xml 中引入如下依赖

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.26.0</version>
</dependency>  

github 项目链接

https://github.com/redisson/redisson

与 spring boot 整合

https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter#spring-boot-starter

区别

分布式锁建立在共享介质上,所以上面的三种方式都需要借助于其他组件来实现。

数据库层面不适合做高并发处理。

redis 依赖于全局时间,需要考虑到加锁时间的问题。

zookeeper 建立在创建节点的基础上,属于重操作,相对于 redis 内存操作慢一些。

由于 zookeeper 使用了 zab 协议,针对写请求会转发到领导者,如果领导者节点宕机,会从跟随者节点中选举出数据最完整的节点晋升为领导者,不会出现类似 redis 异步同步数据丢失的问题。

对于一些并发请求大的应用,使用 zookeeper 可能出现锁获取失败的情况,使用 redis 集群不会因为,因为 redis 的单线程高并发处理一般情况下难以达到,一般瓶颈在网卡带宽上。

之前自己写的文章

https://blog.csdn.net/zlpzlpzyd/article/details/132716450

参考链接

https://www.jianshu.com/p/31335efec309

https://www.cnblogs.com/xuwc/p/14019932.html

https://juejin.cn/post/6844903729406148622

https://www.cnblogs.com/crazymakercircle/p/14504520.html

https://blog.csdn.net/qq_26709459/article/details/112770526

https://zhuanlan.zhihu.com/p/639756647

https://zhuanlan.zhihu.com/p/383512946

https://www.cnblogs.com/zwj-199306231519/articles/17411947.html

https://baijiahao.baidu.com/s?id=1784900642230670850

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Node+Express编写接口---前端

前端页面 vue_node_admin: 第一个以node后端,vue为前端的后台管理项目https://gitee.com/ah-ah-bao/vue_node_admin.git

1.1 数据库概述

1.1 数据库概述 1.1.1 数据库基本概念 - 数据&#xff08;Data&#xff09; - 数据库&#xff08;DataBase&#xff0c;DB&#xff09; - 数据库管理系统&#xff08;DataBase Management System&#xff0c;DBMS&#xff09; - …

【C++】List模拟实现过程中值得注意的点

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.List迭代器 2.适…

AI对比:ChatGPT与文心一言的异同与未来

文章目录 &#x1f4d1;前言一、ChatGPT和文心一言概述1.1 ChatGPT1.2 文心一言 二、ChatGPT和文心一言比较2.1 训练数据与知识储备2.2 语义理解与生成能力2.2 应用场景与商业化探索 三、未来展望3.1 模型规模与参数数量不断增加3.2 多模态交互成为主流3.3 知识图谱与大模型的结…

Vue2移动端项目使用$router.go(-1)不生效问题记录

目录 1、this.$router.go(-1) 改成 this.$router.back() 2、存储 from.path&#xff0c;使用 this.$router.push 3、hash模式中使用h5新增的onhashchange事件做hack处理 4、this.$router.go(-1) 之前添加一个 replace 方法 问题背景 &#xff1a; 在 Vue2 的一个移动端开发…

JS-WebAPIs- Window对象(五)

• BOM(浏览器对象模型) BOM(Browser Object Model ) 是浏览器对象模型 window对象是一个全局对象&#xff0c;也可以说是JavaScript中的顶级对象像document、alert()、console.log()这些都是window的属性&#xff0c;基本BOM的属性和方法都是window的。所有通过var定义在全局…

【web 编程技术】基于 B/S 架构的电商平台(java web)

基于 B/S 架构的电商平台&#xff08;java web&#xff09; 课程设计实验目的课程设计实验环境课程设计功能概述课程设计需求分析三层架构图功能列表系统用例图系统活动图-用户端需求分析 课程设计详细设计实现过程数据库BaseServlet 的实现商品显示模块-分页显示所有商品、查看…

《WebKit 技术内幕》之五(1): HTML解释器和DOM 模型

第五章 HTML 解释器和 DOM 模型 1.DOM 模型 1.1 DOM标准 DOM &#xff08;Document Object Model&#xff09;的全称是文档对象模型&#xff0c;它可以以一种独立于平台和语言的方式访问和修改一个文档的内容和结构。这里的文档可以是 HTML 文档、XML 文档或者 XHTML 文档。D…

MySQL 索引(下)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL-进阶篇 &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现…

【STM32调试】寄存器调试不良问题记录持续版

STM32寄存器调试不良问题记录 低功耗管理NVIC&#xff08;内嵌的中断向量控制器&#xff09;EXTI&#xff08;外部中断/事件&#xff09; 记录一些stm32调试过程中&#xff1a;不易被理解、存在使用误区、不清不楚、是坑、使用常识等方面的一些记录。本记录只包含stm32的内核以…

UE5 C++学习笔记 常用宏的再次理解

1.随意创建一个类&#xff0c;他都有UCLASS()。GENERATED_BODY()这样的默认的宏。 UCLASS() 告知虚幻引擎生成类的反射数据。类必须派生自UObject. &#xff08;告诉引擎我是从远古大帝UObject中&#xff0c;继承而来&#xff0c;我们是一家人&#xff0c;只是我进化了其他功能…

动态规划——炮兵回城【集训笔记】

题目描述 游戏盘面是一个m行n列的方格矩阵&#xff0c;将每个方格用坐标表示&#xff0c;行坐标从下到上依次递增&#xff0c;列坐标从左至右依次递增&#xff0c;左下角方格的坐标为(1,1)&#xff0c;则右上角方格的坐标为(m,n)。 游戏结束盘上只剩下一枚炮兵没有回到城池中&a…

编曲学习:Cubase12导入Cubasis工程的方法!

Steinberg 发布 Cubasis 3 项目导入器&#xff0c;可将 Cubasis 的项目导入到 Cubase 使用https://m.midifan.com/news_body.php?id35635 我偶然看到这个文章&#xff0c;不过发现Cubase12默认好像没有这个选项&#xff0c;心想着要是移动端能和PC端同步&#xff0c;感觉会挺…

【网站项目】329网月科技公司门户网站

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

深入理解JavaScript箭头函数

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 函数是JavaScript中非常重要的一个组成部分,可以封装代码逻辑,提高代…

x-cmd pkg | jq - 命令行 JSON 处理器

目录 简介首次用户功能特点类似工具进一步探索 简介 jq 是轻量级的 JSON 处理工具&#xff0c;由 Stephen Dolan 于 2012 年使用 C 语言开发。 它的功能极为强大&#xff0c;语法简洁&#xff0c;可以灵活高效地完成从 JSON 数据中提取特定字段、过滤和排序数据、执行复杂的转…

Transformer and Pretrain Language Models3-2

transformer structure注意力机制的各种变体 第二种变体&#xff1a; 如果两个向量的维度不一样&#xff0c;我们就需要在中间加上一个权重矩阵&#xff0c;来实现他们之间的相乘&#xff0c;然后最后得到一个标量 第三种变体&#xff1a; additive attention 它和前面的有…

顶顶通用户申请和安装 空号识别 模块流程

一、申请 空号识别 授权 打开网址&#xff1a;http://my.ddrj.com&#xff0c;注册并登录。 点击“我的授权” -> “申请授权” &#xff08;根据负责人的要求选择“在线”或是“离线”&#xff09;。 找到名称为空号识别的授权并点击“加号”图标打开授权&#xff0c;然…

JDK 动态代理(Spring AOP 的原理)(面试重点)

代理模式 也叫委托模式.定义&#xff1a;为其他对象提供⼀种代理以控制对这个对象的访问.它的作⽤就是通过提供⼀个代理类,让我们 在调⽤⽬标⽅法的时候,不再是直接对⽬标⽅法进⾏调⽤,⽽是通过代理类间接调⽤&#xff0c;在某些情况下,⼀个对象不适合或者不能直接引⽤另⼀个对…

geoserver pg_hba.conf 设置连接

geoserver pg_hba.conf 设置连接 在Postgre安装文件目录下的data文件夹中&#xff0c;修改pg_hba.conf文件&#xff0c;末尾添加重启postgresql的服务&#xff0c;应该就可以连了。