【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁

这篇具有很好参考价值的文章主要介绍了【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文已收录于专栏
🍅《Redis从入门到进阶》🍅

专栏前言

   本专栏开启,目的在于帮助大家更好的掌握学习Redis,同时也是为了记录我自己学习Redis的过程,将会从基础的数据类型开始记录,直到一些更多的应用,如缓存击穿还有分布式锁等。希望大家有问题也可以一起沟通,欢迎一起学习,对于专栏内容有错还望您可以及时指点,非常感谢大家 🌹。

1.什么是分布式锁?

  锁这个东西,大家都知道,在我们 jvm 内部多个线程竞争同一个资源时,我们利用jvm提供的synchronized 或者一些其他的锁可以帮助我们让线程对资源的串行使用。但这种方法并不适合现在企业广泛使用的分布式架构。因为在这种集群模式下,jvm内部的锁无法被其他jvm内部感知到,那这样肯定无法满足我们的要求,因为锁肯定是要被大家都能感知到的,所以分布式锁应用而生。以前的锁竞争对象是线程之间,而分布式系统中竞争共享资源的单位从线程升级为了进程。

2. 分布式锁的条件

  那么作为一个分布式锁,它应该具备哪些条件呢?

  1. 可见性:多个进程之间均可以看见该锁,且可以尝试获取该锁
  2. 互斥性:锁最基本的特性,同一时间只能保证锁被一个进程持有
  3. 高可用性:也可以理解为容错性,当提供锁的服务结点产生故障时,程序不会因为守到强烈影响
  4. 高性能:锁的释放和添加本身十分消耗性能,我们应选择性能较好的锁

3.常见的分布式锁

  我们一般常见的分布式锁,有以下三种:

  1. MySQLMySQL自带锁机制,但由于其性能一般,所以作为分布式锁比较少见
  2. RedisRedis是分布式锁一种非常常见的实现方式,我们可以利用setnx这个方法,如果插入成功表示锁获取成功,否则获取失败。利用这个机制完成互斥,从而实现分布式锁,而且Redis存储在内存中本身就符合高性能特点。
  3. ZookeeperZookeeper也是企业开发中较好的一种实现分布式锁的方案,以后有机会讲解。

4.Redis 实现分布式锁

基于Redis实现分布式锁,我们使用两个方法:

  • 1.获取锁
    【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁
    该指令会插入一个结构为lock:thread01的锁,且超时时间为100秒,返回值为OK说明获取锁成功,失败则返回false,该方法不会进行阻塞。

  • 2.释放锁
    【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁
    通过手动删除该锁来进行释放,或者可以等待TTL让该锁自动过期

核心思路:
  利用RedisSETNX方法,当多个进程同时竞争该锁时,都会调用该方法获取锁,只有一个进程成功能成功获取成功,此时Redis将会生成该锁,其他进程获取失败。那么获取锁成功的进程将会去执行业务,最后删除该锁,这样就将锁释放了。如果想让获取锁失败的进程重新获取,可以手动休眠一段时间后重新获取。

下面是一个简单的使用StringRedisTemplate实现分布式锁的代码:

public class DistributedLock {
    
    private StringRedisTemplate redisTemplate;
    private String lockKey;
    private String lockValue;
    private long lockTimeout;
   	//构造方法
    public DistributedLock(StringRedisTemplate redisTemplate, String lockKey, String lockValue, long lockTimeout) {
        this.redisTemplate = redisTemplate;
        //key
        this.lockKey = lockKey;
        //value
        this.lockValue = lockValue;
        //过期时间
        this.lockTimeout = lockTimeout;
    }
  
    public boolean tryLock() {
        // 尝试获取锁
        Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, lockTimeout, TimeUnit.MILLISECONDS);
        return result != null && result;
    }
    
    public void unlock() {
        // 释放锁
        redisTemplate.delete(lockKey);
    }
}

5. 分布式锁误删问题

  上面的逻辑看上去完美无缺,但还是存在很严重的问题,考虑下面一个场景:
逻辑说明
  线程A持有锁执行业务的时候发生了堵塞,导致他的锁TTL到期自动释放了,此时线程B成功获取到锁了,因为线程A已经释放该锁了。这时候线程A阻塞完毕后继续执行完业务,然后删除该锁,线程B执行完业务时突然发现——woc 我锁呢? 它的锁被线程A误删了,这就是分布式锁误删问题。
解决方案
  上诉问题的产生主要原因,还是因为每个线程并不知道该锁是不是自己的,那我们可以在删除锁的时候去加以判断,如果该锁不属于自己,则不删除该锁。如果该锁是自己的且还未到期,再进行删除锁。我们这里标识一把锁的时候同时存入线程的ID,一般在同一个jvm中线程的标识一般不相同,但我们这是在集群模式下,所以也有可能出现ThreadID重复的情况,所以我们可以考虑在前面拼接上一个UUID

【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁
private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
@Override
public boolean tryLock(long timeoutSec) {
    // 获取线程标识
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    // 获取锁
    Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
    return Boolean.TRUE.equals(success);
}

@Override
public void unlock() {
    // 获取当前线程的标识
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    // 获取锁中的标识
    String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
    // 判断标识是否一致
    if (threadId.equals(id)) {
        // 释放锁
        stringRedisTemplate.delete(KEY_PREFIX + name);
    }
}

