首页 > 其他分享 >分布式锁介绍

分布式锁介绍

时间:2024-08-27 22:23:26浏览次数:9  
标签:String 实现 Redis 介绍 线程 分布式 客户端

为什么使用分布式锁?

在单一机器的环境中,当多个线程可以同时修改某个共享变量时,可能会产生线程安全性问题。这些问题可以通过 Java 提供的 volatileReentrantLocksynchronized 以及 concurrent 并发包中的线程安全类等机制来解决。

然而,在分布式系统中,当需要跨不同机器的多个进程确保线程安全性时,这些机制就不再适用,因为它们只能保证在一个 JVM 实例内的多线程访问共享资源时的线程安全性。在这种情况下,就需要使用分布式锁来确保整个集群中同一方法或资源在同一时间只能被一个进程执行。

分布式锁应具备的条件

在探讨分布式锁的实现方式之前,我们需要了解分布式锁应当具备的条件:

  1. 互斥性:在任何时刻,只有一个客户端能够持有锁。
  2. 无死锁:具备锁失效机制,即使持有锁的客户端崩溃未主动释放锁,也要确保其他客户端可以获取锁。
  3. 不可误解锁:加锁和解锁必须是同一个客户端,客户端 A 不能解锁客户端 B 持有的锁。
  4. 高性能和高可用:能够高效地获取和释放锁。
  5. 可重入性:支持同一个客户端多次获取锁。
  6. 非阻塞性:未获取到锁时直接返回失败,而非阻塞等待。

分布式锁的实现方式

分布式锁可以通过以下三种方式实现:

  1. 基于数据库实现
  2. 基于缓存(如 Redis)实现
  3. 基于 Zookeeper 实现
基于数据库的实现方式
  • 悲观锁:创建一张锁表,通过操作该表中的数据来实现加锁和解锁。要锁住某个方法或资源时,就向该表插入一条记录,表中设置方法名为唯一键,这样多个请求同时提交数据库时,只有一个操作可以成功,成功操作的线程获得锁;释放锁时删除这条记录。

  • 乐观锁:每次更新操作都认为不会发生并发冲突,只有在更新失败时才会重试。例如,减少余额的操作可以使用此方案。具体实现是在表中增加一个版本号字段,每次更新时该版本号自增,更新余额时带上旧版本号作为条件,如果版本号匹配则更新,否则表示有其他并发操作已发生,需要重试。

基于 Redis 的实现方式
简单实现

Redis 2.6.12 及以后的版本中,可以使用 SETNX 命令加上过期时间来实现分布式锁:

SET lockKey value NX PX expire-time

加锁逻辑:

  1. 使用 SETNX 尝试获取锁,如果已有锁存在,则稍后再重试,以确保只有一个客户端能持有锁。
  2. 锁值设置为请求 ID(可以是 IP 地址加上线程名称),以便在解锁时验证请求者。
  3. 使用 EXPIRE 给锁设置一个过期时间,以防异常导致无法释放锁。

解锁逻辑:

  1. 获取锁对应的值,检查是否与请求 ID 相匹配,若匹配则删除锁。
  2. 使用 Lua 脚本进行原子操作以确保线程安全。

示例代码

public class RedisTest {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_EXPIRE_TIME = "PX";

    @Autowired
    private JedisPool jedisPool;
    
    public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
        Jedis jedis = jedisPool.getResource();
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    public boolean releaseDistributedLock(String lockKey, String requestId) {
        Jedis jedis = jedisPool.getResource();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if ("1".equals(result.toString())) {
            return true;
        }
        return false;
    }
}
RedLock

上述方案适用于单机 Redis 的分布式锁,但在 Redis 集群环境中,需要考虑主从切换的问题。为了解决这个问题,Redis 作者提出了更高级的分布式锁算法——RedLock。

RedLock 的核心思想是部署多个相互独立的 Redis Master 节点,并在这些节点上使用相同的加锁和解锁方法。客户端需要在大多数节点上成功获取锁,并且获取锁的时间要小于锁的有效期,才能认为获取锁成功。

基于 Zookeeper 的实现方式

ZooKeeper 是一个为分布式应用提供一致性服务的开源组件。基于 ZooKeeper 实现分布式锁的基本步骤包括:

  1. 创建一个目录(例如 mylock)。
  2. 当客户端想要获取锁时,在该目录下创建一个临时顺序节点。
  3. 客户端获取该目录下的所有子节点,并确定自身是否是最小编号的节点,若是,则获得锁。
  4. 若未获得锁,则监听前一个节点的变化。
  5. 当持有锁的客户端完成操作并删除其节点时,下一个节点获得锁。

