首页 > 数据库 >Redis分布式锁应用

Redis分布式锁应用

时间:2022-12-06 15:12:06浏览次数:45  
标签:return 应用 Redis value String param key redisTemplate 分布式

Redis锁的使用

起因:分布式环境下需对并发进行逻辑一致性控制

架构:springboot2、Redis

IDEA实操

  1. 先新建RedisLock组件
    注:释放锁使用lua脚本保持原子性

    @Component
    @Slf4j
    public class RedisLock {
    
        private final RedisTemplate redisTemplate;
    
        public RedisLock(RedisTemplate redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        /**
         * 如果已经存在返回false,否则返回true
         *
         * @param key
         * @param value
         * @return
         */
        public Boolean setNx(String key, String value, Long expireTime, TimeUnit mimeUnit) {
    
            if (key == null || value == null) {
                return false;
            }
    
            // 在spiring boot 2 可以直接使用 redisTemplate的setIfAbsent设置key-value和过期时间,是原子性
            Boolean tf = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, mimeUnit);
    
            return tf;
    
    
        }
    
        /**
         * 获取数据
         *
         * @param key
         * @return
         */
        public Object get(String key) {
    
            if (key == null) {
                return null;
            }
            return redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 删除
         *
         * @param key
         * @return
         */
        public void remove(Object key) {
    
            if (key == null) {
                return;
            }
    
            redisTemplate.delete(key);
        }
    
        /**
         * 加锁
         *
         * @param key        key
         * @param waitTime   等待时间,在这个时间内会多次尝试获取锁,超过这个时间还没获得锁,就返回false
         * @param interval   间隔时间,每隔多长时间尝试一次获的锁
         * @param expireTime key的过期时间
         */
        public Boolean lock(String key, Long waitTime, Long interval, Long expireTime) {
    
            String value = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
    
            Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
    
            // 尝试获取锁 成功返回
            if (flag) {
                return true;
            } else {
                // 获取失败
    
                // 现在时间
                long newTime = System.currentTimeMillis();
    
                // 等待过期时间
                long loseTime = newTime + waitTime;
    
                // 不断尝试获取锁成功返回
                while (System.currentTimeMillis() < loseTime) {
    
                    Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
                    if (testFlag) {
                        return true;
                    }
    
                    try {
                        Thread.sleep(interval);
                    } catch (InterruptedException e) {
                        log.error("获取锁异常", e);
                    }
                }
            }
            return false;
        }
    
        /**
         * 释放锁
         *
         * @param key
         * @return
         */
        public void unLock(String key) {
            remove(key);
        }
    
        public Boolean setIfAbsent(String key, String value) {
            Boolean tf = redisTemplate.opsForValue().setIfAbsent(key, value);
            redisTemplate.expire(key, 60, TimeUnit.DAYS);
            return tf;
        }
    
    
        /**
         * 获取分布式锁
         * @param key
         * @param value
         * @param expireTime
         * @return
         */
        public Boolean lock(String key, String value, Long expireTime) {
            Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
            if (flag == null || !flag) {
                return false;
            }
    
            return true;
        }
    
        /***
         * lua释放分布式锁
         * @param key
         * @param value
         */
        public void unLock(String key, String value) {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
            Long result = (Long) redisTemplate.execute(redisScript, Arrays.asList(key),value);
            if (result == null || result == 0) {
                log.info("释放锁(" + key + "," + value + ")失败,该锁不存在或锁已经过期");
            } else {
                log.info("释放锁(" + key + "," + value + ")成功");
            }
        }
    
        /***
         * lua不存在才插入队列
         * @param key
         * @param values
         * jackson序列化后String会变成\"xxx\",不要直接用ARGV去转成number使用
         */
        public void putAllIfAbsent(String key, List<String> values,long timeout,TimeUnit unit) {
            Long rawTimeout = TimeoutUtils.toMillis(timeout, unit);
            String script = "if redis.call('exists', KEYS[1]) == 0 then local listValues = ARGV" +
                            " for k,v in ipairs(listValues) do " +
                            "       redis.call('RPUSH',KEYS[1],v) " +
                            " end " +
                            " redis.call('expire',KEYS[1],KEYS[2])" +
                            " end";
            RedisScript<Void> redisScript = new DefaultRedisScript<>(script);
            redisTemplate.execute(redisScript, Arrays.asList(key,String.valueOf(rawTimeout)),values.toArray());
        }
    }
    
    
    
    
  2. 业务使用
    进入逻辑前先判断有没有锁
    用try-catch包住业务逻辑,finally释放锁,以防抛错直接占有锁

    if (redisLock.lock(redisLockKey, value, 60 * 1000L)) {
                    try {
                        return reactiveMongoTemplate.find(query, JrRedPacketTask.class)
                                .collectList()
                                .flatMap(taskList -> {
                                    if (taskList.size() > 0) {
                                        //缓存到redis中
                                        return reactiveRedisTemplate.opsForValue().set(redisKey, taskList, Duration.ofDays(1))
                                                .then(Mono.just(taskList));
                                    }
                                    return Mono.just(taskList);
                                });
                    } finally {
                        redisLock.unLock(redisLockKey, value);// 释放锁
                    }
    
                } else {
                    try {
                        //休息,休息一会儿
                        Thread.sleep(8);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    

标签:return,应用,Redis,value,String,param,key,redisTemplate,分布式
From: https://www.cnblogs.com/checkcode/p/redisLock.html

相关文章

  • classList应用
    HTML:<divclass="box">1</div><divclass="box">2</div><divclass="box">3</div><divclass="box">4</div><divclass="box">5</div>JS:varbox=......
  • springboot2 搭建日志收集系统存入mongodb + redis+mq+线程池+xxljobs
    我们看到了高效批量插入mongodb速度还不错,那么我们的系统日志收集怎么做呢。当然当前文件日志收集效果也不错,比如前面博文写的elkf搭建日志收集系统。但我们系统中总是有......
  • 踩坑记录:Redis的lettuce连接池不生效
    踩坑记录:Redis的lettuce连接池不生效一、lettuce客户端lettuce客户端Lettuce和Jedis的都是连接RedisServer的客户端程序。Jedis在实现上是直连redisserver,多线程环......
  • Redis如何模糊匹配Key值
    Redis模糊匹配Key值使用Redis的scan代替Keys指令:publicSet<String>scan(StringmatchKey){Set<String>keys=(Set<String>)redisTemplate.execute((RedisC......
  • mysql两种索引结构应用场景
    B-tree索引和Hash索引区别B+tree:B+树是一个平衡的多叉树,从根节点到每个叶子节点的高度差值不超过1,而且同层级的节点间有指针相互链接。在B+树上的常规检索,从根节点......
  • Redis原理 - 对象的数据结构(SDS、Inset、Dict、ZipList、QuickList、SkipList、RedisO
    Redis数据结构1.SDSRedis是用C语言写的,但是对于Redis的字符串,却不是C语言中的字符串(即以空字符’\0’结尾的字符数组),它是自己构建了一种名为简单动态字符串(sim......
  • Redis
    Redis缓存一、关于缓存缓存的定义:缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码举个例子:越野车,山地自行车,都......
  • Redis 缓存击穿问题
    解决方案一:互斥锁假设一个热门产品的缓存时间到期了,那么将会有大量的请求查询不到缓存,就只能去查询数据库然后再把数据添加到缓存中。但是如果在缓存时间到期的瞬间有很多......
  • 电动机保护器在化工行业的应用
    安科瑞陈盼应用场景功能1、U、I、P、Q、S、PF、EP、EQ测量2、2-63次分次谐波测量3、缺相、过载、堵转、阻塞、三相不平衡保护4、10路DI输入、6路DO输出可编程5、支持晃......
  • GXT之旅:第五章:高级Components(2)——Grid的高级应用
    Grid的高级应用之前,我们学习的都是Grid的基本功能。事实上,Grids提供了丰富的功能,下面就让我们了解一下。HeaderGroupConfig假设我们想比较欧洲东部在1950和2000年之间的人......