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

72 分布式锁

72 分布式锁

什么是分布式锁

分布式锁 = 分布式 + 锁。那么分布式是指的什么呢?锁又是锁的谁呢?在业务开发中我们经常会听到分布式分布式的概念,分布式也很简单,通俗的来说就是你具有多个服务器,每个服务器上运行的程序是一样的,用户的每一次请求,都会平衡的分配到随机的一个服务器中进行处理。那么这样的话就会导致一个问题,那就是并发冲突和数据不一致等问题。下面结合一个简单的小例子来介绍一下分布式环境下不使用分布式锁会造成什么问题。

假设你运营着一个在线电商平台,某个商品(商品ID为 product123)的库存为10件。当用户在下单时,系统会检查库存,确定库存是否充足,然后减少库存并生成订单。为了应对大量并发请求,你的系统使用了多台服务器来处理订单请求。

用户1(请求在服务器A上处理)和 用户2(请求在服务器B上处理)同时发出购买请求。当前库存:10件

服务器A接收到用户1的请求,并查询数据库中的库存,此时库存为10件。

服务器B也接收到用户2的请求,并同时查询数据库中的库存,此时库存也是10件。

此时,两个服务器节点都“看到”库存为10件。

服务器A为用户1生成了订单,并将库存减少1。库存应减少为9件。

与此同时,服务器B为用户2生成了订单,并将库存也减少1,认为库存现在也是9件。

  • 服务器A将新的库存9件更新回数据库。
  • 服务器B将新的库存9件(根据它的视角)更新回数据库。

最终结果:虽然两个用户下单并减少库存,但两台服务器并没有意识到对方的操作,因此它们都认为库存应该从10减少到9,而不是从10减少到8。

实际发生的问题是,两个用户都成功购买了商品,但库存实际上只减少了1件,而不是2件。这就是库存超卖的现象。

库存最终显示为9件,而实际上它应该显示为8件,意味着库存被错误地记录,系统允许用户购买超过实际库存量的商品。

分布式锁保证了在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;

分布式锁应该具备的品质

课本上的概念直接列出,让人看了好似懂了又好似没懂。分布式锁简单的来说就是为了在不同服务器上的相同方法只能有一个在某一时刻运行就是了,我们需要锁住一些资源,让其达到这种效果,无论是什么,都会涉及到六个字:高可用高性能,这个也不例外。这个分布式锁需要保证高可用的获取锁和释放锁,也需要保证高性能的获取锁和释放锁。同时为了避免一些意外情况导致某线程一直占用锁,我们需要保证分布式锁具有过期时间,从而避免死锁。

  • 1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行;
  • 2、高可用的获取锁与释放锁;
  • 3、高性能的获取锁与释放锁;
  • 4、具备可重入特性;
  • 5、具备锁失效机制,防止死锁;
  • 6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

实现方式

目前我们接触到的很多大型网站都是基于分布式环境下开发的,但是有一个理论被称为CAP理论,这个理论告诉我们,**任何一个分布式系统都无法同时保证系统的一致性、可用性和分区容错性。**所以很多分布式系统都是选择牺牲一定的强一致性来得到高可用和高性能这一目标,它们选择系统只要保证最终一致性即可。

在很多的分布式场景中,为了达到最终一致性需要很多的技术支持,比如:分布式事务和分布式锁等等。这里我们先详细的介绍一下分布式锁的实现方式,分布式锁的实现一般分为三个大类:

  1. 基于数据库实现分布式锁;
  2. 基于缓存实现分布式锁;
  3. 基于ZooKeeper实现分布式锁;

接下来我们一一介绍。

在介绍之前,我们先仔细思考一下锁的概念,什么是锁呢?锁只是一个概念,它不涉及到一个具体的事物。任何只要能保证唯一性的都可以作为锁,我们仔细想想什么能保证唯一性呢?没错,就是数据库中的ID和Redis中的setnx,只要插入了两个一样的就会失败,这就是等价于获取锁失败了。

基于数据库实现分布式锁