三种实现方式的比较

数据库分布式锁实现

  • 优点:简单易用,无需引入额外中间件。
  • 缺点:不适合高并发场景,数据库操作性能较低。

Redis 分布式锁实现

  • 优点:性能好,适用于高并发场景,有较好的框架支持。
  • 缺点:过期时间难以精确控制,需考虑锁被其他线程误删的情况。

Zookeeper 分布式锁实现

  • 优点:具备良好的性能和可靠性,有成熟的框架支持。
  • 缺点:相对于 Redis 实现来说性能略低,实现较为复杂。

总结

  • 从性能角度来看:Redis > Zookeeper > 数据库。
  • 从实现难度来看:数据库 > Redis > Zookeeper。
  • 从可靠性和成熟度来看:Zookeeper > Redis > 数据库。

标签:String,实现,Redis,介绍,线程,分布式,客户端
From: https://blog.csdn.net/weixin_45049746/article/details/141612709

相关文章

  • 自我介绍+软工5问
    这个作业属于哪个课程计科22级34班这个作业要求在哪里自我介绍+软工5问这个作业的目标准备GitHub和博客园账号,发表一篇包含自我介绍、问题提出和期望收获的随笔,同时,学习GitHub和Git的使用。自我介绍我是计科3班的陈国威,来自广东惠州。性格上较为开朗,并没有什......
  • Java元注解介绍
    Java四种元注解相关介绍概述注解从Java1.5引入以来,不断地简化我们编写代码的流程,逐渐的也成为了我们必学的一项技术。我们学习了各种注解,学习了他们的用法,学习了他们的限制,是否想过他们的组成呢,下面我将我对元注解的理解分享给大家。元注解是用来修饰注解的注解,在java.lang.ann......
  • RocketMQ在基金大厂的分布式事务实践
    1行业背景基金公司核心业务主要分为:投研线业务,即投资管理和行业研究业务,体现基金公司核心竞争力市场线业务,即基金公司利用自身渠道和市场能力完成基金销售并做好客户服务随互联网技术发展,基金销售渠道更加多元化,线上成为基金销售重要渠道。相比传统基金客户,线上渠道具有客......
  • 自我介绍+软工5问
    这个作业属于哪个课程 2024软工这个作业要求在哪里 第一次作业要求这个作业的目标 熟悉markdown,github,博客园自我介绍我是来自广州本地的学生。爱好运动,打游戏五个问题如何保证开发人员在工作中能真正获得提升?如何提升开发人员的效率?开发人员是否应该广涉略还是专精一个领......
  • Git组件介绍
    写在前面今天我们来学习一下Git。Git是一个分布式版本控制系统,用于跟踪文件的更改和管理多个版本的代码。#Git使用指南下载与安装下载GitforWindowsv2.46.0安装选择好路径,直接点击“下一步”完成安装即可。基本设置设置名称gitconfig--globaluser.name"Your......
  • 自我介绍&软工5问
    这个作业属于哪个课程首页-计科22级12班-广东工业大学-班级博客-博客园(cnblogs.com)这个作业要求在哪里自我介绍+软工5问-作业-计科22级12班-班级博客-博客园(cnblogs.com)这个作业的目标熟悉博客的创作自我介绍我是广东工业大学22级计算机......
  • 自我介绍+软工5问
    这个作业属于哪个课程计科22级12班这个作业要求在哪里作业这个作业的目标创建博客园和github并使用markdown发布随笔自我介绍我叫陈煜,是22级计科一班的学生。在大一大二参与过兴趣社团,也报名许多课外拓展活动,掌握了一定编程知识,通过了四六级。平日里我爱好......
  • 自我介绍+软工5问
    这个作业属于哪个课程[计科22级34班]——https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里[自我介绍+软工5问]——https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13228这个作业的目标介绍自己,学会使用Markdown排版进行随笔的编......
  • 自我介绍 + 软工 5 问
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13228这个作业的目标学习使用博客园和GitHub以及学习使用Markdown排版进行随笔的编写自我介绍我是......
  • 自我介绍+软工5问
    这个作业属于哪个课程https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13228这个作业的目标<初步检验对于新事物的学习能力,初步了解关于博客园的相关学习操作,以及对于github相关内......