分布式核心技术之分布式锁

文章目录

    • 为什么要使用分布锁?
    • 分布式锁的三种实现方法
      • 基于数据库实现分布式锁
      • 基于缓存实现分布式锁
      • 基于 ZooKeeper 实现分布式锁
        • 知识扩展:如何解决分布式锁的羊群效应问题?
    • 三种实现方式对比

  • 分布式互斥,领悟了其“有你没我,有我没你”的精髓,为你解释了同一临界资源同一时刻只能被一个程序访问的问题,并介绍了解决分布式互斥的算法。分布式互斥的算法主要讲了如何协调多个进程获取权限和根据权限有序访问共享资源,“获得访问权限的进程可以访问共享资源,其他进程必须等待拥有该权限的进程释放权限”。但是,并没有介绍在访问共享资源时,这个权限是如何设置或产生的,以及设置或产生这个权限的工作原理是什么。分布式锁是如何解决这个问题的

为什么要使用分布锁?

  • 在单机多线程环境中,我们经常遇到多个线程访问同一个共享资源(这里需要注意的是:在很多地方,这种资源会称为临界资源,但在今天这篇文章中,我们统一称之为共享资源)的情况。为了维护数据的一致性,我们需要某种机制来保证只有满足某个条件的线程才能访问资源,不满足条件的线程只能等待,在下一轮竞争中重新满足条件时才能访问资源。这个机制指的是,为了实现分布式互斥,在某个地方做个标记,这个标记每个线程都能看到,到标记不存在时可以设置该标记,当标记被设置后,其他线程只能等待拥有该标记的线程执行完成,并释放该标记后,才能去设置该标记和访问共享资源。这里的标记,就是我们常说的锁。也就是说,锁是实现多线程同时访问同一共享资源,保证同一时刻只有一个线程可访问共享资源所做的一种标记
  • 与普通锁不同的是,分布式锁是指分布式环境下,系统部署在多个机器中,实现多进程分布式互斥的一种锁。为了保证多个进程能看到锁,锁被存在公共存储(比如 Redis、Memcache、数据库等三方存储中),以实现多个进程并发访问同一个临界资源,同一时刻只有一个进程可访问共享资源,确保数据的一致性。
  • 在大规模分布式系统中,单个机器的线程锁无法管控多个机器对同一资源的访问,这时使用分布式锁,就可以把整个集群当作一个应用一样去处理,实用性和扩展性更好。

分布式锁的三种实现方法

  • 实现分布式锁的 3 种主流方法,即:
    • 基于数据库实现分布式锁;(数据库指的是关系型数据库)
    • 基于缓存实现分布式锁;
    • 基于 ZooKeeper 实现分布式锁。

在这里插入图片描述

基于数据库实现分布式锁

  • 要实现分布式锁,最简单的方式就是创建一张锁表,然后通过操作该表中的数据来实现。当我们要锁住某个资源时,就在该表中增加一条记录,想要释放锁的时候就删除这条记录。数据库对共享资源做了唯一性约束,如果有多个请求被同时提交到数据库的话,数据库会保证只有一个操作可以成功,操作成功的那个线程就获得了访问共享资源的锁,可以进行操作。

  • 基于数据库实现的分布式锁,是最容易理解的。但是,因为数据库需要落到硬盘上,频繁读取数据库会导致 IO 开销大,因此这种分布式锁适用于并发量低,对性能要求低的场景。对于双 11、双 12 等需求量激增的场景,数据库锁是无法满足其性能要求的。而在平日的购物中,我们可以在局部场景中使用数据库锁实现对资源的互斥访问。

  • 基于数据库实现分布式锁比较简单,绝招在于创建一张锁表,为申请者在锁表里建立一条记录,记录建立成功则获得锁,消除记录则释放锁。该方法依赖于数据库,主要有两个缺点:

    • 单点故障问题。一旦数据库不可用,会导致整个系统崩溃。
    • 死锁问题。数据库锁没有失效时间,未获得锁的进程只能一直等待已获得锁的进程主动释放锁。一旦已获得锁的进程挂掉或者解锁操作失败,会导致锁记录一直存在数据库中,其他进程无法获得锁。