通过上面的介绍,那么这个就很简单了,就是创建一个表,这个表就是为了分布式系统用来获取锁的,比如就创建了这个:

CREATE TABLE `distributed_lock` (`lock_key` VARCHAR(64) NOT NULL PRIMARY KEY,`lock_value` VARCHAR(64) NOT NULL,`expires_at` TIMESTAMP NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

如果这个线程想获取到这个锁,就在这个表中插入一条数据,其中lock_key为该线程需要对这个数据进行操作的ID,比如订单ID等等

在每个分布式节点中,当需要对某个共享资源进行操作时,我们会尝试向 distributed_lock 表中插入一条记录。如果插入成功,则表示获取了锁,可以安全地执行操作。如果插入失败,则说明锁已被其他节点占用。

INSERT INTO `distributed_lock` (`lock_key`, `lock_value`, `expires_at`)
VALUES ('product_123', 'UUID-xxxx', DATE_ADD(NOW(), INTERVAL 10 SECOND))
ON DUPLICATE KEY UPDATE`lock_value` = IF(`expires_at` < NOW(), 'UUID-xxxx', `lock_value`),`expires_at` = IF(`expires_at` < NOW(), DATE_ADD(NOW(), INTERVAL 10 SECOND), `expires_at`);

到了评价这个方法的优劣的时候了,首先这个是基于MySQL的,那就是外存中的,不用说效率低,效率低带来的结果就是并发性不高,就不适合高并发的情况,但是该方法方便,但是一般不用。

基于 MySQL 的分布式锁适合那些并发量不大、锁的粒度较粗的场景,如任务调度、定时任务等。对于高并发、低延迟要求较高的场景,建议使用更高效的分布式锁实现,如 Redis 或 ZooKeeper。

ON DUPLICATE KEY UPDATE:这个语句的作用是在插入新记录时,如果 lock_key 已经存在(比如因为主键或唯一约束冲突),则不会重新插入,而是执行更新操作。换句话说,如果已经有人持有了锁,这里会判断锁是否已经过期,如果过期了就让当前请求获取锁。

基于Redis实现分布式锁

基于setnx实现分布式锁

set if not exist = setnx;

  1. 加锁:执行setnx,若成功再执行expire添加过期时间
  2. 解锁:执行delete命令
SETNX lock_key unique_value
EXPIRE lock_key 10

这里会存在一些问题,首先就是这事两个操作,不是原子性的,如果客户端在执行 SETNX 后崩溃,EXPIRE 命令无法执行,导致这个锁永远不会自动释放,造成死锁问题。

我们可以通过这个来解决:set nx ex

SET lock_key unique_value NX EX 10

这个是原子性的。

同时这里还有一个过期时间的问题,就是我获取到锁的这个线程,无法在过期时间内完成这个任务,导致别的线程获得了锁,从而又导致了分布式锁的问题。

我们可以通过自动续期(检查当前线程是否完成了任务,如果没有完成就自动的延长过期时间),我们也可以采用更成熟的比如redisson,该提供了更成熟的分布式锁的设计。

基于Redis的Lua脚本能力

使用 SETNX 创建锁时,如果锁被客户端成功获取并持有,当客户端任务执行完毕后需要手动释放锁。然而,如果客户端在释放锁之前崩溃或者出现了错误,可能会导致锁未被正确释放,或者其他客户端错误地释放了本不属于它的锁。

使用 DEL 命令来释放锁时,必须确保只有持有锁的客户端才可以释放它。为此,可以将 unique_value 设置为唯一值(如 UUID),并通过 Lua 脚本确保只有在持有锁的客户端才能删除锁。

if redis.call("GET", "lock_key") == "unique_value" thenreturn redis.call("DEL", "lock_key")
elsereturn 0
end
基于Redisson实现分布式锁

参考资料

  • Redis分布式锁-这一篇全了解(Redission实现分布式锁完美方案)
  • 7年一线互联网经验,超爱图解底层原理,全栈一枚

重点关注这个方式实现,在以后会经常使用这个Redisson来实现分布式锁。

基于Zookeeper实现分布式锁

在这里插入图片描述

优点:不必多说,可以保证高可用,毕竟是专业的,Curator框架已原生支持系列分布式锁命令,使用简单

缺点:也不必多说,成本高,需要单独维护一套ZK集群。

相关文章:

72 分布式锁

72 分布式锁 什么是分布式锁 分布式锁 分布式 锁。那么分布式是指的什么呢&#xff1f;锁又是锁的谁呢&#xff1f;在业务开发中我们经常会听到分布式分布式的概念&#xff0c;分布式也很简单&#xff0c;通俗的来说就是你具有多个服务器&#xff0c;每个服务器上运行的程序…...

使用Windbg分析dump文件排查C++软件异常的一般步骤与要点分享

目录 1、概述 2、打开dump文件&#xff0c;查看发生异常的异常类型码 3、查看发生异常的那条汇编指令 3.1、汇编代码能最直接、最本真的反映出崩溃的原因 3.2、汇编指令中访问64KB小地址内存区&#xff0c;可能是访问了空指针 3.3、汇编指令中访问了很大的内核态的内存地…...

30 天 Python 3 学习计划

30 天 Python 3 学习计划 https://www.runoob.com/python3/python3-tutorial.html 1. Python3 基础语法 2. Python3 基本数据类型 3. Python3 数据类型转换 4. Python3 解释器 5. Python3 注释 6. Python3 运算符 7. Python3 数字(Number) 8. Python3 字符串 …...

【MATLAB实例】批量提取.csv数据并根据变量名筛选

【MATLAB实例】批量提取.csv数据并根据变量名筛选 准备&#xff1a;数据说明MATLAB批量提取参考 准备&#xff1a;数据说明 .csv数据如下&#xff1a; 打开某表格数据&#xff0c;如下&#xff1a;&#xff08;需要说明的是此数据含表头&#xff09; 需求说明&#xff1a;需…...

【软件】Ubuntu下QT的安装和使用

【软件】Ubuntu下QT的安装和使用 零、前言 QT是应用得比较广泛的程序框架&#xff0c;是因为其跨平台特性比较好&#xff0c;且用C/C作为开发语言&#xff0c;性能也比较好&#xff0c;故本文介绍如何安装和使用QT&#xff0c;用的版本是QT 6.2.4&#xff0c;由于QT在Windows…...

在Spring Boot中具有多个实现的接口正确注入的六种方式

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 在Spring Boot中&#xff0c;当一个接口具有多个实现时&#xff0c;正确地将这些实现注入到需要使用它们的地方是一个常见的需求。以下是在Spring Boot中实现这一目标的六种方式&#xff1a; 1. 使用Autowir…...

登陆微软账户太慢了,如何解决

软账号登录慢解决办法&#xff1a; 打开“网络和Internet”选择“以太网”选择“更改适配器选项”选择现用网络&#xff0c;右键->属性选择“IPV4”右键属性更改DNS地址为以下两者4.2.2.14.2.2.2...

Vue3动态组件component不生效问题解决方法

问题&#xff1a; vue3循环渲染动态组件component不生效&#xff0c;页面空白 在vue3使用component动态组件展示组件时&#xff0c;组件就是不展示显示空白。在vue2中使用动态变量component展示组件都是没问题。试了很多方法 踩了很多坑&#xff0c;所以记录下&#xff1a; 登录…...

算力基础篇:从零开始了解算力

什么是算力 算力即计算能力&#xff08;Computing Power&#xff09;&#xff0c;狭义上指对数字问题的运算能力&#xff0c;而广义上指对输入信息处理后实现结果输出的一种能力。虽然处理的内容不同&#xff0c;但处理过程的能力都可抽象为算力。比如人类大脑、手机以及各类服…...

Redis 万字入门教程

0. 前言 文章已经收录到 GitHub 个人博客项目&#xff0c;欢迎 Star&#xff1a; https://github.com/chenyl8848/chenyl8848.github.io或者访问网站&#xff0c;进行在线浏览&#xff1a; https://chenyl8848.github.io/1. NoSQL 1.1 NoSQL 介绍 NoSQL(Not Only SQL )&…...

LeetCode :LCR 173. 点名

​ ​ &#x1f525;个人主页&#xff1a;guoguoqiang. &#x1f525;专栏&#xff1a;leetcode刷题 ​ ​ LeetCode :LCR 173. 点名 这个题就是缺失的数字&#xff0c;我们可以通过三种方式来解决这个问题。 1.可以通过位异或的方式来找到这个数&#xff08;相同的数异或为…...

Gin框架操作指南06:POST绑定(下)

官方文档地址&#xff08;中文&#xff09;&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;没用过Gin的读者强烈建议先阅读第一节&#xff1a;Gin操作指南&#xff1a;开山篇。 本节继续演示POST绑定&#xff0c;包括将request-body绑定到不同的结构体中&#x…...

LLaMA、llama.cpp和Ollama区别

LLaMA&#xff1a;LLaMA是由Meta&#xff08;Facebook的母公司&#xff09;开源的大型语言模型&#xff0c;它提供了不同规模的模型&#xff0c;包括1B、3B、11B和90B等参数规模的版本。LLaMA模型支持多语言对话&#xff0c;并在多个基准数据集上进行了评估&#xff0c;展现出与…...

NDK开发

NDK介绍 app为什么会把代码放到so中 a) C语言历史悠久&#xff0c;有很多现成的代码可用 b) C代码执行效率比Java高 c) Java代码很容易被反编译&#xff0c;而且反编译以后的逻辑很清晰 为什么要学习NDK开发 在安卓的so开发中&#xff0c;其他基本与C/C开发一致&#xff…...

