技术广度必备——高并发设计之分布式锁的实现方式

news2024/11/27 0:31:37

在这里插入图片描述

文章目录

  • 问题背景
  • 前言
  • 实现
    • 基于MySQL实现
      • 唯一索引
      • 乐观锁
      • 悲观锁
    • 基于Redis
    • 基于Zookeeper
      • 原理
      • 使用Curator框架实现ZK分布式锁
      • 缺点

问题背景

研究有哪几种方案可以实现分布式锁,技术选型的场景下能用到。

前言

  1. 本文参考过的文章有分布式锁的几种实现方式
  2. 方式大致分为3种:基于磁盘存储的关系型数据库MySQL;基于内存的数据库Redis;基于Zookeeper

实现

基于MySQL实现

基于MySQL也有3种方式:基于唯一索引;基于MySQL行锁的乐观锁;基于悲观锁

唯一索引

利用唯一索引,在竞争锁时用insert操作,insert成功表示获取锁成功。下面专门创建一张表实现分布式锁:

CREATE TABLE `database_lock` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `resource` int NOT NULL COMMENT '锁定的资源',
    `description` varchar(1024) NOT NULL DEFAULT "" COMMENT '描述',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uiq_idx_resource` (`resource`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据库分布式锁表';

通过insert操作竞争锁,通过delete操作释放锁。

优点:实现简单,复杂度低
缺点

  1. 锁没有过期时间。可以利用RocketMQ的延时消息或者延时线程池、利用定时任务使锁过期。
  2. 强依赖于数据库。建议设置备库或者集群,避免的单点数据库挂了,提高可靠性。
  3. 非阻塞。竞争锁失败则会继续往下执行业务,不会继续重试获取锁,也不会阻塞等待。可以使用 for、while实现阻塞等待或循坏尝试竞争锁。
  4. 非可重入。数据库表没有记录获取到锁的唯一信息(比如线程信息、进程信息、主机信息),增加字段记录这些信息实现可重入。竞争锁时根据自身的信息(线程信息、进程信息、主机信息)查询表,表中有记录则获取锁成功,实现了可重入。
  5. 如果并发高,则会有大量请求失败或者大量写压力给到数据库。

乐观锁

利用mysql自带的行锁机制,前提是本身就要在数据库里面有数据

CREATE TABLE `optimistic_lock` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `resource` int NOT NULL COMMENT '锁定的资源',
    `version` int NOT NULL COMMENT '版本信息',
    `created_at` datetime COMMENT '创建时间',
    `updated_at` datetime COMMENT '更新时间',
    `deleted_at` datetime COMMENT '删除时间', 
    PRIMARY KEY (`id`),
    UNIQUE KEY `uiq_idx_resource` (`resource`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据库分布式锁表';

实现的SQL如下:

update optimistic_lock set resource =  xx, version = newVersion where id = xx and version = oldVersion;

解释:需要通过主键id去加行锁,否则会走范围锁或者表锁。更新时不仅要更新资源值,也要更新版本信息。查询需要带上版本号信息。

优缺点与唯一索引是宪法方式一样。大量并发下会有大量请求失败或者写压力很高,不适合高并发。

悲观锁

利用MySQL的X排他锁

在查询语句后面增加FOR UPDATE,数据库会在查询过程中给数据库表增加悲观锁,也称排他锁。当某条记录被加上悲观锁之后,其它线程也就无法再改行上增加悲观锁。

在使用悲观锁时,我们必须关闭MySQL数据库的自动提交属性(参考下面的示例),因为MySQL默认使用autocommit模式

mysql> SET AUTOCOMMIT = 0; 

这样在使用FOR UPDATE获得锁之后可以执行相应的业务逻辑,执行完之后再使用COMMIT来释放锁。

优点
在悲观锁中,每一次行数据的访问都是独占的,只有当正在访问该行数据的请求事务提交以后,其他请求才能依次访问该数据,否则将阻塞等待锁的获取。悲观锁可以严格保证数据访问的安全。

缺点

  1. 每次请求都会额外产生加锁的开销且未获取到锁的请求将会阻塞等待锁的获取,在高并发环境下,容易造成大量请求阻塞,影响系统可用性。另外,悲观锁使用不当还可能产生死锁的情况。

  2. 使用排他锁来进行分布式锁的lock,那么一个排他锁长时间不提交,就会占用数据库连接。一旦类似的连接变得多了,就可能把数据库连接池撑爆

基于Redis

详情见笔者写的大厂的Redis分布式锁是如何设计的

基于Zookeeper

原理

利用ZK的临时顺序节点实现分布式锁。

  1. ZooKeeper的每一个节点都是一个天然的顺序发号器在每一个节点下面创建临时顺序节点(EPHEMERAL_SEQUENTIAL)类型,新的子节点后面会加上一个顺序编号。这个顺序编号是在上一个生成的顺序编号加1。
  2. ZooKeeper节点的递增有序性可以确保锁的公平。一个ZooKeeper分布式锁,首先需要创建一个父节点,尽量是持久节点(PERSISTENT类型),然后每个要获得锁的线程都在这个节点下创建个临时顺序节点。由于Zk节点是按照创建的顺序依次递增的,为了确保公平,可以简单地规定,编号最小的那个节点表示获得了锁。因此,每个线程在尝试占用锁之前,首先判断自己的排号是不是当前最小的,如果是,则获取锁。
  3. ZooKeeper的节点监听机制可以保障占有锁的传递有序而且高效ZooKeeper内部优越的机制,能保证由于网络异常或者其他原因造成集群中占用锁的客户端失联时,锁能够被有效释放。一旦占用锁的ZNode客户端与ZooKeeper集群服务器失去联系,这个临时ZNode也将自动删除。排在它后面的那个节点也能收到删除事件,从而获得锁。所以,在创建取号节点的时候,尽量创建临时ZNode节点,
  4. ZooKeeper的节点监听机制能避免羊群效应ZooKeeper这种首尾相接,后面监听前面的方式,可以避免羊群效应。所谓羊群效应就是一个节点挂掉了,所有节点都去监听,然后作出反应,这样会给服务器带来巨大压力,所以有了临时顺序节点,当一个节点挂掉,只有它后面的那一个节点才作出反应。

使用Curator框架实现ZK分布式锁

详情见笔者写的SpringBoot基于Zookeeper实现分布式锁

Curator提供的InterProcessMutex是分布式锁的实现。acquire方法用户获取锁,release方法用于释放锁。

缺点

  1. 性能上: 性能上可能并没有缓存服务那么高。因为每次在创建锁和释放锁的过程中,都要动态创建、销毁瞬时节点来实现锁功能。ZK中创建和删除节点只能通过Leader服务器来执行,然后将数据同步到所有的Follower机器上。
  2. 并发问题: 使用Zookeeper也有可能带来并发问题,只是并不常见而已。考虑这样的情况,由于网络抖动,客户端可ZK集群的session连接断了,那么zk以为客户端挂了,就会删除临时节点,这时候其他客户端就可以获取到分布式锁了。就可能产生并发问题。这个问题不常见是因为zk有重试机制,一旦zk集群检测不到客户端的心跳,就会重试,Curator客户端支持多种重试策略。多次重试之后还不行的话才会删除临时节点。(所以,选择一个合适的重试策略也比较重要,要在锁的粒度和并发之间找一个平衡。)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝帽杯 取证2022

网站取证 网站取证_1 下载附件 并解压 得到了一个文件以及一个压缩包 解压压缩包 用火绒查病毒 发现后门 打开文件路径之后 发现了一句话木马 解出flag 网站取证_2 让找数据库链接的明文密码 打开www文件找找 查看数据库配置文件/application/database.php(CodeI…

Cuda和cuDNN安装

Cuda和cuDNN安装 1Cuda下载与安装1.1查看适合cuda的版本1.2下载cuda toolkit1.3cuda安装步骤1.4配置环境变量1.5验证 2cuDNN下载与安装2.1cuDNN下载2.2cuDNN配置2.3配置环境变量2.4验证 3安装PyTorch-GPU3.1打开Anaconda Prompt![在这里插入图片描述](https://img-blog.csdnimg…

ARM--day2(cpsr、spsr、数据搬移指令、移位操作指令、位运算操作指令、算数运算指令、比较指令、跳转指令)

.text .global _gcd _gcd:mov r0,#9mov r1,#15b loop loop:cmp r0,r1beq stopsubhi r0,r1bhi loopsubcc r1,r0bcc loopstop:b stop.end用for循环实现1~100之间和5050 .text .global _gcd _gcd:mov r0,#0x0mov r1,#0x1mov r2,#0x64b loop loop:cmp r1,r2bhi stopadd r0,r0,r1ad…

卷王特斯拉又全网降价了,卷死车企们

哈喽,大家好,今天媒介盒子小编又来跟大家分享软文推广的干货知识了,本篇分享的主要内容是:特斯无孔不入的营销手段。 1、特斯拉Model Y降价 车企要打架 自2023 年 8 月 14 日起,Model Y 长续航版起售价从 31.39 万元调整为 29.99 万元,Mode…

React+Typescript清理项目环境

上文 创建一个 ReactTypescript 项目 我们创建出了一个 React配合Ts开发的项目环境 那么 本文 我们先将环境清理感觉 方便后续开发 我们先来聊一下React的一个目录结构 跟我们之前开发的React项目还是有一些区别 public 主要是存放一些静态资源文件 例如 html 图片 icon之类的 …

走进 Linux

一、开关机 开机: 开机会启动许多程序。他们在windows叫做“服务”(service),在Linux就叫做“守护进程”(daemon)开机成功后,它会显示一个文本登录界面, 这个界面就是我们经常看到的登录界面,在这个登录界…

【有奖体验】COS插件体验,四重好礼等你拿!

对象存储 COS 为降低用户接入门槛,集成了多款 COS 插件,开放供用户使用,包含搭建网站、图床、论坛等多个热门业务场景的插件,让使用更便捷!对象存储 COS 准备了多重好礼,欢迎广大同学们踊跃体验 COS 插件&a…

U盘安装CentOS7系统出现dracut timeout的解决办法

文章目录 业务场景操作步骤U盘装CentOS7系统确定U盘盘符修改启动命令系统配置 总结 业务场景 我们在某市实施交通信控平台项目,我们申请了一台服务器,用于平台安装由于机房机器只有内网,不连互联网,我们无法安装所需要的软件&…

FreeRTOS(任务通知)

资料来源于硬件家园:资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、任务通知的概念 1、概念 2、发送通知给任务的方式 3、任务通知使用限制 二、任务通知的运行机制 三、任务通知的API函数 1、任务通知的数据结构 2、常用的API函数 3、函数x…

Java多线程编程中的线程死锁

Java多线程编程中的线程死锁 ​ 在多线程编程中,线程死锁是一种常见的问题,它发生在两个或多个线程互相等待对方释放资源的情况下,导致程序无法继续执行。本文将介绍线程死锁的概念、产生原因、示例以及如何预防和解决线程死锁问题。 线程死…

LeNet中文翻译

Gradient-Based Learning Applied to Document Recognition 基于梯度的学习应用于文档识别 摘要 使用反向传播算法训练的多层神经网络构成了成功的基于梯度的学习技术的最佳示例。给定适当的网络架构,基于梯度的学习算法可用于合成复杂的决策表面,该决策…

【C语言实战项目】通讯录

一.了解项目功能 在本次实战项目中我们的目标是实现一个通讯录: 该通讯录可以用来存储1000个人的信息 每个人的信息包括:姓名、年龄、性别、住址、电话 通讯录提供功能有: 添加联系人信息删除指定联系人信息查找指定联系人信息修改指定联系人信息显示所有…

miniExcel 生成excel

一、nuget dotnet add package MiniExcel --version 1.31.2 二、新建表及数据 ExampleProducts 三、这里我用了Dapper.Query方法 读取excel public virtual async Task<IActionResult> Anonymous(){try{//using (var connection _dbContext.GetDbConnection())//{//…

请教电路高手帮忙Review一下是否可行?

想要实现STM32 3.3V GPIO 控制5V电源通断&#xff0c;默认状态为&#xff1a;接通。 使用如下电路图有无问题&#xff1f;参数是否需要调整&#xff1f;

机器学习笔记之优化算法(十二)梯度下降法:凸函数VS强凸函数

机器学习笔记之优化算法——梯度下降法&#xff1a;凸函数VS强凸函数 引言凸函数&#xff1a;凸函数的定义与判定条件凸函数的一阶条件凸函数的梯度单调性凸函数的二阶条件 强凸函数强凸函数的定义强凸函数的判定条件强凸函数的一阶条件强凸函数的梯度单调性强突函数的二阶条件…

为博客获取阿里云免费HTTPS证书:简易指南

文章目录 前置条件&#xff1a;步骤1 例如阿里云控制台&#xff0c;选择SSL证书步骤2 申请购买免费证书步骤3 创建证书步骤3.1 证书申请步骤3.2 DNS域名验证 步骤4 等待证书审核成功&#xff0c;下载证书总结 本文分享&#xff0c;如何在阿里云申请免费HTTPS证书 前置条件&…

基于Spring Boot的高校在线考试系统的设计与实现(Java+spring boot+VUE+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的高校在线考试系统的设计与实现&#xff08;Javaspring bootVUEMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java s…

Netty:channel的事件顺序

服务端&#xff1a;正常启动的channel事件顺序 REGISTERED -> BIND -> ACTIVE 客户端&#xff1a;正常启动的channel事件顺序 REGISTERED -> CONNECT -> ACTIVE 服务端&#xff1a;接收到客户端连接&#xff0c;为客户端分配的channel的事件顺序 REGISTERED…

分布式唯一ID实战

目录 一、UUID二、数据库方式1、数据库生成之简单方式2、数据库生成 - 多台机器和设置步长&#xff0c;解决性能问题3、Leaf-segment 方案实现4、双 buffer 优化5、Leaf高可用容灾 三、基于Redis实现分布式ID四、雪花算法 一、UUID UUID的标准形式包含32个16进制数字&#xff…

【北大核心】改进花朵授粉算法的无线传感器网络部署优化(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…