第七章高级篇分布式锁之redis6+lua脚本实现原生分布式锁(代码片段)

老吴IT代码笔记* 老吴IT代码笔记*     2022-12-01     531

关键词:

第1集 分布式核心技术-关于高并发下分布式锁你知道多少?

简介:分布式锁核心知识介绍和注意事项

  • 背景

    • 就是保证同一时间只有一个客户端可以对共享资源进行操作

    • 案例:优惠券领劵限制张数、商品库存超卖

    • 核心

      • 为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度
      • 利用互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题

       

  • 避免共享资源并发操作导致数据问题

    • 加锁

      • 本地锁:synchronize、lock等,锁在当前进程内,集群部署下依旧存在问题
      • 分布式锁:redis、zookeeper等实现,虽然还是锁,但是多个进程共用的锁标记,可以用Redis、Zookeeper、Mysql等都可以

image-20210410164720650

  • 设计分布式锁应该考虑的东西

    • 排他性

      • 在分布式应用集群中,同一个方法在同一时间只能被一台机器上的一个线程执行
    • 容错性

      • 分布式锁一定能得到释放,比如客户端奔溃或者网络中断
    • 满足可重入、高性能、高可用

    • 注意分布式锁的开销、锁粒度

 

 

 

 

 

 

 

 

 

 

 

第2集 基于Redis实现分布式锁的几种坑你是否踩过《上》

简介:基于Redis实现分布式锁的几种坑

  • 实现分布式锁 可以用 Redis、Zookeeper、Mysql数据库这几种 , 性能最好的是Redis且是最容易理解

    • 分布式锁离不开 key - value 设置
 
 
 
 
 
 
key 是锁的唯一标识,一般按业务来决定命名,比如想要给一种优惠券活动加锁,key 命名为 “coupon:id” 。value就可以使用固定值,比如设置成1
 

 

  • 基于redis实现分布式锁,文档:http://www.redis.cn/commands.html#string

    • 加锁 SETNX key value
     
     
     
     
     
     
    setnx 的含义就是 SET if Not Exists,有两个参数 setnx(key, value),该方法是原子性操作
    如果 key 不存在,则设置当前 key 成功,返回 1;
    如果当前 key 已经存在,则设置当前 key 失败,返回 0
     
    • 解锁 del (key)
     
     
     
     
     
     
    得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入,调用 del(key)
     
    • 配置锁超时 expire (key,30s)
     
     
     
     
     
     
    客户端奔溃或者网络中断,资源将会永远被锁住,即死锁,因此需要给key配置过期时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放
     
    • 综合伪代码
     
     
     
     
     
     
    methodA()
      String key = "coupon_66"
      if(setnx(key,1) == 1)
          expire(key,30,TimeUnit.MILLISECONDS)
          try 
              //做对应的业务逻辑
              //查询用户是否已经领券
              //如果没有则扣减库存
              //新增领劵记录
           finally 
              del(key)
          
      else
        //睡眠100毫秒,然后自旋调用本方法
        methodA()
      
     
    • 存在哪些问题,大家自行思考下

     

     

     

     

第3集 基于Redis实现分布式锁的几种坑你是否踩过《下》

简介:手把手教你彻底掌握分布式锁+原生代码编写

  • 存在什么问题?

    • 多个命令之间不是原子性操作,如setnxexpire之间,如果setnx成功,但是expire失败,且宕机了,则这个资源就是死锁
     
     
     
     
     
     
    使用原子命令:设置和配置过期时间  setnx / setex
    如: set key 1 ex 30 nx
    java里面 redisTemplate.opsForValue().setIfAbsent("seckill_1","success",30,TimeUnit.MILLISECONDS)
     

    image-20210410165806304

    • 业务超时,存在其他线程勿删,key 30秒过期,假如线程A执行很慢超过30秒,则key就被释放了,其他线程B就得到了锁,这个时候线程A执行完成,而B还没执行完成,结果就是线程A删除了线程B加的锁
     
     
     
     
     
     
    可以在 del 释放锁之前做一个判断,验证当前的锁是不是自己加的锁, 那 value 应该是存当前线程的标识或者uuid
    String key = "coupon_66"
    String value = Thread.currentThread().getId()
    if(setnx(key,value) == 1)
        expire(key,30,TimeUnit.MILLISECONDS)
        try 
            //做对应的业务逻辑
         finally 
          //删除锁,判断是否是当前线程加的
          if(get(key).equals(value))
              //还存在时间间隔
              del(key)
            
        
    else
    
    
      //睡眠100毫秒,然后自旋调用本方法
     
    • 进一步细化误删

      • 当线程A获取到正常值时,返回带代码中判断期间锁过期了,线程B刚好重新设置了新值,线程A那边有判断value是自己的标识,然后调用del方法,结果就是删除了新设置的线程B的值
      • 核心还是判断和删除命令 不是原子性操作导致
    • 总结

      • 加锁+配置过期时间:保证原子性操作
      • 解锁: 防止误删除、也要保证原子性操作

     

    • 那如何解决呢?下集讲解

 

 

 

 

 

 

 

 

 

 