基于缓存实现分布式锁

  • 基于缓存实现分布式锁的方式,非常适合解决这种场景下的问题。所谓基于缓存,也就是说把数据存放在计算机内存中,不需要写入磁盘,减少了 IO 读写。以 Redis 为例。
  • Redis 通常可以使用 setnx(key, value) 函数来实现分布式锁。key 和 value 就是基于缓存的分布式锁的两个属性,其中 key 表示锁 id,value = currentTime + timeOut,表示当前时间 + 超时时间。也就是说,某个进程获得 key 这把锁后,如果在 value 的时间内未释放锁,系统就会主动释放锁。setnx 函数的返回值有 0 和 1:
    • 返回 1,说明该服务器获得锁,setnx 将 key 对应的 value 设置为当前时间 + 锁的有效时间。
    • 返回 0,说明其他服务器已经获得了锁,进程不能进入临界区。该服务器可以不断尝试 setnx 操作,以获得锁。

例子

  • 以电商售卖吹风机的场景为例。假设现在库存数量是足够的,有 5 个来自不同地区的用户{A,B,C,D,E}想要购买,其中用户 A 想买 1 个,用户 B 想买 2 个,用户 C 想买 1 个。
  • 用户 A 的请求因为网速快,最先到达 Server2,setnx 操作返回 1,并获取到购买吹风机的锁;用户 B 和用户 C 的请求,几乎同时到达了 Server1 和 Server3,但因为这时 Server2 获取到了吹风机数据的锁,所以只能加入等待队列。
  • Server2 获取到锁后,负责管理吹风机的服务器执行业务逻辑,只用了 1s 就完成了订单。订单请求完成后,删除锁的 key,从而释放锁。此时,排在第二顺位的 Server1 获得了锁,可以访问吹风机的数据资源。但不巧的是,Server1 在完成订单后发生了故障,无法主动释放锁。
  • 于是,排在第三顺位的 Server3 只能等设定的有效时间(比如 30 分钟)到期,锁自动释放后,才能访问吹风机的数据资源,也就是说用户 C 只能到 00:30:01 以后才能继续抢购。
    在这里插入图片描述
  • 总结来说,Redis 通过队列来维持进程访问共享资源的先后顺序。Redis 锁主要基于 setnx 函数实现分布式锁,当进程通过 setnx<key,value> 函数返回 1 时,表示已经获得锁。排在后面的进程只能等待前面的进程主动释放锁,或者等到时间超时才能获得锁。
  • 相对于基于数据库实现分布式锁的方案来说,基于缓存实现的分布式锁的优势表现在以下几个方面:
    • 性能更好。数据被存放在内存,而不是磁盘,避免了频繁的 IO 操作。
    • 很多缓存可以跨集群部署,避免了单点故障问题。
    • 很多缓存服务都提供了可以用来实现分布式锁的方法,比如 Redis 的 setnx 方法等。
    • 可以直接设置超时时间来控制锁的释放,因为这些缓存服务器一般支持自动删除过期数据。
  • 这个方案的不足是,通过超时时间来控制锁的失效时间,并不是十分靠谱,因为一个进程执行时间可能比较长,或受系统进程做内存回收等影响,导致时间超时,从而不正确地释放了锁。

