分布式锁概述

一、概念

1、什么是分布式锁

我们知道传统进程内的多线程之间可以利用锁机制来实现它的同步,同时进程之间也可以互相通信,那我我们如果使用分布式服务的话,有应该怎么实现集群内多服务之间访问公共资源,并且确保它们不会出现问题呢?

其实有一种概念就帮我们实现了这一点,那就是分布式锁

分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。

举个不太恰当的例子:假设共享的资源就是一个房子,里面有各种书,分布式系统就是要进屋看书的人,分布式锁就是保证这个房子只有一个门并且一次只有一个人可以进,而且门只有一把钥匙。然后许多人要去看书,可以,排队,第一个人拿着钥匙把门打开进屋看书并且把门锁上,然后第二个人没有钥匙,那就等着,等第一个出来,然后你在拿着钥匙进去,然后就是以此类推,和我们传统意义上的锁是一个意思。

2、举例说明

我们举一个例子,一个应用需要部署到几台机器上然后做负载均衡,大致如下图:

上图可以看到,变量A存在三个服务器内存中(这个变量A主要体现是在一个类中的一个成员变量,是一个有状态的对象),如果不加任何控制的话,变量A同时都会在分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的!即使不是同时发过来,三个请求分别操作三个不同内存区域的数据,变量A之间不存在共享,也不具有可见性,处理的结果也是不对的!

如果我们业务中确实存在这个场景的话,我们就需要一种方法解决这个问题!

而这就是分布式锁需要解决的问题。

二、分布式锁应该具备的条件

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

2、高可用的获取锁与释放锁;

3、高性能的获取锁与释放锁;

4、具备可重入特性;

5、具备锁失效机制,防止死锁;

6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

三、分布式锁的实现方式

目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

所以,很多系统在设计之初就要对这三者做出取舍。在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证“最终一致性”,只要这个最终时间是在用户可以接受的范围内即可。

也就是说,分布式锁需要做到以下几点:

1、保证资源独立

2、弱化强一致性

3、提高效率和可用性

4、保证资源最终一致性

在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。有的时候,我们需要保证一个方法在同一时间内只能被同一个线程执行。

当前我们实现分布式锁的方式主要有三种,分别是:

 基于数据库实现分布式锁;
 基于缓存(Redis等)实现分布式锁;
 基于Zookeeper实现分布式锁;

1、基于数据库

基于数据库的实现方式的核心思想是:在数据库中创建一个表,表中包含方法名等字段,并在方法名字段上创建唯一索引,想要执行某个方法,就使用这个方法名向表中插入数据,成功插入则获取锁,执行完成后删除对应的行数据释放锁。

1.1、具体做法

