Redis调用Lua脚本

Posted by LeoSpring on September 28, 2018

1、调用Lua脚本的语法:

$ redis-cli –eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] …

–eval,告诉redis-cli读取并运行后面的lua脚本 path/to/redis.lua,是lua脚本的位置 KEYS[1] KEYS[2],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取 ARGV[1] ARGV[2],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。

注意: KEYS和ARGV中间的 ‘,’ 两边的空格,不能省略。

redis支持大部分Lua标准库

库名 说明
Base 提供一些基础函数
String 提供用于字符串操作的函数
Table 提供用于表操作的函数
Math 提供数学计算函数
Debug 提供用于调试的函数

2、在脚本中调用redis命令

 在脚本中可以使用redis.call函数调用Redis命令

redis.call(‘set’, ‘foo’, ‘bar’) local value=redis.call(‘get’, ‘foo’) –value的值为bar

redis.call函数的返回值就是Redis命令的执行结果

Redis命令的返回值有5种类型,redis.call函数会将这5种类型的回复转换成对应的Lua的数据类型,具体的对应规则如下(空结果比较特殊,其对应Lua的false)

redis返回值类型和Lua数据类型转换规则

redis返回值类型 Lua数据类型
整数回复 数字类型
字符串回复 字符串类型
多行字符串回复 table类型(数组形式)
状态回复 table类型(只有一个ok字段存储状态信息)
错误回复 table类型(只有一个err字段存储错误信息)

redis还提供了redis.pcall函数,功能与redis.call相同,唯一的区别是当命令执行出错时,redis.pcall会记录错误并继续执行,而redis.call会直接返回错误,不会继续执行。在脚本中可以使用return语句将值返回给客户端,如果没有执行return语句则默认返回nil

Lua数据类型和redis返回值类型转换规则

Lua数据类型 redis返回值类型
数字类型 整数回复(Lua的数字类型会被自动转换成整数)
字符串类型 字符串回复
table类型(数组形式) 多行字符串回复
table类型(只有一个ok字段存储状态信息) 状态回复
table类型(只有一个err字段存储错误信息) 错误回复

3、脚本相关命令

EVAL语法: eval script numkeys key [key …] arg [arg …] 通过key和arg这两类参数向脚本传递数据,它们的值在脚本中分别使用KEYS和ARGV两个表类型的全局变量访问。 script: 是lua脚本 numkeys:表示有几个key,分别是KEYS[1],KEYS[2]…,如果有值,从第numkeys+1个开始就是参数值,ARGV[1],ARGV[2]… 注意: EVAL命令依据参数numkeys来将其后面的所有参数分别存入脚本中KEYS和ARGV两个table类型的全局变量。当脚本不需要任何参数时,也不能省略这个参数(设为0)

Java中使用Redis调用lua

public Integer isAccessed(String tenantId, String appCode, Long offset,Integer totalCount) throws BusinessException {
        String key = Constants.SYSCODE + "_" + tenantId + "_" + appCode;

        //lua脚本
        String script = "local accessed = redis.call('getbit',KEYS[1],ARGV[1]);" + //判断是否已经访问过
                        " local totalCount = redis.call('bitcount',KEYS[1]);" +
                        "if (accessed==1) then " +
                        "   return 0; " + //已经访问过 直接放过
                        "elseif(tonumber(ARGV[2]) > 0 and totalCount >= tonumber(ARGV[2])) then " +
                                " return 1;" + //人数超量
                        "  else " +
                                " redis.call('setbit',KEYS[1],ARGV[1],1);" + //人数未超,添加到缓存中
                                " return 2;" +
                        " end";
        List<String> keys = Lists.newArrayList(key);
        List<String> args = Lists.newArrayList(offset.toString(),totalCount.toString());
        return jedisTemplate.getShard(key).execute(new JedisTemplate.JedisAction<Integer>() {
            public Integer action(Jedis jedis) {
                return Integer.parseInt(jedis.eval(script,keys, args).toString());
            }
        });

    }