分布式锁的实现

什么是分布式锁?

为了保证一个方法或属性在高并发情况下的同一时间只能被同一个线程执行,在传统单体应用单机部署的情况下,可以使用Java并发处理相关的API(如ReentrantLock或Synchronized)进行互斥控制。在单机环境中,Java中提供了很多并发处理相关的API。
但是,随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。
为了解决这个问题就需要一种 跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁应该具备的条件

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

基于Redis实现的分布式锁

使用setnx命令实现分布式锁
setNx:当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。
上面的例子很容易产生死锁,并且 setnx和expire(设置超时时间)不是原子操作,当setnx后,redis宕掉,则此锁无法释放掉!
故我们谈到redis分布式锁实现,更多使用set来获取锁,lua脚本来释放锁,由于set是原子操作,可以达到setNX同时设置expire的效果

Redis分布式锁存在的问题

  • 锁超时
    如果在加锁和释放锁之间,业务逻辑执行时间太长,导致超出了锁的超时限制,就会出现锁过期问题;
    换句话说,就是第一台应用执行了业务,导致锁过期;第二台应用此时可以获得锁,进行执行业务,此时第一台应用释放了锁,第二台应用在执行业务的时第三台应用获得了锁执行业务,导致在执行过程中,会有2台应用在同时执行业务逻辑;
    解决思路:
    在释放锁的时候出现了问题,即每台应用都可以释放锁,这会造成1应用的锁释放了2应用锁的问题,换句话说,很多人手中持有的钥匙是通用的,都可以开同一个门;
    为了避免这个问题,就是1 应用只能释放1应用上的锁,2应用只能释放2应用上的锁,则需要对释放锁进行身份校验;
    由于上锁的时候key是唯一,但value可以不同,所以可以根据value进行身份的唯一标识,随机数就是一个很好的选择;

关于看门狗:
redisson中提供了看门狗机制,对锁超时的情况进行处理,进行了锁的自动延期,但是博主在其他地方看到有说watch dog只有针对没有设置过期时间的锁才会使用,由于目前工作中更多使用的是基于zk实现的分布式锁,故此处暂时保留,日后补充。

基于Zookeeper实现的分布式锁

zookeeper的节点分为四种:

  1. 持久节点
    默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在 。
  2. 顺序持久节点
    在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号
  3. 临时节点
    当创建节点的客户端与zookeeper断开连接后,临时节点会被删除
  4. 临时顺序节点
    在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与zookeeper断开连接后,临时节点会被删除。

zookeeper实现分布式锁依靠的是临时顺序节点

获取锁

  1. 首先,在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时,需要在ParentLock这个节点下面创建一个临时顺序节点 Lock1。

分布式锁的实现
  1. 之后,Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。

分布式锁的实现
  1. 这时候,如果再有一个客户端 Client2 前来获取锁,则在ParentLock下再创建一个临时顺序节点Lock2。

分布式锁的实现
  1. Client2查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock2是不是顺序最靠前的一个,结果发现节点Lock2并不是最小的。
    于是,Client2向排序仅比它靠前的节点Lock1注册Watcher,用于监听Lock1节点是否存在。这意味着Client2抢锁失败,进入了等待状态。

分布式锁的实现
  1. 这时候,如果又有一个客户端Client3前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock3。

分布式锁的实现
  1. Client3查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock3是不是顺序最靠前的一个,结果同样发现节点Lock3并不是最小的。
    于是,Client3向排序仅比它靠前的节点Lock2注册Watcher,用于监听Lock2节点是否存在。这意味着Client3同样抢锁失败,进入了等待状态。

分布式锁的实现

这样一来,Client1得到了锁,Client2监听了Lock1,Client3监听了Lock2。这恰恰形成了一个等待队列,很像是Java当中ReentrantLock所依赖的

释放锁

释放锁分为两种情况

  1. 任务完成,客户端显示释放
    当任务完成时,Client1会显式调用删除节点Lock1的指令。

分布式锁的实现
  1. 任务执行过程中,客户端崩溃

  2. 获得锁的Client1在任务执行过程中,如果Duang的一声崩溃,则会断开与Zookeeper服务端的链接。根据临时节点的特性,相关联的节点Lock1会随之自动删除。

分布式锁的实现
  • 由于Client2一直监听着Lock1的存在状态,当Lock1节点被删除,Client2会立刻收到通知。这时候Client2会再次查询ParentLock下面的所有节点,确认自己创建的节点Lock2是不是目前最小的节点。如果是最小,则Client2顺理成章获得了锁。

分布式锁的实现
  • 同理,如果Client2也因为任务完成或者节点崩溃而删除了节点Lock2,那么Client3就会接到通知。

分布式锁的实现

