redis中使用lua脚本处理业务逻辑

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。redis中使用lua脚本处理业务逻辑,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

lua脚本语言小巧轻便,运行性能优异,被很多语言作为嵌入式脚本语言使用。redis对lua脚本语言有很好的支持,对于开发者,如果要处理的数据涉及到只是简单的计算逻辑,就不必将redis中的数据拿到应用程序中执行了,直接写一段lua脚本程序就可以在redis服务端完成逻辑处理。
有关lua语言的语法内容,这里就不做过多的介绍,这里只是介绍在redis中如何运行lua脚本。

  1. 命令行运行脚本

在redis命令行运行lua脚本非常简单,只需要定义好lua代码的字符串,在redis客户端使用 eval 命令运行脚本,命令格式如下:

127.0.0.1:6379> eval script numkeys key [key ...] arg [arg ...]

script :是要执行的lua脚本字符串
numkeys :key的数量,如果没有键这个数就设置为0
key :key的具体值,key的数量要与前面定义的数量保持一致
arg :参数列表

下面演示一下如何使用上面的命令:

127.0.0.1:6379> eval "return 'hello,world!';" 0 
"hello,world!"
127.0.0.1:6379> 
127.0.0.1:6379> eval "return redis.call('set', KEYS[1], ARGV[1]);" 1 lua_key lua_val
OK
127.0.0.1:6379> 
127.0.0.1:6379> eval "return redis.call('get', KEYS[1]);" 1 lua_key
"lua_val"
127.0.0.1:6379> 
127.0.0.1:6379> eval "if redis.call('exists', KEYS[1]) > 0 then return redis.call('get', KEYS[1]); else return ''; end;" 1 kkkk 
""
127.0.0.1:6379> 
127.0.0.1:6379> eval "if redis.call('exists', KEYS[1]) > 0 then return redis.call('get', KEYS[1]); else return ''; end;" 1 lua_key 
"lua_val"
127.0.0.1:6379> 

在lua脚本中调用redis命令使用 redis.call() 方法,方法的第一个参数是命令的名称,后面跟参数,所有的键放入KEYS中,值放入ARGV中,在脚本中参数下标从1开始。
除了上面的调用方式,我们也可以将lua脚本写入到系统文件中,然后通过命令行调用本地文件执行:
我们在系统新建一个lua脚本文件,命名为lua_script.lua,内容如下:

local exist = redis.call('exists', KEYS[1]);
if exist > 0 then
  local rs = redis.call('incrby', KEYS[1], ARGV[1]);
  return '1_' .. rs;   // 这里是lua中的字符串拼接方法:str1 .. str2 会将两个字符串拼接成一个字符串
else 
  local rs = redis.call('set', KEYS[1], ARGV[2]);
  if rs then
    local rs = redis.call('incrby', KEYS[1], ARGV[1]);
    return '2_' .. rs;
  else
    return nil;
  end;
end;

脚本的执行流程是:先判断键是否存在,如果存在直接执行incrby指令进行加数操作;如果不存在,先初始化键的值,然后再执行incrby指令进行加数操作。

lua脚本文件执行调用命令是:

redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3

–eval 表示要执行lua脚本文件,后面跟着的就是脚本文件路径,可以是相对路径也可以是绝对路径;接下来就是所有的键和值,键和值之间使用”,”分隔,多个键和多个值之间使用空格分隔。
例如,要调用上面写好的脚本文件使用下面的命令:

[root@localhost src]# ./redis-cli --eval /root/lua_scripts/lua_script.lua incr:key:0001 , -2 200
"2_198"
[root@localhost src]# 
[root@localhost src]# ./redis-cli --eval /root/lua_scripts/lua_script.lua incr:key:0001 , -2 200
"1_196"
[root@localhost src]# 
  1. 代码中使用lua脚本

命令行中运行lua脚本在实际项目中其实并不常用,更多的是在代码中执行lua脚本,下面演示在代码中如何使用lua脚本,先演示在redisTemplate中的使用:
在redisTemplate中使用lua脚本需要调用redisTemplate.execute(RedisScript script, List keys, Object… args)方法,该方法第一个参数是lua脚本,第二个参数是键列表,第三个参数是一个不定个数的值数组。使用方式如下:

String lua = "return redis.call('set', KEYS[1], ARGV[1]);";
DefaultRedisScript<String> script = new DefaultRedisScript<>(lua, String.class);

List<String> keys = Collections.singletonList("java:lua:key");
String rs = redisTemplate.execute(script, keys, "java_lua_value");

除了上面这种定义lua脚本方式,还可以写一个lua脚本文件,在程序中调用这个lua脚本文件执行,代码如下:

DefaultRedisScript<String> script = new DefaultRedisScript<>();
script.setResultType(String.class);
script.setScriptSource(new ResourceScriptSource(new FileSystemResource("E:\\lua_script.lua")));
List<String> keys = Collections.singletonList("ncr:key:0001");
String rs = redisTemplate.execute(script, keys, "-2", "200");

如果项目使用的是jedis,代码就跟redis客户端执行命令一样:

String script = "return redis.call('get', KEYS[1]);";
Object rs = jedis.eval(script, 1, "incr:key:0001");
System.out.println(rs);

redis还提供了使用evalsha()方式执行lua脚本,这种方式是先将lua脚本在服务端缓存,客户端只需要传递过去缓存脚本的编码就可以调用lua脚本执行:
比如在服务端加载一个lua脚本并生成了编码:

127.0.0.1:6379> script load "return redis.call('get', KEYS[1]);"
"796941151549c416aa77522fb347487236c05e46"
127.0.0.1:6379> 

这时在客户端要执行lua脚本只需要执行下面的方法:

Object rs = jedis.evalsha("796941151549c416aa77522fb347487236c05e46", 1, "incr:key:0001");
System.out.println(rs);

使用redisTemplate调用evalsha需要redisTemplate的execute()方法,它类似redis原生命令,请求和返回数据都是byte数组:

Object rs = redisTemplate.execute(new RedisCallback<Object>() {
    @Override
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
        byte[] rs = connection.evalSha("796941151549c416aa77522fb347487236c05e46", ReturnType.VALUE, 1, "incr:key:0001".getBytes(StandardCharsets.UTF_8));
        return new String(rs);
    }
});
System.out.println(rs);

上面这种方式性能是最好的,因为脚本都是在服务端缓存起来了,省去了数据提交时的性能损耗。但是在项目中应该很少被使用,因为一旦redis宕机,意味着缓存的脚本编码都将失效,那么程序运行就会出问题。

在redis中使用lua脚本非常好用,可以将一些简单的处理逻辑在服务端就执行了,避免数据的来回传输。使用lua脚本也可以让多个命令执行保持原子性,在lua脚本执行中间不能插入其他的redis命令。由于lua脚本都是整体运行的,所以也不建议在lua脚本中执行非常耗时的命令,或者一个lua脚本中执行过多的命令,这样会导致服务端响应其他的请求等待时间超时。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181823.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!