基于 ZooKeeper 实现分布式锁

  • ZooKeeper 基于树形数据存储结构实现分布式锁,来解决多个进程同时访问同一临界资源时,数据的一致性问题。ZooKeeper 的树形数据存储结构主要由 4 种节点构成:
    • 持久节点。这是默认的节点类型,一直存在于 ZooKeeper 中。
    • 持久顺序节点。也就是说,在创建节点时,ZooKeeper 根据节点创建的时间顺序对节点进行编号。
    • 临时节点。与持久节点不同,当客户端与 ZooKeeper 断开连接后,该进程创建的临时节点就会被删除。
    • 临时顺序节点,就是按时间顺序编号的临时节点。
  • 根据它们的特征,ZooKeeper 基于临时顺序节点实现了分布锁。

  • 还是以电商售卖吹风机的场景为例。假设用户 A、B、C 同时在 11 月 11 日的零点整提交了购买吹风机的请求,ZooKeeper 会采用如下方法来实现分布式锁:

    1. 在与该方法对应的持久节点 shared_lock 的目录下,为每个进程创建一个临时顺序节点。如下图所示,吹风机就是一个拥有 shared_lock 的目录,当有人买吹风机时,会为他创建一个临时顺序节点。
    2. 每个进程获取 shared_lock 目录下的所有临时节点列表,注册子节点变更的 Watcher,并监听节点。
    3. 每个节点确定自己的编号是否是 shared_lock 下所有子节点中最小的,若最小,则获得锁。例如,用户 A 的订单最先到服务器,因此创建了编号为 1 的临时顺序节点 LockNode1。该节点的编号是持久节点目录下最小的,因此获取到分布式锁,可以访问临界资源,从而可以购买吹风机。
    4. 若本进程对应的临时节点编号不是最小的,则分为两种情况:
      • a. 本进程为读请求,如果比自己序号小的节点中有写请求,则等待;
      • b. 本进程为写请求,如果比自己序号小的节点中有读请求,则等待。
  • 例如,用户 B 也想要买吹风机,但在他之前,用户 C 想看看吹风机的库存量。因此,用户 B 只能等用户 A 买完吹风机、用户 C 查询完库存量后,才能购买吹风机。
    在这里插入图片描述

  • 可以看到,使用 ZooKeeper 可以完美解决设计分布式锁时遇到的各种问题,比如单点故障、不可重入、死锁等问题。虽然 ZooKeeper 实现的分布式锁,几乎能涵盖所有分布式锁的特性,且易于实现,但需要频繁地添加和删除节点,所以性能不如基于缓存实现的分布式锁。

知识扩展:如何解决分布式锁的羊群效应问题?
  • 在分布式锁问题中,会经常遇到羊群效应。所谓羊群效应,就是在整个分布式锁的竞争过程中,大量的“Watcher 通知”和“子节点列表的获取”操作重复运行,并且大多数节点的运行结果都是判断出自己当前并不是编号最小的节点,继续等待下一次通知,而不是执行业务逻辑。
  • 这,就会对 ZooKeeper 服务器造成巨大的性能影响和网络冲击。更甚的是,如果同一时间多个节点对应的客户端完成事务或事务中断引起节点消失,ZooKeeper 服务器就会在短时间内向其他客户端发送大量的事件通知。
  • 那如何解决这个问题呢?具体方法可以分为以下三步。
    1. 在与该方法对应的持久节点的目录下,为每个进程创建一个临时顺序节点。
    2. 每个进程获取所有临时节点列表,对比自己的编号是否最小,若最小,则获得锁。
    3. 若本进程对应的临时节点编号不是最小的,则继续判断:
      • 若本进程为读请求,则向比自己序号小的最后一个写请求节点注册 watch 监听,当监听到该节点释放锁后,则获取锁;
      • 若本进程为写请求,则向比自己序号小的最后一个读请求节点注册 watch 监听,当监听到该节点释放锁后,获取锁。

三种实现方式对比

在这里插入图片描述

  • 总结来说,ZooKeeper 分布式锁的可靠性最高,有封装好的框架,很容易实现分布式锁的功能,并且几乎解决了数据库锁和缓存式锁的不足,因此是实现分布式锁的首选方法。
  • 从上述分析可看出,为了确保分布式锁的可用性,我们在设计时应考虑到以下几点:
    • 互斥性,即在分布式系统环境下,分布式锁应该能保证一个资源或一个方法在同一时间只能被一个机器的一个线程或进程操作。
    • 具备锁失效机制,防止死锁。即使有一个进程在持有锁的期间因为崩溃而没有主动解锁,也能保证后续其他进程可以获得锁。
    • 可重入性,即进程未释放锁时,可以多次访问临界资源。
    • 有高可用的获取锁和释放锁的功能,且性能要好。

