redis的搭建及应用(六)-redis应用LUA脚本

edis的lua脚本

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于 1993 年开发的,该小组成员有:Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo

lua脚本设计目的

其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

lua脚本在redis中的应用

从 Redis 2.6.0 版本开始起;可通过内置的 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行执行。

在redis中使用lua脚本的好处:

  • 支持原子性操作 - Redis会将整个脚本作为一个整体执行(原子性),中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。

  • 降低网络开销 - 将多个请求通过脚本的形式一次发送到服务器,减少了网络的时延。

  • 脚本复用 - 客户端发送的脚本可支持永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。

lua基本语法

lua变量
局部变量

local a = 5

全局变量

不需要local关键字。

lua 复制代码
a = 12                  -- 全局变量
local b=3
条件语句

if 条件

:

then

end

if 条件

then

else

end

KEYS数组

传递给Lua脚本零到多个键,KEYS[index],注意索引从1开始。

ARGV数组

是传递给脚本的零到多个附加参数, ARGV[index],注意索引从1开始。

redis.call()

用来调用redis命令。

Hello Lua脚本案例

脚本编辑

在resource路径下创建lua/hello.lua文件

传入的redis key为数组,使用KEYS[index]传参,

传入的redis value为数组,使用ARGV[index]传值。

lua 复制代码
-- key
local k1 = KEYS[1]
local k2 = KEYS[2]
local k3 = KEYS[3]
-- value
local v1 = ARGV[1]
redis.call('set',k1,v1)
redis.call('set',k2,v1)
redis.call('set',k3,v1)
return "OK"
springboot配置

定义一个读取lua脚本的配置类。

java 复制代码
@Bean
public DefaultRedisScript<String> defaultRedisScript(){
    DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
    defaultRedisScript.setResultType(String.class);
    defaultRedisScript.setLocation(new ClassPathResource("lua/hello.lua"));
    return defaultRedisScript;
}
单元测试
java 复制代码
@Test
public void luaTest(){}
List<String> keys = new ArrayList<>();
        keys.add("money01");
        keys.add("money02");
        keys.add("money03");
        String val1 = "100";
        String val2 = "150";
        String val3 = "200";
        String execute = stringRedisTemplate.opsForValue()
                .getOperations()
                .execute(defaultRedisScript,
                        keys,
                        val1,val2,val3);
        System.out.println("lua调用: "+ execute);
    }
}

springboot中Lua脚本的创建流程

定义脚本文件方式
创建lua文件

resources/lua/xxx.lua

编辑lua脚本
  • local 本地变量 类似于js var

  • KEYS数组, 从外界传入的key,下标 从1 开始

  • ARGV 数组, 外界命令中传输的值

  • 条件 :

    lua 复制代码
    if   条件   then
     ...
    else
     ....
    end
  • tonumber():将字符串转换为数值

  • return

定义script对象读取lua脚本
java 复制代码
@Bean
public DefaultRedisScript<String> defaultRedisScript(){
    DefaultRedisScript<String> defaultRedisScript = new DefaultRedisScript<>();
    defaultRedisScript.setResultType(String.class);
    defaultRedisScript.setLocation(new ClassPathResource("lua/hello.lua"));
    return defaultRedisScript;
}
执行脚本
java 复制代码
stringRedisTemplate.opForValue()
                   .getOpertation()
                   .execute(
                      defaultRedisScript,
                      keys             //ArrayList
                      val1,val2...      //值
                   );
代码方式实现

将脚本文件的内容转换为字符串,如果lua脚本比较小,在不同程序中lua内容比较灵活,聚合性比较高。可以采用此种方式。

java 复制代码
String script = "local k1 = KEYS[1]\n" +
            "local k2 = KEYS[2]\n" +
            "local k3 = KEYS[3]\n" +
            "local v1 = ARGV[1]\n" +
            "local v2 = ARGV[2]\n" +
            "local v3 = ARGV[3]\n" +
            "\n" +
            "redis.call('set',k1,v1)\n" +
            "redis.call('set',k2,v2)\n" +
            "redis.call('set',k3,v3)\n" +
            "\n" +
            "return \"OK\"";
String execute = stringRedisTemplate.opsForValue()
        .getOperations()
        .execute(
               new DefaultRedisScript<>(script,String.class),
                new ArrayList<String>(){{
                    add("m1");
                    add("m2");
                    add("m3");
                }},
                10, 20, 30);
System.out.println("lua调用: "+ execute);
    String execute = stringRedisTemplate.opsForValue()
            .getOperations()
            .execute(
                    new DefaultRedisScript<>("local k1 = KEYS[1] local k2 = KEYS[2] local k3 = KEYS[3] local v1 = ARGV[1] local v2 = ARGV[2] local v3 = ARGV[3] redis.call('set',k1,v1) redis.call('set',k2,v2) redis.call('set',k3,v3) return \"OK\"", String.class),
                    new ArrayList<String>() {{
                        add("m1");
                        add("m2");
                        add("m3");
                    }},
                    "100", "20", "30");
    System.out.println("lua调用: " + execute);

lua脚本创建布隆过滤器

shell 复制代码
127.0.0.1:6379> BF.ADD newFilter bmFilter
(integer) 1
127.0.0.1:6379> 

编写lua脚本

在resource下创建lua脚本

lua 复制代码
local key = KEYS[1]
local val = ARGV[1]

local retVal = tonumber(redis.call('BF.ADD',key,val))

if retVal==1 then
    return 1
else
    return 0
end

测试

java 复制代码
@Test
public void testLua() {
    List<String> keys = new ArrayList<>();
    keys.add("bmFilter");

    Long t = (Long) stringRedisTemplate.opsForValue().getOperations()
            .execute(redisScript, keys, "admin888");
    System.out.println("--->"+t);

}

代码中加入脚本

java 复制代码
List<String> keys = new ArrayList<>();
keys.add("bmFilter");
String script ="return redis.call('BF.EXISTS',KEYS[1],ARGV[1])";
Long s = stringRedisTemplate.opsForValue().getOperations()
        .execute(new DefaultRedisScript<Long>(script,Long.class), keys,username);

log.debug("s--->", s);

下一章我们研究-redis的限流插件redis-cell

相关推荐
见山是山-见水是水34 分钟前
鸿蒙flutter第三方库适配 - 汇率换算器
redis·flutter·华为·harmonyos
014-code42 分钟前
Redis 删除缓存失败怎么办?重试、死信、补偿的工程化方案
数据库·redis·缓存
rannn_1111 小时前
【Redis|高级篇1】分布式缓存|持久化(RDB、AOF)、主从集群、哨兵、分片集群
java·redis·分布式·后端·缓存
PD我是你的真爱粉1 小时前
Redis 持久化、过期删除、淘汰策略与内存碎片全解析
java·redis·bootstrap
斌味代码1 小时前
Redis 分库分表实战:从垂直拆分到水平扩容完整记录
数据库·redis·bootstrap
rchmin1 小时前
阿里Tair分布式锁与Redis分布式锁的实现区别
数据库·redis·分布式
VT LI1 小时前
Lua 源码执行流程全解析:词法分析、语法分析、字节码生成、虚拟机执行与垃圾回收
java·开发语言·lua
win x11 小时前
Redis 使用~如何在Java中连接使用redis
java·数据库·redis
程序员萌萌15 小时前
Redis的缓存机制和淘汰策略详解
数据库·redis·缓存机制·淘汰策略
历程里程碑18 小时前
二叉树---二叉树的中序遍历
java·大数据·开发语言·elasticsearch·链表·搜索引擎·lua