第4集 手把手教你彻底掌握分布式锁lua脚本+redis原生代码编写

简介:手把手教你彻底掌握分布式锁+原生代码编写

  • 前面说了redis做分布式锁存在的问题

    • 核心是保证多个指令原子性,加锁使用setnx setex 可以保证原子性,那解锁使用 判断和删除怎么保证原子性
    • 文档:http://www.redis.cn/commands/set.html
    • 多个命令的原子性:采用 lua脚本+redis, 由于【判断和删除】是lua脚本执行,所以要么全成功,要么全失败
     
     
     
     
     
     
    //获取lock的值和传递的值一样,调用删除操作返回1,否则返回0
    String script = "if redis.call(\'get\',KEYS[1]) == ARGV[1] then return redis.call(\'del\',KEYS[1]) else return 0 end";
    //Arrays.asList(lockKey)是key列表,uuid是参数
    Integer result = redisTemplate.execute(new DefaultRedisScript<>(script, Integer.class), Arrays.asList(lockKey), uuid);
     
    • 全部代码
     
     
     
     
     
     
    /**
    * 原生分布式锁 开始
    * 1、原子加锁 设置过期时间,防止宕机死锁
    * 2、原子解锁:需要判断是不是自己的锁
    */
    @RestController
    @RequestMapping("/api/v1/coupon")
    public class CouponController 
        @Autowired
        private StringRedisTemplate redisTemplate;
        @GetMapping("add")
        public JsonData saveCoupon(@RequestParam(value = "coupon_id",required = true) int couponId)
            //防止其他线程误删
            String uuid = UUID.randomUUID().toString();
            String lockKey = "lock:coupon:"+couponId;
            lock(couponId,uuid,lockKey);
            return JsonData.buildSuccess();
        
        private void lock(int couponId,String uuid,String lockKey)
            //lua脚本
            String script = "if redis.call(\'get\',KEYS[1]) == ARGV[1] then return redis.call(\'del\',KEYS[1]) else return 0 end";
            Boolean nativeLock = redisTemplate.opsForValue().setIfAbsent(lockKey,uuid,Duration.ofSeconds(30));
            System.out.println(uuid+"加锁状态:"+nativeLock);
            if(nativeLock)
                //加锁成功
                try
                    //TODO 做相关业务逻辑
                    TimeUnit.SECONDS.sleep(10L);
                 catch (InterruptedException e) 
                 finally 
                    //解锁
                    Long result = redisTemplate.execute( new DefaultRedisScript<>(script,Long.class),Arrays.asList(lockKey),uuid);
                    System.out.println("解锁状态:"+result);
                
            else 
                //自旋操作
                try 
                    System.out.println("加锁失败,睡眠5秒 进行自旋");
                    TimeUnit.MILLISECONDS.sleep(5000);
                 catch (InterruptedException e)  
                //睡眠一会再尝试获取锁
                lock(couponId,uuid,lockKey);
            
        
     
    • 遗留一个问题,锁的过期时间,如何实现锁的自动续期 或者 避免业务执行时间过长,锁过期了?

      • 原生方式的话,一般把锁的过期时间设置久一点,比如10分钟时间

       

    • 原生代码+redis实现分布式锁使用比较复杂,且有些锁续期问题更难处理

 

基于redis实现分布式锁,分析解决锁误删情况及利用lua脚本解决原子性问题并改造锁(代码片段)

(目录)上一篇博文部分:优惠卷秒杀分布式锁1、基本原理和实现方式对比分布式锁:分布式锁的核心思想就是:分布式锁他应该满足一些什么样的条件呢?常见的分布式锁有三种:2、Redis分布式锁的实现核心思路实现分布式锁时... 查看详情

redis6----应用问题解决和新功能预览(代码片段)

...案缓存击穿问题描述解决方案缓存雪崩问题描述解决方案分布式锁问题描述解决方案java代码实现优化之设置锁的过期时间优化之UUID防误删优化之LUA脚本保证删除的原子性总结Redis6.0新功能ACLACL命令1、使用acllist命令展现用户权限... 查看详情

分布式锁之zk(zookeeper)实现(代码片段)

点赞再看,养成习惯,微信搜索【三太子敖丙】关注这个互联网苟且偷生的工具人。本文GitHubhttps://github.com/JavaFamily已收录,有一线大厂面试完整考点、资料以及我的系列文章。前言锁我想不需要我过多的去说,大家都知道是怎... 查看详情

使用redis+lua脚本实现分布式限流(代码片段)

