分布式知识整理

分布式锁

以商场系统超卖现象举例

 超卖现象一

现象:

商品卖出数量超出了库存数量。

产生原因:

扣减库存的动作在程序中进行,在程序中计算剩余库存,在并发场景下,导致库存计算错误。

代码复现

es.shutdown();

cyclicBarrier  作用是确保多个线程同时扣减库存。

countDownLatch  确保执行完之前,线程池不关闭

解决方法

注意,库存做个大于0的判断

超卖现象二

现象

系统中库存变为负数
 

产生原因

1  并发检验库存,造成库存充足的假象

2  update更新库存,导致库存为负数

1  检索商品库存,如果商品库存为负数,抛出异常,则更新操作回滚

2 加锁

单体加锁

synchronized 关键字

当使用 synchronized 关键字时,可以分别应用于实例方法、静态方法和代码块,以确保线程安全。以下是分别对这三种情况的加锁示例:

1. 实例方法加锁示例:

public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}
}

在上面的示例中,increment 方法使用 synchronized 关键字修饰,因此在每次调用该方法时,会对当前对象进行加锁,确保同一时刻只有一个线程可以执行 increment 方法。

2. 静态方法加锁示例:

public class SynchronizedExample {private static int count = 0;public static synchronized void increment() {count++;}
}

在这个示例中,increment 方法被声明为静态方法并使用了 synchronized 关键字修饰。这会导致对类的 Class 对象进行加锁,确保同一时刻只有一个线程可以执行这个静态方法。

3. 代码块加锁示例:

public class SynchronizedExample {private static final Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}
}

在这个示例中,使用了一个私有的静态对象 lock 作为锁,并在 increment 方法中使用 synchronized 块来对一段代码进行加锁,确保同一时刻只有一个线程可以执行这段代码块。

以上这些示例展示了 synchronized 关键字在不同场景下的应用,可以确保线程安全,并避免出现并发访问问题。

ReentrantLock

当需要更灵活地控制加锁和解锁的时候,ReentrantLock 是一种好的选择。下面是一个使用 ReentrantLock 加锁的示例:

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}
}

在上面的示例中,首先创建了一个 ReentrantLock 对象,然后在 increment 方法中使用 lock() 来获取锁,在操作完成后使用 unlock() 来释放锁。这样可以确保在加锁的过程中发生异常时,锁仍然能够被正确释放,避免出现死锁的情况。

ReentrantLock 还提供了更多的灵活性,如可以实现公平锁和非公平锁、可以中断等待锁的线程等功能,更适用于复杂的并发控制场景。

单体加锁的局限性

上述示例中单体加锁的两种方式(synchronized 关键字和 ReentrantLock)虽然可以在某些情况下提供线程安全性,但也存在一些局限性,这些局限性包括:

1. 互斥性:单体加锁是一种悲观锁机制,当一个线程持有锁执行临界区代码时,其他线程必须等待释放锁才能执行。这可能导致性能问题,特别是在并发度较高的情况下。

2. 潜在的死锁风险:如果在加锁的过程中发生异常或逻辑错误,可能导致锁没有被正确释放,从而引发死锁问题。

3. 缺乏灵活性:synchronized 关键字和 ReentrantLock 的加锁范围仅限于方法或代码块内部,这可能限制了并发控制的粒度和灵活性。有些复杂的并发控制场景可能需要更细粒度的加锁或释放锁的控制,此时单体加锁机制可能无法满足要求。

5. 无法跨节点同步:单体加锁是基于进程内的锁机制,无法在分布式环境中实现跨节点的同步。在多个节点之间无法保证互斥性,并且无法实现全局的锁机制。

6. 性能瓶颈:在分布式环境中,如果使用单体加锁来保护共享资源,会导致性能瓶颈。由于加锁的范围扩大到整个服务实例或整个代码块,会造成并发度的降低,影响整体的吞吐量和性能。

7. 高开销和复杂性:由于需要维护全局一致的状态,单体加锁需要进行大量的网络通信和同步操作,增加了额外的开销和复杂性。

为了解决这些问题,分布式环境中常用的并发控制机制是分布式锁。分布式锁是一种基于分布式协议和算法实现的锁机制,可以在分布式环境中保证互斥性和一致性。常见的分布式锁实现包括基于数据库的锁、基于缓存的锁(如 Redis 锁),以及基于 ZooKeeper 等分布式协调服务的锁。

