• Redis之Lua脚本


    Redis之Lua脚本—

    Redission不仅提供了一套丰富的Redis客户端功能,还增加了很多高级功能,其中就包括分布式锁、发布和订阅、支持Lua脚本。Redission在底层利用Redis的SETNX命令实现分布式锁,并且处理了锁的续期问题,使用起来非常方便。

    然而,为什么在实际项目中,我们还需要使用Lua脚本呢?
    这主要是因为Redisson的分布式锁机制在某些特定场景下可能无法满足需求。

    1,复杂的原子操作:Redisson的分布式锁主要用于实现简单的原子操作,例如“获取锁,执行操作,释放锁”。但是,如果你需要执行更复杂的原子操作,例如“获取锁,执行操作1,执行操作2,释放锁”,这时就需要使用Lua脚本。
    .
    2,自定义的锁机制:在某些场景下,你可能需要自定义锁的机制,例如锁的公平性、锁的>时时间等。虽然Redisson提供了一些配置选项,但是如果你需要更细粒度的控制,那么使用Lua脚本可能是更好的选择。
    .
    3,跨多个Redis实例的操作:如果你需要跨多个Redis实例进行操作,例如在一个Redis实例上获取锁,在另一个Redis实例上执行操作,那么使用Lua脚本可能更方便,能够避免跨多个Redis实例操作时可能出现的竞态条件或数据不一致问题
    .
    4,避免网络开销:使用Lua脚本可以将多个操作合并为一个操作,从而减少网络开销。这在某些对性能要求较高的场景下非常有用。

    Lua脚本介绍

    Redis从2.6版本开始,通过内嵌一个Lua解释器,支持在服务器端执行Lua脚本。这个特性为Redis提供了非常大的灵活性。通过Lua脚本,我们可以实现复杂的逻辑,包括判断、循环等这在纯Redis命令中是很难实现的
    .

    Lua是一种轻量级的解释型脚本语言,直接在运行时执行Lua脚本,而无需预先编译。 Lua脚本的主要优点包括:
    简单:Lua的语法简洁直观,易于学习。
    高效:尽管Lua是解释型语言,但它的执行速度非常快,因为编译成字节码。
    安全:Lua提供了沙盒环境,允许在隔离的环境中运行代码,从而提高安全性。
    灵活:Lua可以很容易地与其他语言(如C、C++、Java等)集成,使得你可以在其他应用程序中嵌入Lua脚本,扩展应用程序的功能。

    Lua脚本为啥是高性能的?

    轻量级:Lua是一种轻量级脚本语言,解释器体积小、启动速度快,消耗的内存资源较少,使得Lua脚本在运行时具有较低的开销
    .
    动态类型:Lua是一种动态类型语言,它允许变量在运行时改变类型。这种灵活性避免了静态类型语言在类型转换和类型检查方面的开销,使得Lua脚本在执行时更加高效。
    .
    即时编译:Lua使用了一种混合的编译和解释策略,它将脚本代码编译成字节码,然后在运行时解释执行。这种即时编译的方式可以在一定程度上提高执行效率,因为编译后的字节码更加优化和紧凑。
    .
    垃圾回收:Lua内置了一个高效的垃圾回收机制,可以自动管理内存。这减少了开发人员手动管理内存的负担,避免了内存泄漏和内存碎片问题,使得Lua脚本在长时间运行时仍能保持稳定的性能。
    .
    协程支持:Lua内置了对协程(coroutine)的支持,这是一种轻量级的线程,可以在单个线程中并发执行多个任务。协程的使用可以减少线程切换和同步的开销,提高脚本的并发性能

    接下来,自定义自旋锁、通过Lua脚本实现分布式锁操作

    
    public class RedisLockHelper {
    
        private Logger logger = LoggerFactory.getLogger(RedisLockHelper.class);
    
        /**
         * 加锁超时时间:500毫秒
         */
        private static final long TIME_OUT = 500L;
    
        //定义获取锁的lua脚本
        private final static DefaultRedisScript<Long> LOCK_LUA_SCRIPT = new DefaultRedisScript<>(
                "if redis.call('SETNX', KEYS[1], ARGV[1]) == 1 then return redis.call('PEXPIRE', KEYS[1], ARGV[2]) else return 0 end"
                , Long.class
        );
    
        //定义释放锁的lua脚本
        private final static DefaultRedisScript<Long> RELEASE_LOCK_LUA_SCRIPT = new DefaultRedisScript<>(
                "if redis.call('GET',KEYS[1]) == ARGV[1] then return redis.call('DEL',KEYS[1]) else return -1 end"
                , Long.class
        );
    
        @Lazy
        @Autowired
        private RedisTemplate<String, String> redisTemplate;
        
        /**
         * 自旋锁
         * @param lockKey
         * @param lockValues
         * @param expire
         * @return
         */
        private Boolean spinLock(String lockKey, String lockValues, int expire){
            long startTime = System.currentTimeMillis();
    
            while (true){
                boolean lockRes = tryLock(lockKey, lockValues, expire);
                if(lockRes){
                    return Boolean.TRUE;
                }
                //加锁消耗时长
                long consumeTime = System.currentTimeMillis() - startTime;
                if(consumeTime >= TIME_OUT){
                    logger.warn("获取锁超时:锁key值:{}", lockKey);
                    return Boolean.FALSE;
                }
    
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        public boolean tryLock(String lockKey, String value, int expire) {
            // 参数一:redisScript,参数二:key列表
            Long result = redisTemplate.execute(LOCK_LUA_SCRIPT, Collections.singletonList(lockKey), value, String.valueOf(expire));
            if(result != null && result == 1L){
                if(logger.isDebugEnabled()){
                    logger.info("[加锁成功]result:{}, key:[{}], value:[{}] ", result, lockKey, value);
                }
                return true;
            }else {
                logger.warn("[加锁失败]result:{}, key:[{}], value:[{}] ", result, lockKey, value);
                return false;
            }
        }
    
        public void releaseLock(String lockKey, String value) {
            Long result = redisTemplate.execute(RELEASE_LOCK_LUA_SCRIPT, Collections.singletonList(lockKey), value);
            if(result != null && result == 1L){
                if(logger.isDebugEnabled()){
                    logger.info("[锁释放环节]:锁释放成功!result:{}, key:[{}], value:[{}] ", result, lockKey, value);
                }
            }else {
                logger.warn("[锁释放环节]:查询不到锁!result:{}, key:[{}], value:[{}] ", result, lockKey, value);
            }
        }
    
        public static String getLockValues() {
            return UUID.randomUUID().toString().replace("-", "");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
  • 相关阅读:
    asp.net健身会所管理系统sqlserver
    mysql 忘记 root 密码的解决办法(针对不同 mysql 版本)
    探索 Electron:窗口菜单以及生命周期和对话框讲解
    【面试普通人VS高手系列】ConcurrentHashMap 底层具体实现知道吗?实现原理是什么?
    SpringBoot中post请求报405错误排坑
    【软考学习6】计算机存储结构——局部性原理、Cache、主存地址单元、磁盘存取、总线和可靠性
    Java基础
    奋进新时代 和数SaaS开启下一个波澜壮阔科技新世界
    《Docker 简易速速上手小册》第10章 朝着 Docker Swarm 和 Kubernetes 迈进(2024 最新版)
    Python+超市进销存 毕业设计-附源码211549
  • 原文地址:https://blog.csdn.net/qq_45399396/article/details/133208207