docker overlay 占用空间太大,迁移到 /data/

将 Docker 的 overlay 存储驱动迁移到 /data/ 目录下&#xff0c;可以通过以下步骤完成&#xff1a; 1. 停止 Docker 服务 首先&#xff0c;停止 Docker 服务以确保没有容器在运行&#xff0c;并且数据不会被写入到当前的存储位置。 sudo systemctl stop docker2. 备份现有数…...

Windows性能监控与调优:让电脑运行如飞

一、性能监控 1. 使用任务管理器深入监控 打开任务管理器 我们可以通过按下Ctrl Shift Esc快捷键来打开任务管理器。 或者右键点击任务栏空白处&#xff0c;选择“任务管理器”。 查看性能 在任务管理器中&#xff0c;点击“性能”标签页。 我们可以看到“概览”标签&#x…...

前端响应式布局

1.什么是响应式布局&#xff1f; 响应式布局是一种使网页在不同设备&#xff08;如手机、平板和桌面&#xff09;上均能良好显示的设计理念。 2.响应式布局的原理&#xff1f; 通过灵活的网格布局、CSS 媒体查询和弹性单位等技术&#xff0c;实现内容自适应屏幕尺寸变化。 3.响…...

力扣MySQL 1581

先把两张表连接&#xff0c;amount为null 的正是我们需要的&#xff0c;再按customer_id聚合 select Visits.visit_id,customer_id ,Transactions.visit_id ,transaction_id ,amount from Visits left join Transactions on Visits.visit_idTransactions.visit_id 正确代码&…...