1、创建一个表

 CREATE TABLE `method_lock` (
   `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
   `method_name` varchar(64) NOT NULL COMMENT '锁定的方法名',
   `desc` varchar(255) NOT NULL COMMENT '备注信息',
   `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   PRIMARY KEY (`id`),
   UNIQUE KEY `uidx_method_name` (`method_name`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='锁定中的方法';

img

2、想要执行某个方法,就使用这个方法名向表中插入数据:

INSERT INTO method_lock (method_name, desc) VALUES ('methodName', '测试的methodName');

因为我们对method_name做了唯一性约束,这里如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,可以执行方法体内容。

3、成功插入则获取锁,执行完成后删除对应的行数据释放锁:

delete from method_lock where method_name ='methodName';

1.2、存在问题

使用基于数据库的这种实现方式很简单,但是对于分布式锁应该具备的条件来说,它有一些问题需要解决及优化:

1、因为是基于数据库实现的,数据库的可用性和性能将直接影响分布式锁的可用性及性能,所以,数据库需要双机部署、数据同步、主备切换;

2、不具备可重入的特性,因为同一个线程在释放锁之前,行数据一直存在,无法再次成功插入数据,所以,需要在表中新增一列,用于记录当前获取到锁的机器和线程信息,在再次获取锁的时候,先查询表中机器和线程信息是否和当前机器和线程相同,若相同则直接获取锁;

3、没有锁失效机制,因为有可能出现成功插入数据后,服务器宕机了,对应的数据没有被删除,当服务恢复后一直获取不到锁,所以,需要在表中新增一列,用于记录失效时间,并且需要有定时任务清除这些失效的数据;

4、不具备阻塞锁特性,获取不到锁直接返回失败,所以需要优化获取逻辑,循环多次去获取。

5、在实施的过程中会遇到各种不同的问题,为了解决这些问题,实现方式将会越来越复杂;依赖数据库需要一定的资源开销,性能问题需要考虑。

2、基于Redis

2.1、选用Redis的原因

(1)Redis有很高的性能;

(2)Redis命令对此支持较好,实现起来比较方便

2.2、使用命令

使用命令介绍:

(1)SETNX

SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。

(2)expire

expire key timeout:为key设置一个超时时间,单位为second,超过这个时间锁会自动释放,避免死锁。

(3)delete

delete key:删除key

在使用Redis实现分布式锁的时候,主要就会使用到这三个命令。

2.3、实现思想

(1)获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

(2)获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。

(3)释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。

2.4、简单实现

#连接redis
 redis_client = redis.Redis(host="localhost",
                            port=6379,
                            password=password,
                            db=10)
 ​
 #获取一个锁
 lock_name:锁定名称
 acquire_time: 客户端等待获取锁的时间
 time_out: 锁的超时时间
 def acquire_lock(lock_name, acquire_time=10, time_out=10):
     """获取一个分布式锁"""
     identifier = str(uuid.uuid4())
     end = time.time() + acquire_time
     lock = "string:lock:" + lock_name
     while time.time() < end:
         if redis_client.setnx(lock, identifier):
             # 给锁设置超时时间, 防止进程崩溃导致其他进程无法获取锁
             redis_client.expire(lock, time_out)
             return identifier
         elif not redis_client.ttl(lock):
             redis_client.expire(lock, time_out)
         time.sleep(0.001)
     return False
 ​
 #释放一个锁
 def release_lock(lock_name, identifier):
     """通用的锁释放函数"""
     lock = "string:lock:" + lock_name
     pip = redis_client.pipeline(True)
     while True:
         try:
             pip.watch(lock)
             lock_value = redis_client.get(lock)
             if not lock_value:
                 return True
 ​
             if lock_value.decode() == identifier:
                 pip.multi()
                 pip.delete(lock)
                 pip.execute()
                 return True
             pip.unwatch()
             break
         except redis.excetions.WacthcError:
             pass
     return False

3、基于ZooKeeper

ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名。基于ZooKeeper实现分布式锁的步骤如下:

  1. 创建一个目录mylock;

  2. 线程A想获取锁就在mylock目录下创建临时顺序节点;

  3. 获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁;

  4. 线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点;

  5. 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。

优点:具备高可用、可重入、阻塞锁特性,可解决失效死锁问题。

缺点:因为需要频繁的创建和删除节点,性能上不如Redis方式。

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

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

相关文章

RobotMaster学习——工序导入,参数设置,轨迹生成

目录 引出1.导入工序2.修改刀具其他刀具参数 3.进行工序分配4.设置TCP5.设置工作站6.工序整体导入配置7.进行计算 总结 引出 RobotMaster的操作流程&#xff0c;从导入工序到生产轨迹。 1.导入工序 2.修改刀具 要选择第七把刀具 其他刀具参数 第一把刀具 第二把刀具 第三把刀…

自制数据库空洞率清理工具-C版-01-EasyClean-V1.0(支持南大通用数据库Gbase8a)

目录 一、环境信息 二、简述 三、支持功能 四、空洞率 五、工具流程图 六、安装包下载地址 七、参数介绍 1、命令模板 2、命令样例 3、参数表格 八、安装步骤 1、配置环境变量 2、生效环境变量 3、检验动态链接是否正常 九、运行效果 一、环境信息 名称值CPUInt…

MySQL——内置函数

目录 一.日期函数 1.current_date() 2.current_time() 3.current_stamp() 4.date_add() 5.date_sub() 6.datediff 7.date 8.now 二.字符串函数 1.charset() 2.concat() 3.length() 4.replace 5.substring(str,postion,length) 6.instr&#xff08;string,substr…

基于ssm+jsp二手车估值与销售网络平台源码和论文

随着信息化时代的到来&#xff0c;管理系统都趋向于智能化、系统化&#xff0c;二手车估值与销售网络平台也不例外&#xff0c;但目前国内仍都使用人工管理&#xff0c;市场规模越来越大&#xff0c;同时信息量也越来越庞大&#xff0c;人工管理显然已无法应对时代的变化&#…

ansible-playbook的Temlates模块 tags模块 Roles模块

Temlates模块 jinja模板架构&#xff0c;通过模板可以实现向模板文件传参(python转义)把占位符参数传到配置文件中去,生产一个目标文本文件&#xff0c;传递变量到需要的配置文件当中 &#xff08;web开发&#xff09; nginx.conf.j2 早文件当中配置的是占位符&#xff08;声明…

vivado 自动派生时钟

自动派生时钟 自动派生的时钟也称为自动生成的时钟。Vivado IDE自动在时钟修改块&#xff08;CMB&#xff09;的输出引脚上创建这些的约束&#xff0c;只要已经定义了相关的主时钟。在AMD 7系列设备系列中&#xff0c;CMB有&#xff1a; •MMCM*/PLL* •BUFR •相位器* 在…

Spring源码分析 @Autowired 是怎样完成注入的?究竟是byType还是byName亦两者皆有

1. 五种不同场景下 Autowired 的使用 第一种情况 上下文中只有一个同类型的bean 配置类 package org.example.bean;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class FruitCo…

过度加大SSD内部并发何尝不是一种伤害-part2

方案设计完了&#xff0c;如何验证效果如何呢&#xff1f;作者是这么做的。 第一步&#xff0c;选择模拟环境&#xff1a;PLAN方案在定制的FEMU&#xff08;Flash Emulation Module&#xff09;上实现&#xff0c;该模块支持TRIM和多流功能&#xff0c;具体参数如下&#xff1…

modbus_tcp的实现 through python.

0.引言 当前科技似乎处于加速发展期&#xff0c;各个模块都在快速迭代&#xff0c;迭代的速度会让既有的一些经验产生问题&#xff0c;在用python实现modbus_tcp协议时&#xff0c;网上流传的一些代码中import语句会出现问题。导致pymodbus模块用起来很不好用。 这个原因出在…

基于YOLOv8深度学习的智能玉米害虫检测识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

react当中生命周期(旧生命周期详解)

新生命周期https://blog.csdn.net/kkkys_kkk/article/details/135156102?spm1001.2014.3001.5501 目录 什么是生命周期 react中的生命周期 旧生命周期 生命周期图示 常用的生命周期钩子函数 初始化阶段 挂载阶段 在严格模式下挂载阶段的生命周期函数会执行两次原因 更…

W25Q128

什么是 W25Q128 &#xff1f; W25Q128是一款由Winbond&#xff08;威邦电子&#xff09;公司生产的闪存存储器芯片&#xff0c;属于串行闪存系列。具体来说&#xff0c;W25Q128是一颗128Mb&#xff08;兆位&#xff09;容量的串行闪存芯片&#xff0c;其中"W"代表Wi…

Python匹配文件模块的实战技巧

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在Python中&#xff0c;文件匹配是许多应用中常见的需求&#xff0c;例如文件管理、数据处理等。本文将深入探讨Python中用于文件匹配的模块&#xff0c;包括glob、fnmatch和os.path等&#xff0c;通过丰富的示例…

第11章 GUI Page403~405 步骤三 设置滚动范围

运行效果&#xff1a; 源代码&#xff1a; /**************************************************************** Name: wxMyPainterApp.h* Purpose: Defines Application Class* Author: yanzhenxi (3065598272qq.com)* Created: 2023-12-21* Copyright: yanzhen…

C# 跨越配置

跨越配置1 项目框架 .NET Framework 1.web.config配置 在system.webServer节点中添httpProtocol子节点 Access-Control-Allow-Origin值为“*”” <httpProtocol><customHeaders><add name"Access-Control-Allow-Origin" value"*" /><…

基于DeepSpeed对 llama2-7b的LORA精调

DeepSpeed数据并行研究 1. 技术调研 a. DeepSpeed DeepSpeed是一个开源深度学习训练优化库&#xff0c;其中包含一个新的显存优化技术—— ZeRO&#xff08;零冗余优化器&#xff09;。该框架包含四个技术亮点&#xff1a; 用 3D 并行化实现万亿参数模型训练&#xff1a; D…

【算法】使用二分查找解决算法问题:理解二分法思想,模板讲解与例题实践

文章目录 二分算法思想 / 性质 / 朴素模板二分查找的引入&#xff08;二段性&#xff09;704.二分查找 模板34.在排序数组中查找元素的第一个和最后一个位置 二分查找的前提条件 / 时间复杂度分析 算法题69.x的平方根35.搜索插入位置852.山脉数组的峰顶索引162.寻找峰值153.寻找…

服务熔断(Hystrix)

服务雪崩 多个微服务之间调用的时候&#xff0c;假设微服务A调用微服务B和微服务C&#xff0c;微服务B和微服务C又调用其他的微服务&#xff0c;这就是所谓的“扇出”&#xff0c;如果扇出的链路上某个微服务的调用响应时间过长&#xff0c;或者不可用&#xff0c;对微服务A的…

《每天一分钟学习C语言·六》

1、 1字节&#xff08;Byte&#xff09;8位&#xff0c;1KB1024字节&#xff0c;1M1024KB&#xff0c;1G1024MB 2、 char ch A; printf(“ch %d\n”, ch);ch为65 这里是ASCII码转换 3、 scanf("%d", &i); //一般scanf直接加输入控制符 scanf("m%d&qu…