使用分布式锁可以解决分布式环境中跨节点同步和锁竞争的问题,提高性能和可伸缩性。但是需要注意,分布式锁也可能引入其他问题,如死锁、性能瓶颈等。因此,在设计和使用分布式锁时需要谨慎考虑,并综合考虑系统的一致性、性能和可伸缩性需求。

常见分布式锁

当在分布式系统中实现分布式锁时,可以使用不同的技术作为后端存储和同步机制,常见的包括 Redis、数据库和 ZooKeeper。下面我们将分别讨论它们作为分布式锁的原理、优缺点等。

当在分布式系统中实现分布式锁时,可以使用不同的技术作为后端存储和同步机制,常见的包括 Redis、数据库和 ZooKeeper。下面我们将分别讨论它们作为分布式锁的原理、优缺点等。

 Redis 分布式锁

原理:


- 使用 Redis 作为后端存储,基于 Redis 的 SETNX(set if not exists)命令来实现分布式锁。当某个客户端成功获取锁时,会在 Redis 中创建一个对应的锁键,并设置合适的超时时间,其他客户端获取不到锁。

优点:


- **高性能:** Redis 是内存数据库,读写性能出色,适合作为分布式锁的后端存储。


- **支持阻塞和非阻塞式获取锁:** Redis 提供了支持阻塞以及非阻塞式的锁获取方式(通过 BLPOP 或者 SETNX 结合 EXPIRE 命令)。


- **支持可重入:** 可以通过为每个锁键加上唯一标识,实现可重入性。

缺点:


- **单点故障:** Redis 单节点故障时可能导致锁不可用,需要通过 Redis Sentinel 或 Redis Cluster 等方式解决高可用性问题。
- **并发数受限:** Redis 作为单机或者主从部署模式下,可能受限于单机的并发连接数。

数据库分布式锁

原理:


- 使用关系型数据库的乐观锁或者悲观锁来实现分布式锁,可以利用数据库的事务和唯一索引来保证原子性和唯一性。

优点:


- **成熟稳定:** 数据库作为企业级数据存储系统,具有成熟的高可用、一致性和持久性解决方案。
- **适用性广:** 已有的数据库系统可以直接用于存储锁信息,无需引入新的中间件。

缺点:


- **性能:** 与 Redis 相比,数据库的读写性能通常较差,可能影响锁的获取和释放性能。
- **数据库连接资源消耗:** 大量并发获取锁可能导致数据库连接池耗尽。
- **死锁风险:** 数据库锁对于跨事务并发控制必须慎重处理,避免出现死锁。

当使用 ZooKeeper 作为分布式系统中的分布式锁时,通常会利用 ZooKeeper 的顺序节点(Sequential ZNode)和 Watches 机制来实现分布式锁。下面是详细的介绍以及和 Redis 的对比:

ZooKeeper

工作原理

1. 创建锁节点: 客户端在尝试获取锁时,在 ZooKeeper 上创建一个临时的、有序的顺序节点,代表自己的锁请求。

2. 获取锁: 客户端检查自己创建的节点是否是当前序列中最小的节点,如果是,则表示获得了锁;如果不是,则注册前一个节点的 Watcher 监听,然后进入等待状态。

3. 释放锁:*当客户端不再需要锁时,直接删除自己创建的节点,其他等待的客户端会通过监听到节点删除事件来尝试获取锁。

优点

- 高可用性:*ZooKeeper 提供了高可用的分布式协调服务,适合作为分布式锁的后端存储,具有良好的稳定性和一致性。
- 顺序性:*ZooKeeper 的顺序节点可以使客户端获取锁的顺序有序,并且通过 Watches 机制实现高效的事件通知,避免了轮询导致的资源浪费。

缺点

- 复杂性:*使用 ZooKeeper 需要额外的运维成本和学习成本,部署和维护相对复杂。
- 性能: ZooKeeper 的性能可能不如 Redis 那样出色,尤其在高并发场景下可能受到影响。

 ZooKeeper 和 Redis 分布式锁的对比

- 一致性和可靠性:ZooKeeper 提供了较高的一致性和可靠性,适合作为分布式锁的后端存储;而 Redis 虽然性能出色,但在一致性和可靠性方面略逊一筹。
- 部署复杂性:ZooKeeper 的部署和维护相对复杂,需要专门的运维人员进行管理;而 Redis 相对来说更加简单和容易上手。
- 性能: Redis 作为内存数据库,读写性能通常更优,适用于对性能要求较高的场景;ZooKeeper 在高并发场景下可能性能较为有限,需要权衡选择。