在curator中是通过InterProcessMutex类去实现的分布式锁。

参考链接

三种实现分布式锁的方式—夏目
redis 分布式锁setnx的缺陷与替代方案

Original: https://www.cnblogs.com/winter0730/p/15543443.html
Author: cos晓风残月
Title: 分布式锁的实现

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/576244/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 高并发组件了解

    消息队列 A服务和多个服务耦合,内部维护对多个服务发送数据的接口,那么这些接口如果有的挂了,有的不需要了,那么还得修改A内部的代码,如果使用MQ,A发送消息就好,不必考虑那么多事情…

    数据库 2023年6月16日
    069
  • 打破千篇一律,DIY属于自己独一无二的商城

    随着线上购物成为了人们的主要消费之一,搭建商城系统也成为一大热门的发展方向,在现在的电商市场中,经营的主体规模非常庞大,各种各样的电商系统琳琅满目,但是只要仔细观察就会发现,有很大…

    数据库 2023年6月14日
    0111
  • MYSQL基础学习笔记

    关系型数据库与非关系型数据库 1、关系型数据库(SQL):主要依据”一对一、一对多、多对多”等关系模型来创建的数据库。 2、非关系型数据库(NoSQL):主…

    数据库 2023年5月24日
    080
  • MySQL的文件系统(Linux环境)

    /var/lib/mysql/ 可通过以下sql命令查看: mysql> show variables like ‘datadir’; +—————+–…

    数据库 2023年5月24日
    076
  • MySQL45讲之表空间回收

    本文介绍了为什么在删除部分表数据后表文件的大小保持不变,以及如何回收表空间。 [En] This article describes why the size of the tab…

    数据库 2023年5月24日
    067
  • Github 星标 8K+ 这款国人开源的 Redis 可视化管理工具,真香…

    做程序员就少不了与一些工具打交道,比如:监控工具、管理工具等,有些工具是命令行界面,有些工具是可视化界面,反正都是可以能够满足日常使用的功能需求。 对于redis管理工具来说,也有…

    数据库 2023年6月9日
    0116
  • 当你想静下来的时候,你就可以静下来。

    当你想静下来的时候,你就可以静下来。1,2年前,我有时还在为当时选的专业恼悔,因为继续教育是同事推荐的,最后同事给我的消息是,他在疫情后去其他公司,做人工智能的公司,拿月薪20K,…

    数据库 2023年6月11日
    097
  • 【转】SpringBoot多模块打包瘦身分离

    背景 首先项目是个多层级的多模块springBoot项目,每次打出来的jar包都在90M,其中包含核心代码以及所有依赖的jar包,上传到服务器速度比较慢。 由于核心代码(contr…

    数据库 2023年6月6日
    0107
  • 高可用 | repmgr 构建 PostgreSQL 高可用集群部署文档【建议收藏】

    作者:颜博 青云科技数据库研发工程师目前从事 PostgreSQL 产品开发工作,热衷于 PostgreSQL 数据库的学习和研究 上一期我们介绍了 PG 集群复制管理工具 rep…

    数据库 2023年5月24日
    078
  • Java 多线程共享模型之管程(上)

    主线程与守护线程 默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制…

    数据库 2023年6月16日
    091
  • English words 930 2022

    low hanging fruit 本文来自博客园,作者:ukyo–BlackJesus,转载请注明原文链接:https://www.cnblogs.com/ukzq/…

    数据库 2023年6月11日
    0109
  • MySQL实战45讲 19

    19 | 为什么我只查一行的语句,也执行这么慢? 有些情况下,”查一行”,也会执行得特别慢。 需要说明的是,如果 MySQL 数据库本身就有很大的压力,导致…

    数据库 2023年6月14日
    078
  • 翻译 | 解读首部 Kubernetes 纪录片

    Honeypot.io 自诩为欧洲最大的技术人才招聘平台,同时提供开发者视频网站,又被称其为 ” 开发者的 Netflix“。2022 年 1 月,该公司与…

    数据库 2023年5月24日
    085
  • requests模块的使用

    安装:pip install requests get请求 1、发送 get 请求 import requests header = { ‘referer’: ‘https://w…

    数据库 2023年6月9日
    0107
  • BeautifulSoup4的使用

    Beautiful Soup 主要是用来解析提取 HTML 和 XML 文件中的数据。 现在官网推荐使用 Beautiful Soup 4 ,已经被移植到了BS4中。 安装 Bea…

    数据库 2023年6月9日
    0107
  • django-ckeditor上传图片到阿里云OSS

    参考信息 资料1: django_ckeditor上传图片到七牛云OSS 使用 1. 安装django-ckeditor 【参考 资料1 】 2. setting.py配置INST…

    数据库 2023年6月9日
    0104
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球