...(enhance-boot-limiting)主要是基于Redis+lua实现了分布式限流功能项目中提供两种分布式限流算法(一种是滑动时间窗口算法、一种是令牌桶算法)项目中提供了方便使用的注解形式来直接对接口进行限流,详... 查看详情

使用redis+lua脚本实现分布式限流(代码片段)

...(enhance-boot-limiting)主要是基于Redis+lua实现了分布式限流功能项目中提供两种分布式限流算法(一种是滑动时间窗口算法、一种是令牌 查看详情

使用redistemplate+lua脚本实现redis分布式锁(代码片段)

分布式锁一般有三种实现方式:1.数据库乐观锁;2.基于Redis的分布式锁;3.基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四... 查看详情

redis6.0高级(代码片段)

...案缓存击穿问题描述解决方案缓存雪崩问题描述解决方案分布式锁问题描述:解决方案:使用redis实现分布式锁优化之设置锁的过期时间优化之UUID防误删分布式锁总结ÿ 查看详情

redis使用string+lua实现分布式锁

参考技术A注:原始资料来自享学课堂,加上自己的理解和修改redis中的数据是能够设置过期时间的,时间到了之后,当前key会被自动删除,具体命令如下setnxkeyvalue,当key已经存在时,不做任何操作,当key不存在时,设置key=value... 查看详情

redis中使用lua脚本

...的支持,Lua脚本的使用还是比较广泛的,比如商品秒杀、分布式锁等,使用Lua脚本可以带来以下的好处:为了让例子更加的贴近实际应用,这里实现一个简单版的分布式锁。这里先用Jedis操作。上边详细的介绍了分布式锁的实现... 查看详情

Redis 分布式锁混淆结果与 Lua 脚本

】Redis分布式锁混淆结果与Lua脚本【英文标题】:ARedisdistributedlockconfusingresultwithLuascript【发布时间】:2018-11-2006:06:09【问题描述】:我用这样的代码来实现Redis分布式锁:DefaultRedisScript<String>script=newDefaultRedisScript<>();scrip... 查看详情

jediscluster怎么使用lua脚本

...实现。本文做个总结,主要分享如下内容:【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】好了,一个一个来。一、Pipeline。 查看详情

(2)redis常见功能分析

...ission已提供支持,无需重复造轮子。(通过Lua脚本实现)分布式锁需要考虑哪些问题。Lua脚本是一种脚本语言,弱类型(灵活性较强)。在Redis中能够保证原子性。主要通过阻塞方式实现。(如果有一个客户端执行Lua脚本中,其... 查看详情

redis6_分布式存储极致性能目录

Juc24_AQS的概述、体系架构、深入源码解读(非公平)、源码总结鸟欲高飞先振翅,人求上进需读书!部分高级暂不公开GUAVA本地缓存_概述、优缺点、创建方式、回收机制、监听器、统计、异步锁定REDIS01_概述、安装、key、字... 查看详情

redis使用lua脚本进行原子操作(代码片段)

...原子操作Intro之前写过一篇文章也是Redis使用LUA脚本实现分布式的CAS操作,可以参考:基于Redis实现CAS操作最近使用Redis的时候有一个需求,只有值发生变化的时候才更新,如果要更新的值和现在的值是一样的就不... 查看详情

redis必会基础命令数据结构lua脚本和分布式锁等(代码片段)

...对应的命令、持久化配置和Lua脚本,以及基于redis的分布式锁实现方案,使用redis时这些都是必会的基础知识,建议保存以下命令通用命令#查看当前库中key的数量dbsize#清空当前库flushdb#清空所有库flushall#查看当前库下... 查看详情

2022年12月.netcore工具案例-csredis执行lua脚本实现商品秒杀(代码片段)

...程模拟多线程进行秒杀2.多线程进行秒杀前言下面是Redis分布式锁常用的概念说明:设置、获取、过期时间、删除。1、Setnx命令:SETNXkeyvalue说明:将key的值设为value,当且仅当key不存在。若给定的key已经存在,... 查看详情

lua从青铜到王者基础篇第七篇:lua数组和迭代器(代码片段)

系列文章目录文章目录系列文章目录前言🌲一、Lua数组🌲1.一维数组🌳2.多维数组🌳二、Lua迭代器🌲1.泛型for的迭代器🌳2.无状态的迭代器🌴3.多状态的迭代器💬🌲🌳🌴🌵总结... 查看详情

实现分布式锁的解决方案

目录1.分布式锁1.1什么是分布式锁1.2为什么要使用分布式锁1.3分布式锁应具有的特性2分布式锁实现方案2.1数据库实现分布式锁2.2ZooKeeper实现分布式锁2.3Redis实现分布式锁2.3.1版本一2.3.2版本二2.3.3版本三3.Redisson3.1Redisson介绍3.2Redisso... 查看详情