最终选择 ZooKeeper 还是 Redis 作为分布式锁的后端存储,需要根据具体的业务需求、系统的性能要求以及运维成本等方面进行综合考虑。

curator分布式锁和redisson分布式锁

Curator是Netflix开源的一组Apache ZooKeeper客户端库的高级API。Curator 提供了一套易用

基于分布式锁解决定时任务的重复问题

当使用 Redis 实现分布式锁来解决定时任务的重复问题时,可以通过以下步骤来实现:

1. 首先,实现一个定时任务,例如每隔一定时间执行某个任务。

2. 在任务执行之前,客户端尝试获取 Redis 中特定的锁,比如使用 SETNX 命令来设置一个特定的键。如果返回成功,即获取到了锁,就可以执行任务;否则,任务不应该被执行。

3. 在任务执行完毕后,释放锁,通过 DEL 命令来删除锁对应的键。

这是一个基本的使用 Redis 实现分布式锁解决定时任务重复问题的例子。然而,在实现过程中可能会遇到一些问题,如下所示:

- 锁竞争问题:*在高并发的情况下,多个客户端可能同时尝试获取同一个锁,从而导致竞争。只有一个客户端能够成功获取到锁,其他客户端则不能执行任务。

- 锁超时问题:如果设置的锁超时时间过长,那么在某个客户端执行任务期间,该客户端意外崩溃或失去连接,没有及时释放锁,就会导致其他客户端无法获取锁,进而无法执行任务。

为了解决这些问题,可以采取以下方案:

- 设置锁的有效期限制: 在获取锁的时候,使用 SETNX 命令设置一个过期时间,避免某个客户端崩溃或失去连接而不能释放锁。可以使用 PEXPIRE 命令来设置锁的过期时间。

- 添加唯一标识符:当某个客户端成功获取到锁时,在锁的键名中添加一个唯一标识符,使得只有拥有相应标识符的客户端才能释放该锁。这样可以避免其他客户端误释放锁。(当两个客户端在几乎相同的时间内向 Redis 请求锁时,根据 Redis 的单线程执行特性,Redis 会依次处理这两个客户端的请求。由于执行速度非常快,Redis 无法辨别两个请求的先后顺序,因此有可能会出现两个客户端同时获得锁的情况。)

- 使用 Redlock 或 RediLock 算法:Redlock 或 RediLock 是一种分布式锁算法,在 Redis 集群环境中使用多个 Redis 实例来实现分布式锁,提供更高的可靠性和鲁棒性。

红锁(Redlock)是一种分布式锁算法,被设计用于在Redis集群环境中实现分布式锁。它旨在提供更高的可靠性和鲁棒性,尤其针对网络分区和故障恢复的情况。

红锁算法的原理如下:

1. 客户端根据一致性哈希算法将锁映射到多个Redis实例上,形成一个由n个实例组成的锁集群。

2. 客户端尝试在每个Redis实例上获取锁。通过使用`SETNX`命令来设置一个特定键和值,如果返回成功则客户端获取到了锁。

3. 客户端记录获取锁成功的实例数量,并计算时间戳。

4. 客户端检查是否超过了超时时间(通常是锁的有效期的一半),以及是否获得了大部分实例锁的数量。如果是,则客户端认为成功获取到了锁。

5. 如果客户端无法获取到过半数的实例锁,或者超时了,则客户端尝试释放已经获取的锁。

红锁算法的优点是考虑了分布式环境可能出现的网络分区和故障恢复问题。通过使用多个Redis实例进行锁的获取和释放,能够提供更高的可靠性和鲁棒性,同时保证只有一个客户端能够获得锁。

针对定时任务的重复问题,可以使用红锁算法结合定时任务的处理方式来解决。具体实现步骤如下:

1. 在每个定时任务执行前,客户端使用红锁算法获取一个分布式锁。

2. 获取锁成功后,执行定时任务。

3. 定时任务执行完毕后,客户端释放分布式锁。

这样可以确保在分布式环境中,同一时间只有一个客户端能够获取到锁,从而避免了定时任务的重复执行问题。同时,红锁算法的可靠性和鲁棒性能够应对网络分区和故障恢复的情况,提供更强大的分布式锁功能。

