分布式知识整理

分布式锁

以商场系统超卖现象举例

 超卖现象一

现象:

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

产生原因:

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

代码复现

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等,提供了分布式事务的解决方案,可以简化分布式事务的开发和管理。这些中间件通常提供了一致性协议、分布式事务管理和补偿机制等功能。

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

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

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

相关文章

2024年2月16日优雅草蜻蜓API大数据服务中心v1.1.1大更新-UI全新大改版采用最新设计ui·增加心率计算器·退休储蓄计算·贷款还款计算器等数接口

2024年2月16日优雅草蜻蜓API大数据服务中心v1.1.1大更新-UI全新大改版采用最新设计ui增加心率计算器退休储蓄计算贷款还款计算器等数接口 更新日志 前言:本次更新中途跨越了很多个版本,其次本次ui大改版-同步实时发布教程《带9.7k预算的实战项目layuiph…

SpringBoot3整合Swagger3,访问出现404错误问题(未解决)

秉承着能用就用新的的理念,在JDK、SpringBoot、SpringCloud版本的兼容性下,选择了Java17、SpringBoot3.0.2整合Swagger3。 代码编译一切正常,Swagger的Bean也能加载,到了最后访问前端页面swagger-ui的时候出现404。 根据网上资料…

jmeter 压测数据库

当前版本: jmeter 5.6.3mysql 5.7.39 简介 JMeter 是一个开源的 Java 应用程序,主要用于进行性能测试和负载测试。它支持多种协议,包括但不限于 HTTP、HTTPS、FTP、JDBC 以及各种 Web Services。对于数据库的压力测试可以使用 JDBC 协议与数…

字符函数和字符串函数(C语言进阶)(二)

目录 前言 1.4 strcmp 1.5 strncpy strncat strncmp 1.6 strstr 前言 C语言中对字符和字符串的处理是很频繁的,但是c语言本身是没有字符串类型的,字符串通常放在常量字符串中或着字符数组中。 字符串常量适用于那些对它不做修改的字符串函数。 1…

matlab新能源汽车三自由度操纵稳定性分析及优化

1、内容简介 略 可以交流、咨询、答疑 55-新能源汽车三自由度操纵稳定性分析及优化 2、内容说明 略 摘 要 电动化是节能减排、寻求替代能源的最佳途径,已成为行业共识,论文基于江西科技学院桑塔纳轿车油改气项目,在拆除发动机、变速…

低功耗设计——门控时钟

1. 前言 芯片功耗组成中,有高达40%甚至更多是由时钟树消耗掉的。这个结果的原因也很直观,因为这些时钟树在系统中具有最高的切换频率,而且有很多时钟buffer,而且为了最小化时钟延时,它们通常具有很高的驱动强度。此外&…

ChatGPT调教指南 | 咒语指南 | Prompts提示词教程(三)

在人工智能成为我们日常互动中无处不在的一部分的时代,与大型语言模型(llm)有效沟通的能力是无价的。“良好提示的26条原则”为优化与这些复杂系统的交互提供了全面的指导。本指南证明了人类和人工智能之间的微妙关系,强调清晰、专一和结构化的沟通方法。…

matlab绘制雷达图和二维FFT变换图

1、内容简介 略 49-可以交流、咨询、答疑 matlab绘制雷达图和二维FFT变换图 NMO组及NORMAL组 RNFL层、GCL层、IPL层、GCC层、ORL层做雷达图(共10张) 2、内容说明 略 NMO组及NORMAL组 RNFL层、GCL层、IPL层、GCC层、ORL层请分别做雷达图&#xff08…

YOLOv8改进 | Conv篇 | 全新的SOATA轻量化下采样操作ADown(参数量下降百分之二十,附手撕结构图)

一、本文介绍 本文给大家带来的改进机制是利用2024/02/21号最新发布的YOLOv9其中提出的ADown模块来改进我们的Conv模块,其中YOLOv9针对于这个模块并没有介绍,只是在其项目文件中用到了,我将其整理出来用于我们的YOLOv8的项目,经过实验我发现该卷积模块(作为下采样模块)…

QT DAY4 事件

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);speecher new QTextToSpeech(this); }Widget::~Widget() {delete ui; }//显示系统时间 void Widget::timerEve…

栈和堆什么意思,Rust所有权机制又是什么

栈和堆什么意思 栈:存储基本数据类型和引用数据类型的指针引用(地址),基本数据类型占据固定大小的内存空间。 堆:存储引用数据类型的值,引用数据类型包括对象,数组和函数,在堆中,引用数据类型…

8k中英双语文本嵌入模型效果初探

一 模型介绍 向量模型用于生成向量表示,被广泛应用于检索、分类、聚类或语义匹配等传统的自然语言处理任务。到了大模型时代,由于上下文长度的限制,需要压缩、存储和查询大量的信息,这就需要用到向量模型对输入的文本进行向量化表…

HTB-Bizness

一、信息收集 访问ip自动跳转域名,host绑定域名后访问 目录爆破 有一个登录目录,访问发现是apahce ofbiz登录页面 发现存在漏洞 二、漏洞利用 在github上找到了图形化利用工具 使用工具反弹shell 得到flag 三、权限提升 从本地利用python开启http服务…

项目解决方案:街道社区视频监控接入、汇聚和联网设计方案

目 录 一、客户需求 二、网络拓扑图 三、方案描述 四、系统配置 1、服务器配置 2、带宽配置 五、方案优势 1. 平台可堆叠使用 2. 支持主流接入协议 4. 多种终端显示 5. 客户端功能强大 6. 一机一档 一、客户需求 1,一个街道有十个社…

迷你世界寻找火山脚本附带雪山

print("寻找火山雪山\n星空露珠工作室制作") local blockbing120 --冰山 local blockhuo124 -- 火山 local bing{} local huo{} local m,n0,0 local ra0 local run0 local function o(e) if run-1 then return end run-1 local objide.eventobjid--点击方块的玩家 lo…

【python】0、超详细介绍:json、http

文章目录 一、json二、http2.1 json 读取 request 序列化 三、基本类型3.1 decimal 四、图像4.1 颜色格式转换 一、json import json f open(data.json) # open json file data json.load(f) # 读出 json object for i in data[emp_details]: # 取出一级属性 emp_details, …

二蛋赠书十六期:《高效使用Redis:一书学透数据存储与高可用集群》

很多人都遇到过这么一道面试题:Redis是单线程还是多线程?这个问题既简单又复杂。说他简单是因为大多数人都知道Redis是单线程,说复杂是因为这个答案其实并不准确。 难道Redis不是单线程?我们启动一个Redis实例,验证一…

Spring定时任务--手动执行定时任务(替代@Scheduled)

原文网址:Spring定时任务--手动执行定时任务(替代Scheduled) 简介 本文介绍SpringBoot如何手动执行定时任务。 之前此文已经介绍过,直接用Scheduled即可使用Spring的定时任务,但有时需要手动去提交定时任务&#xf…

解决MySQL安装server时报被什么需要问题

bug: 执行这个四个命令的时候,最后一个server报错,显示被需要 rpm -ivh mysql-community-common-5.7.35-1.el7.x86_64.rpm rpm -ivh mysql-community-libs-5.7.35-1.el7.x86_64.rpm rpm -ivh mysql-community-client-5.7.35-1.el7.x86_64.rpm rpm -ivh mysql-comm…

深入理解Java中的Reader类:一步步剖析

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好…