你知道的越多,你不知道的越多。

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

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

相关文章

绝地反击,不做背锅侠!

那么作为运维人员&#xff0c;如何摆脱以上背黑锅的尴尬局面呢&#xff1f;堡垒机当然是破解此局面的绝杀大招。 1.统一登录入口 提供统一入口&#xff0c;集中管理和分配账户密码、所有运维人员只能登录堡垒机才能访问服务器&#xff0c;梳理“人与服务器”之间的关系&#…

新版Microsoft Edge和google chrome谁才是浏览器的王者?

Microsoft Edge是一款现代化的浏览器&#xff0c;它拥有众多功能和强大的性能&#xff0c;为用户带来更加流畅的浏览体验。 Edge最近推出了分屏功能&#xff0c;支持一个窗口同时显示两个选项卡&#xff0c;这可以大大提高生产力和多任务处理能力。 一、结合平时的使用经历&…

2024 年网络安全展望:未来是什么?

为了建立强大的网络安全计划&#xff0c;组织必须首先了解整体威胁环境不断变化的性质。 人工智能在成为安全团队的帮助之前&#xff0c;将为网络犯罪分子带来巨大的福音。 网络犯罪分子和不良行为者将受益于先进人工智能工具的广泛部署&#xff0c;然后他们的目标才能建立人…

【计算机网络】—— 奈氏准则和香农定理

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 失真 - 信号的变化 ​编辑影像失真的因素&#xff1a; ​编辑信道带宽&#xff1a; 码间串扰…

使用Go语言编写基本的HTTP服务器

你是否曾经想过自己动手编写一个Web服务器&#xff1f;那种可以接收来自全世界的请求&#xff0c;然后回应一些“Hello, World!”之类的消息的服务器&#xff1f;如果你有这个想法&#xff0c;那么Go语言就是你的最佳伙伴。让我们一起踏上这段奇妙的探险之旅吧&#xff01; 首…

Docker部署Nexus Maven私服并实现远程访问Nexus界面

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 1. Docker安装Nexus2. 本地访问Nexus3. Linux安装Cpolar4. 配置Nexus界面公网地址5. 远程访问 Nexus界面6. 固定N…

【HarmonyOS开发】探究Hap与App包的结构与区别

1、Hap与App包的区别 OpenHarmony 可以进行两种形式&#xff08;Hap和App&#xff09;的打包&#xff0c;HAP是用于本地调试的&#xff0c;APP包是用于上架发布的。 根据不同的设备类型&#xff0c;一个APP包可以包含多个HAP包。 下面从两个角度进行分析 1.1 编译构建角度 编…

百度Apollo五步入门自动驾驶:Dreamview与离线数据包分析(文末赠送apollo周边)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 粉丝福利活动 ✅参与方式&#xff1a;通过连接报名观看课程&#xff0c;即可免费获取精美周边 ⛳️活动链接&#xf…

matlab设置colorbar标题的两种方式

%% 第一种 figure; A rand(3,4,3); A1 A(:,:,1); A2 A(:,:,2); A3 A(:,:,3); contourf(A1,A2,A3,30); colormap(jet);colorbar; my_handlecolorbar; my_handle.Label.String depth/km; my_handle.Label.FontSize 15;%% 第二种 figure; A rand(3,4,3); A1 A(:,:,1); A2 …

FinGPT:金融大语言模型 | 开源日报 No.127