就是这个样的粗爆,手搓一个计算器:科学计算器

作为程序员&#xff0c;没有合适的工具&#xff0c;就得手搓一个&#xff0c;PC端&#xff0c;移动端均可适用。废话不多说&#xff0c;直接上代码。 HTML: <div class"calculator"><div class"display-wrapper"><div class"display…...

wordpress使用popup弹窗插件的对比

您在寻找最好的 WordPress 弹出插件吗&#xff1f;大多数网站利用某种形状或形式的弹出窗口来将访问者指向他们希望他们去的地方。例如&#xff0c;这可能用于结帐、电子邮件订阅或用于生成潜在客户。 表现 弹出插件会减慢您的网站速度。当插件使用 WordPress 跟踪弹出窗口的…...

开源OpenStack

1.查询HCS基于OpenStack哪个版本开发 2.九大核心组件 OpenStack可以对接FC也可以对接KVM主机&#xff1b;&#xff08;OpenStack 对接华为FusionCompute&#xff0c;一个集群对应 openstack 一台计算主机&#xff09;-引申出nova compute 2.1nova nova两个核心组件nova contro…...

基于Spring Boot+vue技术的导游系统设计与实现

论文下载【免费】基于SpringBootvue技术的导游系统设计与实现资源-CSDN文库 摘 要 本研究背景主要聚焦于当前旅游业信息化、智能化的发展趋势。随着移动互联网的普及和人们出行方式的多样化&#xff0c;导游系统作为旅游服务的重要组成部分&#xff0c;亟需进行技术革新以提…...

