• 【Redis】Redis 的学习教程(十二)之在 Redis使用 lua 脚本


    lua 菜鸟教程:https://www.runoob.com/lua/lua-tutorial.html

    Redis 使用 lua 脚本的好处:

    1. 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延及开销
    2. 原子性操作。Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务
    3. 复用。客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑

    1. 常用命令

    • EVAL:将脚本 script 添加到脚本缓存中,并且立即执行这个脚本
      • 语法:EVAL script numkeys key [key …] arg [arg …]
      • 参数含义:
        • script:是 Lua5.1 脚本程序。此Lua脚本不需要也不应该定义函数,它运行在 Redis 服务器中
        • numkeys:键名参数的个数。即:key [key …] 中 key 的个数。如没有 key,则为 0
        • key[]:键名参数,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 lua 中通过全局变量 KEYS 数组。在 lua 脚本中通过 KEYS[1],KEYS[2] 获取
        • arg [arg …] :不是键名参数的附加参数,可以在 lua 中通过全局变量 ARGV 数组访问。在 lua 脚本中通过 ARGV[1],ARGV[2] 获取
      • 案例
        1. 调用 set 方法:EVAL "return redis.call('set', 'name', 'bob')" 0
        2. 调用 set 方法(使用参数):EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 name jack
    • EVALSHA:根据给定的 sha1 校验码,执行缓存在服务器中的脚本。将脚本缓存到服务器的操作可以通过 SCRIPT LOAD 命令进行。这个命令的其他地方,比如参数的传入方式,都和 EVAL 命令一样
      • 语法:EVALSHA sha1 numkeys key [key ...] arg [arg ...]
    • SCRIPT LOAD:将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。 在脚本被加入到缓存之后,通过 EVALSHA 命令,可以使用脚本的 SHA1 校验和来调用这个脚本。
      脚本可以在缓存中保留无限长的时间,直到执行 SCRIPT FLUSH 为止
      • 语法:SCRIPT LOAD script
      • 返回:脚本的 SHA1 校验和
    • SCRIPT EXISTS:校验指定的脚本是否已经被保存在缓存当中
      • 语法:SCRIPT EXISTS sha1 [sha1 ...]
    • SCRIPT FLUSH:清除 Redis 服务端所有 lua 脚本缓存
    • SCRIPT KILL用于杀死当前正在运行的 lua 脚本,当且仅当这个脚本没有执行过任何写操作时,这个命令才生效。这个命令主要用于终止运行时间过长的脚本,比如一个因为 BUG 而发生无限循环的脚本

    案例:

    redis 127.0.0.1:6379> SCRIPT LOAD "return 'hello moto'"    # 载入一个脚本
    "232fd51614574cf0867b83d384a5e898cfd24e5a"
    
    redis 127.0.0.1:6379> SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a
    1) (integer) 1
    
    redis 127.0.0.1:6379> SCRIPT FLUSH     # 清空缓存
    OK
    
    redis 127.0.0.1:6379> SCRIPT EXISTS 232fd51614574cf0867b83d384a5e898cfd24e5a
    1) (integer) 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2. 具体业务使用案例

    基于 Redis 的分布式锁

    释放锁的流程:

    1. 获取锁中的线程标识
    2. 判断是否与指定的标识(当前线程标识)一致
    3. 如果一致,则删除;否则,什么都不做

    unlock.lua 如下:resources/unlock.lua

    -- 比较线程标示与锁中的标示是否一致
    if(redis.call('get', KEYS[1]) ==  ARGV[1]) then
        -- 释放锁 del key
        return redis.call('del', KEYS[1])
    end
    return 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在 Java 中调用:

    // 初始化 lua 脚本文件
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        //lua脚本位置
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        //返回值类型
        UNLOCK_SCRIPT.setResultType(Long.class);
    }
    
    // 使用 lua 脚本释放锁
    public void unlock(String lockKey,String lockValue){
        // 调用lua脚本
        redisTemplate.execute(
                UNLOCK_SCRIPT,
                Collections.singletonList(lockKey),
                lockValue);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    QCC51XX---UI相关分析
    【C++】动态内存管理 ③ ( C++ 对象的动态创建和释放 | new 运算符 为类对象 分配内存 | delete 运算符 释放对象内存 )
    Python操作MongoDB数据库
    生态系统长期观测数据产品体系
    人人自媒体的时候,Ai绘画还值得踏入吗?
    Spring IOC之Condition 接口
    使用uniapp实现时钟功能
    基于C#利用S7.net库与西门子S7-1200PLC进行通信的具体方法(利用线程循环读取)
    【计算机网络】Tcp详解
    2023微信大数据挑战赛—参赛总结
  • 原文地址:https://blog.csdn.net/sco5282/article/details/133384106