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

分布式锁三种方案

基于数据库的分布式锁(基于主键id和唯一索引)

1基于主键实现分布式锁

2基于唯一索引实现分布式锁

其实原理一致,都是采用一个唯一的标识进行判断是否加锁。

原理:通过主键或者唯一索性两者都是唯一的特性,如果多个服务器同时请求到数据库,数据库只会允许同一时间只有一个服务器的请求在对数据库进行操作,其他服务器的请求就需要进行阻塞等待或者进行自旋。如何实现的呢?可以理解为同一时间只有一个请求能够拿到锁,当方式执行完成过后,对锁进行释放过后,其他请求就可以拿到锁再对数据库进行操作,这样就避免了数据不安全问题。

阻塞:线程等待锁释放的一种方式

自旋:自旋包括了递归自旋,while自旋。意思就是不断地去尝试获取锁,只有获取锁才会停止自旋过程,没有拿到就会一直尝试获取锁。

以下是一个基于MySQL实现分布式锁的示例代码:

import java.sql.*;
import java.util.Properties;public class DatabaseLock {private Connection conn;private static final String dbUrl = "jdbc:mysql://localhost:3306/test";private static final String username = "xxxx";private static final String password = "xxxxx";// 构造函数,建立数据库连接public DatabaseLock() throws SQLException {Properties props = new Properties();props.setProperty("user", username);props.setProperty("password", password);conn = DriverManager.getConnection(dbUrl, props);}// 尝试获取锁public boolean tryLock(String lockId) throws SQLException {PreparedStatement stmt = conn.prepareStatement("INSERT INTO locks (id) VALUES (?)");stmt.setString(1, lockId);try {stmt.executeUpdate();return true;} catch (SQLException e) {if (e.getErrorCode() == 1062) { // 锁已存在return false;} else {throw e;}}}// 释放锁public void releaseLock(String lockId) throws SQLException {PreparedStatement stmt = conn.prepareStatement("DELETE FROM locks WHERE id=?");stmt.setString(1, lockId);stmt.executeUpdate();}
}

在使用时,通过创建一个DatabaseLock实例来获取和释放锁:

DatabaseLock databaseLock = new DatabaseLock();// 尝试获取锁,获取成功返回true,获取失败返回false
if (databaseLock.tryLock("lockId")) {try {// do something} finally {databaseLock.releaseLock("lockId"); // 释放锁}
} else {// get lock failed
}

基于Redis的分布式锁 

下面就来介绍基于Redis的分布式锁,直接上图;

虚拟机A和虚拟机B两个虚拟机都想对可变的共享资源(广义的概念,可以是数据库的某一张表和数据库中的某一行数据 ),就会出现线程安全问题,就需要基于锁模型实现同步互斥的手段,保证只有一个虚拟机中的线程进而实现这个线程的相对安全。现在虚拟机要对共享资源上锁,锁对象是Redis,操作的对象就是这个共享资源(假如数据库的一行数据)。

基于Redis实现分布式锁执行流程:

1虚拟机实例A根据Hash算法选择Redis节点(Redis采用的是集群部署,每个服务器都是一个节点),执行Lua脚本加锁(就是通过setnx方法,即set一个key(主键id)到Redis当中,判断Redis中是否有当前key,没有,就返回1,表示加锁成功,有就返回0,表示加锁失败),并且设置锁的过期时间。当虚拟机A对共享资源上锁成功过后,就拥有了对共享数据的操作权限,然后就可以对共享数据的操作处理,执行事务处理。

问题:在从Redis获取锁的过程和进行设置锁的过期时间过程中出现宕机,就会出现锁一辈子不会被释放?出现死锁问题?

        这时候就需要保证获取锁和设置锁的过期时间两行代码的原子性,就是要么同时成功,要么同时失败,如何实现呢?

        这时候只要保证将两行代码变成一行代码即可。

  原本的setnx和expire是两行代码

if(jedis.setnx(lock_stock,1) == 1){	//获取锁
//=========在这里出现宕机了=====死锁问题出现了=========expire(lock_stock,5)		 //设置锁超时try {业务代码} finally {jedis.del(lock_stock)			 //释放锁}
}

 通过set变成一行代码过后,解决了死锁问题。

if(set(lock_stock,1,"NX","EX",5) == 1){	//获取锁并设置超时try {业务代码} finally {del(lock_stock)			 		//释放锁}
}

 2这时候如果虚拟机B也需要对共享资源进行操作,也去执行lua脚本进行加锁(就是采用setnx的方式--通过set一个key到redis,判断redis中是否已经存储了这个key(行数据的主键id)),如果查询到redis中没有,就会返回1表示加锁成功,如果有就会返回0表示加锁失败 ,这就能保证共享资源同一时间不会被多个虚拟机同时操作。

        3当虚拟机A执行完对自己已经加锁的共享资源执行操作完成之后,必须要执行DEL释放锁,不然其他虚拟机包括虚拟机A都不能再对当前共享资源进行加锁操作,

问题:虚拟机A能保证会执行完成过后一定执行DEL释放锁吗? 答案是:不一定的

        4当虚拟机A执行对共享资源事务操作完成之后,在执行DEL释放锁之前,代码出现问题,抛出异常就会出现这种问题,虚拟机A就永远不能执行DEL释放锁了,就会导致后续上锁都会失败。

问题:所以就出现上面这个执行流程,如何解决呢?         

        5这时候起初指执行lua脚本加锁的时候,存储一个过期时间,当不能主动进行DEL释放锁时,到达Redis设置的过期时间,锁就会过期。

这个时候又会出现另一个问题? 就是虚拟机A在设置的过期时间以内还没有执行完对共享资源的操作,锁就过期了,如何解决呢?

        6这时候就会执行最后一个流程,后台守护线程(类似于Redission内部提供一个监控锁的看门狗),来定期的检查锁是否存在,如果存在,延长key的过期时间,还需要判断事务是否还在正常执行,如果是异常已经抛出异常,就不用进行后台守护线程了,然后等待锁自动过期。

说这么多?其实就是明白其执行原理,而实际开发过程中,大佬们已经使用Redission封装好了上面的具体实现细节。
Redission实现分布式锁【封装了基于Redis的分布式锁】

什么是Redission?
       
简单理解为就是操作Redis的一个工具包,让我们使用Redis更加简单,让使用者能够将精力更集中地放在处理业务逻辑上。

Redisson实现分布式锁

        Redisson官方文档对分布式锁的解释总结下来有两点

            1Redisson加锁自动有过期时间30s,监控锁的看门狗发现业务没执行完,会自动进行锁的续期(重回30s),这样做的好处是防止在程序还没有执行结束,锁自动过期被删除问题
            2当业务执行完成不再给锁续期,即使没有手动释放锁,锁的过期时间到了也会自动释放锁。

 // 获取锁RLock lock = redisson.getLock(LOCK_KEY);try {// 加锁lock.lock();int s = Integer.parseInt(Objects.requireNonNull(stringRedisTemplate.opsForValue().get(REDIS_KEY)));if (s > 0) {// 扣库存s--;System.out.printf("秒杀商品个数剩余:" + s + "\n");// 更新库存stringRedisTemplate.opsForValue().set(REDIS_KEY, String.valueOf(s));} else {System.out.println("活动太火爆了,商品已经被抢购一空了!");}} catch (Exception e) {System.out.println(Thread.currentThread().getName() + "异常:");e.printStackTrace();} finally {// 释放锁lock.unlock();}

基于Zookeeper的分布式锁

什么是Zookeep?

ZooKeeper是一个分布式的协调服务,Zookeeper是基于CP,注重数据的一致性,若主机挂掉则Zookeeper不会对外进行提供服务了,需要选择一个新的Leader出来才能提供服务,不保证高可用性。简单来说zookeeper=文件系统+监听通知机制

Zookeeper数据模型

Zookeeper会维护一个具有层次关系的树状的数据结构,它非常类似于一个标准的文件系统,如下图所示:同一个目录下不能有相同名称的

每个子目录项如 NameService 都被称作为 znode(目录节点),和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。

有四种类型的znode:

PERSISTENT-持久化目录节点

客户端与zookeeper断开连接后,该节点依旧存在

PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点

客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

EPHEMERAL-临时目录节点

客户端与zookeeper断开连接后,该节点被删除

EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点

客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

监听通知机制

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

实现方案:临时顺序目录节点+监听机制

​​​​​​​

​​​​​​​在项目中可以使用curator,这个是Apache封装好的基于zookeeper的分布式锁方案

总结

1基于数据库实现:通常基于主键,或者唯一索引来实现分布式锁,但是性能比较差,一般不建议使用

2基于Redis实现分布式锁:可以使用setnx来加锁 ,但是需要设置锁的过期时间来防止死锁,所以要结合expire使用.为了保证setnx和expire两个命令的原子性,可以使用set命令组合【将setnx和expire结合成一行代码】。

        总之自己封装Redis的分布式锁是很麻烦的,我们可以使用Redissoin来实现分布式锁,Redissoin已经封装好了。

3.基于zookeeper : 使用临时顺序节点+监听实现,线程进来都去创建临时顺序节点,第一个节点的创建线程获取到锁,后面的节点监听自己的上一个节点的删除事件,如果第一个节点被删除,释放锁第二个节点就成为第一个节点,获取到锁。

在项目中可以使用curator,这个是Apache封装好的基于zookeeper的分布式锁方案。

 

 

相关文章:

分布式锁三种方案

基于数据库的分布式锁(基于主键id和唯一索引) 1基于主键实现分布式锁 2基于唯一索引实现分布式锁 其实原理一致,都是采用一个唯一的标识进行判断是否加锁。 原理:通过主键或者唯一索性两者都是唯一的特性,如果多个…...

【HarmonyOS NEXT】har 包的构建生成过程

Har模块文件结构 构建HAR 打包规则 开源HAR除了默认不需要打包的文件(build、node_modules、oh_modules、.cxx、.previewer、.hvigor、.gitignore、.ohpmignore)和.gitignore/.ohpmignore中配置的文件,cpp工程的CMakeLists.txt,…...

从0开发一个Chrome插件:项目实战——翻译插件(附带申请谷歌翻译、百度翻译教程)

前言 这是《从0开发一个Chrome插件》系列的第十八篇文章,本系列教你如何从0去开发一个Chrome插件,每篇文章都会好好打磨,写清楚我在开发过程遇到的问题,还有开发经验和技巧。 专栏: 从0开发一个Chrome插件:什么是Chrome插件?从0开发一个Chrome插件:开发Chrome插件的必…...

查看nginx安装/配置路径,一个服务器启动两个nginx

查看nginx安装/配置路径 查看nginx的pid: ps -ef | grep nginx查看pid对应服务的启动路径 ll /proc/2320/exe使用检查配置文件命令,查看配置文件位置 /usr/local/nginx/sbin/nginx -t一个服务启动两个nginx 拷贝一份程序,cpbin是我自己创…...

JavaScript中 Map与reduce的应用

1. Map:映射新世界 Map构造函数创建一个新Map对象,它允许你以键值对的形式存储数据,提供了一种更加灵活的数据结构。与传统的对象相比,Map允许任何值(包括对象)作为键,而且具有更好的性能表现。…...

1688商品详情API:一键解锁海量批发数据

引言 1688作为阿里巴巴旗下的B2B交易平台,拥有庞大的商品数据库和丰富的供应商资源。对于想要获取商品详细信息的开发者和企业而言,1688提供的API接口是获取一手数据的关键途径。本文将详细介绍如何使用1688商品详情API,包括注册、获取API密…...

C#结合JS 修改解决 KindEditor 弹出层问题

目录 问题现象 原因分析 范例运行环境 解决问题 修改 kindeditor.js C# 服务端更新 小结 问题现象 KindEditor 是一款出色的富文本HTML在线编辑器,关于编辑器的详细介绍可参考我的文章《C# 将 TextBox 绑定为 KindEditor 富文本》,这里我们讲述在…...

二开的精美UI站长源码分享论坛网站源码 可切换皮肤界面

二开的精美UI站长源码分享论坛网站源码 可切换皮肤界面 二开的精美UI站长源码分享论坛网站源码 可切换皮肤界面...

【diffusers极速入门(三)】生成的图像尺寸与 UNet 和 VAE 之间的关系

先上结论,一句话总结即: SD 图片的输入\输出尺寸(高或宽) Unet 输入\输出的样本尺寸(高或宽) x VAE 的缩放尺寸 在使用生成模型时,特别是图像生成任务中,理解 UNet 和 VAE&#xf…...

react实现窗口悬浮框,可拖拽、折叠、滚动

1、效果如下 2、如下两个文件不需要修改 drag.js import React from "react"; import PropTypes from "prop-types";export default class DragM extends React.Component {static propTypes {children: PropTypes.element.isRequired};static defaultP…...

52【场景作图】空间感

参考 场景绘制,画面空间感如何拉开?分分钟就能学会的场景优化思路更新啦!_哔哩哔哩_bilibili https://www.bilibili.com/video/BV1pa411J7Ps/?spm_id_from333.337.search-card.all.click&vd_source20db0c4e2d303527ed13c4b9cdf698ec 1 …...

SpringBoot系列之搭建WebSocket应用

SpringBoot系列之ServerEndpoint方式开发WebSocket应用。在实时的数据推送方面,经常会使用WebSocket或者MQTT来实现,WebSocket是一种不错的方案,只需要建立连接,服务端和客户端就可以进行双向的数据通信。很多网站的客户聊天&…...

RK3568技术笔记十四 Ubuntu创建共享文件夹

单击“虚拟机”,单击“设置”,如图所示: 单击“选项”,选择“总是启用(E)”,单击“添加”,如图所示: 单击“下一步”,如图所示: 单击“浏览”添加…...

JavaScript 获取地理位置 Geolocation

在现代的 web 应用程序中,获取用户的地理位置信息是一项常见的需求。这可以用于提供个性化内容、本地化服务或者基于位置的功能。HTML5 引入了 Geolocation API,使得从浏览器中获取地理位置信息变得非常简单。 1. Geolocation API 简介 Geolocation AP…...

android串口助手apk下载 源码 演示 支持android 4-14及以上

android串口助手apk下载 1、自动获取串口列表 2、打开串口就开始接收 3、收发 字符或16进制 4、默认发送at\r\n 5、android串口助手apk 支持android 4-14 (Google seral port 太老) 源码找我 需要 用adb root 再setenforce 0进入SELinux 模式 才有权限…...

windows11 生产力工具配置

一、系统安装 官方windows11.iso镜像文件安装操作系统时,会强制要求联网验证,否则无法继续安装操作系统,跳过联网登录账号的方式为:按下【shiftF10】快捷键,调出cmd命令窗口,输入命令 OOBE\BYPASSNRO 等…...

Nacos配置中心不可用会有什么影响

服务端: Nacos的数据存储接口 com.alibaba.nacos.config.server.service.DataSourceService 有两种实现: 如果指定了mysq 作为数据库,则必须使用 mysql 如果是 集群方式部署Nacos,则必须使用mysql 如果是单例方式部署 并且 没…...

AI时代下的自动化代码审计工具

代码审计工具分享 吉祥学安全知识星球🔗除了包含技术干货:Java代码审计、web安全、应急响应等,还包含了安全中常见的售前护网案例、售前方案、ppt等,同时也有面向学生的网络安全面试、护网面试等。 这两年一直都在提“安全左移”&…...

不懂索引,简历上都不敢写自己熟悉SQL优化

大家好,我是考哥。 今天给大家带来MySQL索引相关核心知识。对MySQL索引的理解甚至比你掌握SQL优化还重要,索引是优化SQL的前提和基础,我们一步步来先打好地基。 当MySQL表数据量不大时,缺少索引对查询性能的影响不会太大&#x…...

C# 设置PDF表单不可编辑、或提取PDF表单数据

PDF表单是PDF中的可编辑区域,允许用户填写指定信息。当表单填写完成后,有时候我们可能需要将其设置为不可编辑,以保护表单内容的完整性和可靠性。或者需要从PDF表单中提取数据以便后续处理或分析。 之前文章详细介绍过如何使用免费Spire.PDF…...

面试篇-求两个有序数组的交集

题目 两个有序数组,第一个有序数组m是1000w个元素,第二个有序数组n是1000个元素,求交集,需要考虑时间复杂度和空间复杂度。 解题思路 解法1:遍历小数组n,在m数组中进行折半查找,根据数组有序…...

Web爬虫-edu_SRC-目标列表爬取

免责声明:本文仅做技术交流与学习... 爬取后,结合暗黑搜索引擎等等进行进一步搜索. edu_src.py import requests, time from bs4 import BeautifulSoup for i in range(1, 20):url fhttps://src.sjtu.edu.cn/rank/firm/0/?page{i}print(f"正在获取第{i}页数据")s …...

云原生周刊:Harbor v2.11 版本发布 | 2024.6.17

开源项目推荐 Descheduler Descheduler 是一个工具,可用于优化 Kubernetes 集群中 Pod 的部署位置。它可以找到可以移动的 Pod,并将其驱逐,让默认调度器将它们重新调度到更合适的节点上。 Prowler Prowler 是一款适用于 AWS、Azure、GCP …...

低版本火狐浏览器报错:class is a reserved identifier

低版本火狐浏览器报错:class is a reserved identifier 原因:react-dnd,dnd-core 等node包的相关依赖有过更新,使得在低版本火狐浏览器中不支持 class 解决方法:在使用webpack打包构建时,编译排除node_modu…...

掌握高等数学、线性代数、概率论所需数学知识及标题建议

在数学的广袤领域中,高等数学、线性代数和概率论作为三大核心分支,不仅在理论研究中占据重要地位,更在实际应用中发挥着举足轻重的作用。为了深入理解和掌握这三门学科,我们需要掌握一系列扎实的数学知识。 高等数学所需数学知识 …...

value_and_grad

value_and_grad 是 JAX 提供的一个便捷函数,它同时计算函数的值和其梯度。这在优化过程中非常有用,因为在一次函数调用中可以同时获得损失值和相应的梯度。 以下是对 value_and_grad(loss, argnums0, has_auxFalse)(params, data, u, tol) 的详细解释&a…...

AI 已经在污染互联网了。。赛博喂屎成为现实

大家好,我是程序员鱼皮。这两年 AI 发展势头迅猛,更好的性能、更低的成本、更优的效果,让 AI 这一曾经高高在上的技术也走入大众的视野,能够被我们大多数普通人轻松使用,无需理解复杂的技术和原理。 其中,…...

Linux系统安装ODBC驱动,统信服务器E版安装psqlodbc方法

应用场景 硬件/整机信息:AMD平台 OS版本信息:服务器e版 软件信息:psqlodbc 12.02版本 功能介绍 部分用户在使用etl工具连接数据库时,需要使用到odbc驱动,下面介绍下服务器e版系统中编译安装此工具的相关过程。 E…...

品牌对电商平台价格的监测流程

在当今的电商时代,品牌商会重点关注众多电商平台,如淘宝、天猫、京东、拼多多、苏宁、小红书、抖音、快手等。之所以这些平台备受瞩目,很大程度上是因为其上的店铺数量众多,情况复杂。如今,无论是品牌的经销商还是非经…...

osgearth提示“simple.earth: file not handled”

在用vcpkg编译完osg和osgearth后,为了验证osgearth编译是否正确,进行测试,模型加载代码如下: root->addChild(osgDB::readNodeFile("simple.earth")); 此时以为是simple.earth路径的问题,遂改为以下代码…...

wordpress 模板免费下载/网络营销个人感悟小结

我自己做了一个练手的项目,项目已经基本完成,但是项目开始用的是jdbc配置数据源,一切都没有问题,后来我想把它改成用jndi配置数据源。下面是我改造时查了很多资料后整理的学习笔记,自己试过了没问题。仅供参考&#xf…...

宠物主题网站模板/开发一个网站需要哪些技术

树与二叉树 1节1. 树的逻辑结构2. 树的物理结构3. 二叉树的逻辑结构4. 二叉树的物理结构4.1 顺序存储结构4.2 链式存储5. 二叉树的遍历5.1 先序遍历5.2 先序遍历代码实现5.3 中序遍历5.4 中序遍历代码实现5.5 后序遍历5.6 后序遍历代码6. 非递归的二叉树遍历6.0 辅助栈的实现6.…...

做展示类网站/惠州seo外包公司

不知道你在使用mac vim编辑器的时候,整个编辑界面就一个颜色,也没有行号,是不是很崩溃?那么我们来简设置一下,变得很漂亮! 第一步:如果你不想改默认的设置文件,那么我们复制一份&am…...

网站开发搭建/旧版优化大师

本文件比较简单,代码比较少,主要涉及解锁后四个电机依次1/2/3/4转动,然后四轴也不会飞,只是在原地轻微转动,随后需要逐渐加油门至50%到达临界点,稍微往上推一点,电机声音就会发生变化&#xff0…...

七牛wordpress/什么是电商?电商怎么做

之前讲了RAC如何帮我们实现KVO / 代理 / 事件 / 通知 今天先不去分析它的核心代码, 我们先看看ReactiveObjC库里面一些特别的东西, 如果大家点开ReactiveObjC目录应该会看到很多category, 今天我们先来看看这些 我们先从UITextViewRACSignalSupport.h开始看 #import <UIKit…...

厦门建站价格/阿里巴巴官网

教程介绍 设计模式对你来说&#xff0c;应该不陌生。在面试中&#xff0c;经常会被问到&#xff1b;在工作中&#xff0c;有时候也会用到。通过整个专栏200多个实战案例&#xff0c;手把手带你高强度、刻意地练习设计模式&#xff0c;潜移默化地提高你的设计编码能力&#xff…...