分布式锁的产生以及使用

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

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

单节点锁问题

目前针对这种锁资源的情况采取的往往是互斥锁,例如 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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高性能跨平台网络通信框架 HP-Socket v5.9.5

项目主页 : http://www.oschina.net/p/hp-socket开发文档 : https://www.docin.com/p-4478351216.html下载地址 : https://github.com/ldcsaa/HP-SocketQQ Group: 44636872, 663903943 v5.9.5 更新 一、主要更新 问题修复&#xff1a;由于 v5.9.4 版本升级了 KCP 导致 UDP AR…

C语言中的变量与scanf介绍(干货)

目录 前言 一、变量 1. 变量的创建 2. 变量的分类 3. 强制类型转换 二、scanf介绍 1. scanf的基本用法 2. scanf的返回值 3. scanf的占位符 4. 赋值忽略符 结语&#xff1a; 前言 我们在前面的文章中介绍了数据类型&#xff0c;以及printf函数的使用。 C语言中的数…

为什么使用物理、数学等工具不能实现对人类智能的模拟

物理和数学是研究自然界和抽象概念的工具&#xff0c;它们为我们提供了理论和方法来解决问题。在实现类人的智能方面&#xff0c;物理和数学可以应用于以下几个方面&#xff1a; 1、计算机科学和算法 数学中的逻辑、统计学和优化理论等可以用于开发智能算法和机器学习模型。这些…

Frenet坐标系下动态街道场景的最优轨迹生成

0 前言 有两个主要算法已经在实际中使用&#xff1a; &#xff08;1&#xff09;大多数研究组采用插值来解决规划问题&#xff0c;如奥迪、斯坦福最近演示中使用了回旋曲线&#xff0c;贝塞尔以及多项式曲线也被其他研究组使用。主要原因是在结构化环境中增强映射可以提供所需…

web蓝桥杯真题--12、由文本溢出引发的“不友好体验”

背景介绍 通常情况下&#xff0c;为保证布局的稳定性&#xff0c;以及遵循在有限的空间展示更多内容的原则&#xff0c;页面的某块区域不会随内容的增多而无限增高或增宽&#xff0c;一般会有一个约束。 例如&#xff1a;整体元素过多可以使用滚动条&#xff1b;文字内容过多…

flask 与 小程序 购物车 展示功能

mina/pages/cart/index.wxml <!--index.wxml--> <view class"container"><view class"title-box" wx:if"{{ !list.length }}">购物车空空如也&#xff5e;</view><view class"to-index-btn" bindtap"…

在k8s上部署ClickHouse

概述 clickhouse的容器化部署&#xff0c;已经有非常成熟的生态了。在一些互联网大厂也已经得到了大规模的应用。 clickhouse作为一款数据库&#xff0c;其容器化的主要难点在于它是有状态的服务&#xff0c;因此&#xff0c;我们需要配置PVC。 目前业界比较流行的部署方式有…

[每日一题] 01.20

文章目录 Apples Plus找最小值分类平均一尺之棰 Apples Plus L int(input()) for i in range(1,L 1):if i 1:print(fToday, I ate 1 apple.)else:print(fToday, I ate {i} apples.)找最小值 a int(input()) b list(map(int,input().split())) print(min(b))分类平均 n,k …

有什么提高编程能力的书籍推荐吗?

数据密集型应用系统设计 原文完整版PDF&#xff1a;https://pan.quark.cn/s/d5a34151fee9 这本书的作者是少有的从工业界干到学术界的牛人&#xff0c;知识面广得惊人&#xff0c;也善于举一反三&#xff0c;知识之间互相关联&#xff0c;比如有个地方把读路径比作programming …

esp32-idf Eclipse Log日志打印demo

Log日志打印demo 1、代码例程 esp32-S2 芯片 / Eclipse软件 开发环境 #include <stdio.h> #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_…

力扣hot100 找到字符串中所有字母异位词 滑动窗口 双指针 一题双解

Problem: 438. 找到字符串中所有字母异位词 文章目录 思路滑动窗口 数组滑动窗口 双指针 思路 &#x1f469;‍&#x1f3eb; 参考题解 滑动窗口 数组 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) class Solution { // 滑动窗口 …

mfc110.dll丢失是什么意思?全面解析mfc110.dll丢失的解决方法

在使用计算机的过程中&#xff0c;用户可能会遭遇一个常见的困扰&#xff0c;即系统提示无法找到mfc110.dll文件。这个动态链接库文件&#xff08;DLL&#xff09;是Microsoft Foundation Classes&#xff08;MFC&#xff09;库的重要组成部分&#xff0c;对于许多基于Windows的…