软件测试 —— 灰度测试及测试流程!

软件测试中的灰度测试是一种结合了黑盒测试和白盒测试特点的测试方法&#xff0c;旨在通过逐步扩大测试范围来评估新系统或新功能在真实环境中的性能和稳定性。灰度测试是软件开发过程中的一个重要环节&#xff0c;它有助于在全面发布前发现并修复潜在问题&#xff0c;同时收集…...

中科星图GVE(案例)——AI实现光伏面板提取

目录 简介 函数 gve.Services.AI.solarExtraction(image) 代码 结果 知识星球 机器学习 简介 光伏面板提取是一种将光伏面板从图像或视频中准确地分割出来的任务&#xff0c;可以通过使用深度学习算法来实现。 以下是一种基于深度学习的光伏面板提取的实现步骤&#x…...

一种压缩QRCode矩阵以用于存储的方法

通常QRCode由服务器生成&#xff0c;以图片格式发送到客户端&#xff0c;由客户端直接展示&#xff0c;也可以由客户端使用javascript或其他内置的SDK直接生成。 0、需求 QRCode生成过程中往往是先生成矩阵&#xff0c;然后使用矩阵生成图片&#xff0c;矩阵就是由01组成的一…...

鸿蒙HarmonyOS开发:系统服务

拨打电话 call.makeCall 跳转到拨号界面&#xff0c;并显示待拨出的号码。使用callback异步回调。 makeCall(phoneNumber: string, callback: AsyncCallback<void>): voidimport { call } from kit.TelephonyKit;import { BusinessError } from kit.BasicServicesKit;c…...

【Go】GO语言知识总结浅析

Go语言是一种现代化的编程语言&#xff0c;由Google于2007年设计并于2009年发布。它旨在使编程变得简单、高效&#xff0c;并且可以在多核处理器上轻松构建高性能应用。Go语言的编程思想、发展历史、版本特点、运行原理、数据类型、应用场景&#xff0c;以及在web开发、网络编程…...

GWO-Transformer-LSTM灰狼算法优化深度学习多变量回归预测(Maltab)

GWO-Transformer-LSTM灰狼算法优化深度学习多变量回归预测&#xff08;Maltab&#xff09; 目录 GWO-Transformer-LSTM灰狼算法优化深度学习多变量回归预测&#xff08;Maltab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现灰狼算法OOA-Transf…...

上市公司企业供应链抵抗力数据集(2012-2023年)

一、测算方式&#xff1a;参考《财经研究》张树山&#xff08;2024&#xff09;老师的做法&#xff0c;供应链抵抗力&#xff08;Resis&#xff09;体现了供应链运行状态的稳定性&#xff0c;即在应对外部扰动时&#xff0c;供应链仍能维持循环畅通。本文从稳固供应链关系来筛选…...

javaWeb项目-ssm+jsp-XX牙科诊所管理系统功能介绍

本项目源码&#xff08;点击下方链接下载&#xff09;&#xff1a;java-ssmjsp私人牙科诊所管理系统实现源码(项目源码-说明文档)资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&…...