需要注意的是,使用 Redis 实现分布式锁时,仍然需要保证 Redis 的高可用性和故障恢复能力,可以通过 Redis Sentinel 或 Redis Cluster 来实现。同时,还需要谨慎处理 Redis 连接池的资源管理,以避免资源耗尽的问题。

常见面试题

如何解决分布式事务

在解决分布式事务问题时,可以采用以下几种常见的解决思路:

1. 强一致性分布式事务(Two-Phase Commit,2PC):2PC 是一种经典的解决方案,基于协调者和参与者之间的协议来保证所有参与者要么全部提交,要么全部回滚。协调者负责协调参与者的状态,并决定是否进行提交。然而,2PC 存在单点故障问题,并且在分布式系统规模较大时性能和可扩展性会受到限制。

2. 最终一致性分布式事务(Saga Pattern):Saga Pattern 将一个大事务拆分为多个小事务,并通过补偿操作来保证最终一致性。每个小事务可以独立执行,并且在失败时可以执行相应的补偿操作。Saga Pattern 对于长时间运行的分布式事务和异步消息的处理非常有用。但需要注意,Saga Pattern 的设计和实现可能会增加系统的复杂性。

3. 基于消息的分布式事务(Transactional Messaging):在分布式系统中,可以将事务性操作与消息传递相结合。通过消息队列来确保事务的一致性。当一个服务完成其本地事务后,会向消息队列发送消息,其他服务在接收到消息后执行相应的操作。这种方法可以实现比较灵活的事务处理,但需要考虑消息队列的可靠性和幂等性。

4. 分布式事务中间件:一些开源和商业的分布式事务中间件,如Seata、TCC-Transaction、Hmily等,提供了分布式事务的解决方案,可以简化分布式事务的开发和管理。这些中间件通常提供了一致性协议、分布式事务管理和补偿机制等功能。

需要根据具体的业务场景、系统规模和性能要求来选择合适的分布式事务解决方案。同时,需注意分布式事务的设计和实现可能会增加系统的复杂性,并需要充分考虑事务一致性、性能、可靠性和开发复杂度等因素之间的权衡。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧应急与物联网相结合:物联网技术如何提升智慧应急响应能力

目录 一、引言 二、智慧应急与物联网技术的结合 三、物联网技术提升智慧应急响应能力的途径 四、物联网技术在智慧应急中的应用案例 五、物联网技术在智慧应急中面临的挑战与解决方案 挑战一:技术标准与规范不统一 解决方案: 挑战二:…

Linux之JAVA环境配置jdkTomcatMySQL