6. 分布式锁原子性问题

  在进行区分锁的处理后,那么是不是一定不会产生问题呢?
  考虑一种更加极端的情况,当线程 A判断完标识发现一致后,准备释放锁的时候又突然出现了阻塞情况(比如JVM垃圾挥手),锁又到期了,线程B进来拿了一把锁,因为线程A已经判断完标识,所以它一删锁又把B的锁给删掉了,这就又产生了误删的问题。
  解决的方案需要我们使用Lua脚本,来保证拿锁、判断标识、删锁三个操作是一个原子性操作,而Lua脚本可以同时执行多条Redis指令并且保证原子性,Lua脚本是一门脚本语言,有兴趣可以自行了解一下。
【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁文章来源地址https://www.toymoban.com/news/detail-425650.html

到了这里,关于【Redis从入门到进阶】第 7 讲:基于 Redis 实现分布式锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 自定义注解,基于redis实现分布式锁

    1.1、注解的基础知识 实现自定义注解其实很简单,格式基本都差不多。也就参数可能变一变。 @Retention:取值决定了注解在什么时候生效,一般都是取运行时,也就是RetentionPolicy.RUNTIME。 @Target:决定了这个注解可以使用在哪些地方,可以取方法,字段,类等。 注解这就定义

    2024年02月08日
    浏览(38)
  • Redis进阶:分布式锁问题

    单机单体中的锁机制在分布式集群系统中失效; 单纯的Java API并不能提供分布式锁的能力,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问; 1.2.1 分布式锁主流的实现方案 基于数据库实现分布式锁; 基于 缓存(Redis等),性能最高 ; 基于Zookeeper,可靠

    2024年02月08日
    浏览(35)
  • 基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性

    1.加锁的Lua脚本: lock.lua 2.解锁的Lua脚本: unLock.lua 3.将资源文件放在资源文件夹下 4.Java中调用lua脚本 1)获取文件方式 2)lua字符串方式 5.jedis调用Lua脚本实现分布式重试锁 1)引入jedis依赖 2)jedis调用lua

    2024年02月07日
    浏览(56)
  • 一种基于springboot、redis的分布式任务引擎的实现(一)

     总体思路是,主节点接收到任务请求,将根据任务情况拆分成多个任务块,将任务块标识的主键放入redis。发送redis消息,等待其他节点运行完毕,结束处理。接收到信息的节点注册本节点信息到redis、开启多线程、获取任务块、执行任务、结束处理。 1、主节点接收任务请求

    2024年02月11日
    浏览(40)
  • Zookeeper 和 Redis 哪种更好? 为什么使用分布式锁? 1. 利用 Redis 提供的 第二种,基于 ZK 实现分布式锁的落地方案 对于 redis 的分布式锁而言,它有以下缺点:

    关于这个问题,我们 可以从 3 个方面来说: 为什么使用分布式锁? 使用分布式锁的目的,是为了保证同一时间只有一个 JVM 进程可以对共享资源进行操作。 根据锁的用途可以细分为以下两类: 允许多个客户端操作共享资源,我们称为共享锁 这种锁的一般是对共享资源具有

    2024年01月16日
    浏览(49)
  • Redis实战案例14-分布式锁的基本原理、不同实现方法对比以及基于Redis进行实现思路

    基于数据库的分布式锁:这种方式使用数据库的特性来实现分布式锁。具体流程如下: 获取锁:当一个节点需要获得锁时,它尝试在数据库中插入一个特定的唯一键值(如唯一约束的主键),如果插入成功,则表示获得了锁。 释放锁:当节点完成任务后,通过删除该唯一键

    2024年02月13日
    浏览(52)
  • Asynq: 基于Redis实现的Go生态分布式任务队列和异步处理库

    Asynq [1] 是一个Go实现的分布式任务队列和异步处理库,基于redis,类似Ruby的 sidekiq [2] 和Python的 celery [3] 。Go生态类似的还有 machinery [4] 和goworker 同时提供一个WebUI asynqmon [5] ,可以源码形式安装或使用Docker image, 还可以和Prometheus集成 docker run --rm --name asynqmon -p 8080:8080 hibiken/as

    2024年02月14日
    浏览(43)
  • Redis从基础到进阶篇(四)----性能调优、分布式锁与缓存问题

    目录 一、Redis 集群演变 1.1 Replication+Sentinel*高可用 1.2 Proxy+Replication+Sentinel(仅仅了解) 1.3 Redis Cluster 集群 (重点) 1.3.1 Redis-cluster架构图 1.3.2 工作原理 1.3.3 主从切换 1.3.4 副本漂移 1.3.5 分片漂移 二、Redis版本历史(增加了解) 三、Redis 5.0 源码清单 (对源码感兴趣的,看一下

    2024年02月09日
    浏览(45)
  • 在Spring中,可以使用不同的方式来实现分布式锁,例如基于数据库、Redis、ZooKeeper等

    在Spring中,可以使用不同的方式来实现分布式锁,例如基于数据库、Redis、ZooKeeper等。下面是两种常见的实现方式: 使用Redis实现分布式锁: 使用自定义注解实现本地锁: 以上是两种常见的在Spring中实现分布式锁的方式。第一种方式使用Redis作为分布式锁的存储介质,通过

    2024年03月17日
    浏览(46)
  • 基于Redis的分布式限流详解

    Redis除了能用作缓存外,还有很多其他用途,比如分布式锁,分布式限流,分布式唯一主键等,本文将和大家分享下基于Redis分布式限流的各种实现方案。 用最简单的话来说: 外部请求是不可控的,而我们系统的负载是有限的,如果没有限流机制,一旦外部请求超过系统承载

    2024年02月04日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包