verdaccio/verdaccio Stars: 15.0k License: MIT Verdaccio 是一个轻量级的 Node.js 私有代理仓库。 以下是 Verdaccio 的核心优势和关键特性&#xff1a; 零配置&#xff1a;无需复杂设置即可快速启动私有 npm 注册表。本地化管理&#xff1a;通过内置小型数据库进行简单而…

【STM32单片机】电子木鱼设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103/F407单片机控制器&#xff0c;TFTLCD触摸屏、蜂鸣器等。 主要功能&#xff1a; 系统运行后&#xff0c;TFTLCD显示画面&#xff0c;可触摸木鱼区域&#xff0c;功德计数加1&#xf…

【操作系统】探究进程奥秘:显示进程列表的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;Linux专栏&#xff1a;《探秘Linux | 操作系统解密》⏰诗赋清音&#xff1a;月悬苍穹泛清辉&#xff0c;梦随星河徜徉辉。情牵天际云千层&#xff0c;志立乘风意自飞。 ​ 目录 &a…

2023年国赛高教杯数学建模E题黄河水沙监测数据分析解题全过程文档及程序

2023年国赛高教杯数学建模 E题 黄河水沙监测数据分析 原题再现 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变化和人民生活的影响&#xff0c;以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾等方面都具有重要的理论指导…

Visual Studio2022配置ReSharper C++ 常用设置

如需安装免费的可以在下面留言&#xff0c;看到即回复 文章目录 Visual Studio2022配置ReSharper C 常用设置配置Visual Studio2022&#xff0c;使其能够按回车进行补全配置ReSharper C 设置自动弹出配置ReSharper C 的快捷键ReSharper C 去掉注释拼写使用中文注释 如何关闭新版…

C语言中函数调用和嵌套

函数是C语言的基本组成元素 函数调用 根据函数在程序中出现的位置有下列三种函数调用方式&#xff1a; 将函数作为表达式调用 将函数作为表达式调用时&#xff0c;函数的返回值参与表达式的运算&#xff0c;此时要求函数必须有返回值 int retmax(100,150); 将函数作为语句…

Vue使用Element表格Table设置所有单元格内容居中对齐

为单个列的单元格设置居中对齐代码如下&#xff1a; <el-table-columnprop"productInfo.productName"label"中文名"width"100"align"center"></el-table-column>需要设置el-table-column标签里面的属性设置为align"c…

掌握JWT:解密身份验证和授权的关键技术

JSON Web Token 1、什么是JWT2、JWT解决了什么问题3、早期的SSO认证4、JWT认证5、JWT优势6、JWT结构Header 标头Payload 负载 Signature 签名 7、代码实现添加依赖生成Token认证token 8、工具类9、JWT整合Web10、拦截器校验11、网关路由校验12、解决多用户登录的问题13、客户端…

积水监测识别摄像机

积水监测识别摄像机是一种利用摄像技术来监测和识别道路、桥梁、隧道等区域积水情况的设备&#xff0c;它可以有效地提供实时的积水监测信息&#xff0c;帮助交通部门和相关单位及时采取应对措施&#xff0c;确保道路交通的畅通和人员安全。 积水监测识别摄像机通过安装在适当位…

如何使用支付宝沙箱环境支付并公网调用sdk创建支付单服务

文章目录 1.测试环境2.本地配置2. 内网穿透2.1 下载安装cpolar内网穿透2.2 创建隧道3. 测试公网访问4. 配置固定二级子域名4.1 保留一个二级子域名4.2 配置二级子域名5. 使用固定二级子域名进行访问 1.测试环境 MavenSpring bootJdk 1.8 2.本地配置 获取支付宝支付Java SDK,…

【深度学习-目标检测】03 - Faster R-CNN 论文学习与总结

论文地址&#xff1a;Faster R-CNN: Towards Real-Time ObjectDetection with Region Proposal Networks 论文学习 1. 摘要与引言 研究背景与挑战&#xff1a;当前最先进的目标检测网络依赖于 区域提议&#xff08;Region Proposals&#xff09;来假设目标的位置&#xff0c…