目录 一. 安装jdk 1.1 查询是否有jdk 1.2 解压 1.3 配置环境变量 二. 安装Tomcat(开机自启动) 2.1 解压 2.2 启动tomcat 2.3 防火墙设置 2.4 创建启动脚本(设置自启动,服务器开启即启动) 三. MySQL安装(…

国漫年番成趋势?但只有这5部最值得看

自从《斗罗大陆》动画爆火之后,越来越多国漫都开始以年番形式播出,每周都能追自己喜欢的动画也是观众们所期待的。但其实年番对制作公司的要求很高,如果技术跟不上难免出现质量下滑的问题。今天就带大家盘点一下目前在播的最值得看的5部国漫年…

查看navicat保存的数据库连接密码

背景 经常使用navicat的朋友可能会碰到忘记数据库连接密码的情况,自然会想到navicat连接配置中就保存了密码。 个人经验,按以下步骤可查看密码明文 本人在mac上使用的navicat版本 1,导出connection_local.ncx 点击OK导出保存为connection_l…

基于频率增强的数据增广的视觉语言导航方法(VLN论文阅读)

基于频率增强的数据增广的视觉语言导航方法(VLN论文阅读) 摘要 视觉和语言导航(VLN)是一项具有挑战性的任务,它需要代理基于自然语言指令在复杂的环境中导航。 在视觉语言导航任务中,之前的研究主要是在空间…

Android RecyclerView 如何展示自定义列表 Kotlin

Android RecyclerView 如何展示自定义列表 Kotlin 一、前提 有这么一个对象 class DeviceDemo (val name: String, val type: String, val address: String)要展示一个包含这个对象的列表 bluetoothDevices.add(DeviceDemo("bb 9800", "LE", "32:…

Linux 内存管理概述(偏实战,略理论,附链接)

基础理论 1. 内存映射 可以参考: Linux内存映射 - 知乎 写的很详细,而且也有代码分析 2. 虚拟内存的空间分布 通过这张图你可以看到,用户空间内存,从低到高分别是五种不同的内存段。只读段,包括代码和常量等。数据段…

Unity学习之Unity中的MVC思想

文章目录 1 前言2 MVC的基本概念3 不使用MVC思想制作UI逻辑3.1 拼面板3.2 面板脚本3.3 角色面板逻辑3.4 角色升级 4 使用MVC思想制作UI逻辑4.1 Model数据脚本4.2 View界面脚本4.2.1 MainView主界面4.2.2 RoleView 角色面板界面 4.3 Controller业务逻辑脚本4.3.1 MainController…

使用openai-whisper实现语音转文字

使用openai-whisper实现语音转文字 1 安装依赖 1.1 Windows下安装ffmpeg FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。 # ffmpeg官网 https://ffm…

2024年2月20日v1.0.5更新·优雅草便民工具youyacao-tools

2024年2月20日v1.0.5更新优雅草便民工具youyacao-tools apk下载 https://fenfacun.youyacao.com/tools105.apk 介绍 优雅草便民工具是一款由成都市一颗优雅草科技有限公司打造的便民查询公益工具,2024年1月17日正式发布v1.0.0版本,本工具为了方便大众免…

互联网加竞赛 机器视觉opencv答题卡识别系统

0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 答题卡识别系统 - opencv python 图像识别 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🥇学长这里给一个题目综合评分(每项满分5分…

力扣● 343. 整数拆分 ● 96.不同的二叉搜索树

● 343. 整数拆分 想不到,要勇于看题解。 关键在于理解递推公式。 1、DP数组及其下标的含义:dp[i]是分解i这个数得到的最大的乘积。 2、DP数组如何初始化:dp[0]和dp[1]都没意义,所以直接不赋值,初始化dp[2]1即可。…

golang通过http访问外部网址

不同项目之前,通过http访问,进行数据沟通 先设定一个接口,确认外部能访问到 PHP写一个接口 public function ceshi_return() {$data $this->request->param();$id $data[id];$res Db::name(user)->field(id,status,price,name)->where([id>$id])->find…

【大数据】Flink SQL 语法篇(四):Group 聚合

Flink SQL 语法篇(四):Group 聚合 1.基础概念2.窗口聚合和 Group 聚合3.SQL 语义4.Group 聚合支持 Grouping sets、Rollup、Cube 1.基础概念 Group 聚合定义(支持 Batch / Streaming 任务):Flink 也支持 G…

深度神经网络中的计算和内存带宽

深度神经网络中的计算和内存带宽 文章目录 深度神经网络中的计算和内存带宽来源原理介绍分析1:线性层分析2:卷积层分析3:循环层总结 来源 相关知识来源于这里。 原理介绍 Memory bandwidth and data re-use in deep neural network computat…

S32 Design Studio PE工具配置UART

配置操作 先得配置下GPIO用的是哪个引脚 跟之前的外设一样,它这里有两个UART可以用。 这里只能用UART0和2的原因是UART1被LIN使用了。 配置的内容为 生成代码对应 首先会根据components名称创建个驱动状态结构体 /*! Driver state structure */ lpuart_state_t …

程序媛的mac修炼手册-- 2024如何彻底卸载Python

啊,前段时间因为想尝试chatgpt的API,需要先创建一个python虚拟环境来安装OpenAI Python library. 结果,不出意外的出意外了,安装好OpenAI Python library后,因为身份认证问题,根本就没有获取API key的权限…

stable diffusion学习笔记 手部修复

图片手部修复原理 某张图片在生成后,仅有手部表现不符合预期(多指,畸形等)。这种情况下我们通常使用【局部重绘】的方式对该图片的手部进行【图生图】操作,重新绘制手部区域。 但是仅采用重绘的方式也很难保证生成的…

试卷打印如何去除答案?3个方法一键还原

试卷打印如何去除答案?在日常学习中,在打印试卷时,去除答案是一个常见的需求,特别是在学生复习或老师准备教学材料时。答案的存在可能会干扰学生的学习过程,或者使得试卷的重复使用变得困难。为了解决这个问题&#xf…

JWT学习笔记

了解 JWT Token 释义及使用 | Authing 文档 JSON Web Token Introduction - jwt.io JSON Web Token (JWT,RFC 7519 (opens new window)),是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准((RFC 7519)。该 token 被设计为紧凑…