当前位置:   article > 正文

redis中使用lua脚本_redis lua更新json某个值

redis lua更新json某个值

1.lua脚本

1.1 简介

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

1.2 使用脚本的好处

1. 减少网络开销,在Lua脚本中可以把多个命令放在同一个脚本中运行
2. 原子操作,redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。换句话说,编写脚本的过程中无需担心会出现竞态条件
3. 复用性,客户端发送的脚本会永远存储在redis中,这意味着其他客户端可以复用这一脚本来完成同样的逻辑

2.安装

#下载
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz
#解压
tar zxf lua-5.3.0.tar.gz
#进入解压的目录
cd lua-5.3.0
#linux环境下编译
make linux test
#安装
make install

#如果报错,说找不到readline/readline.h, 可以通过yum命令安装
yum install readline-devel
#安装完以后再
make linux  / make install

#进入lua控制台
lua
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.lua基础教程

查看菜鸟教程 链接

4.Redis与Lua

在Lua脚本中调用Redis命令,可以使用redis.call函数调用。比如我们调用string类型的命令

eval "return redis.call('set','hello','world')" 0

  • 1
  • 2

redis.call 函数的返回值就是redis命令的执行结果。前面我们介绍过redis的5中类型的数据返回的值的类型也都不一样。redis.call函数会将这5种类型的返回值转化对应的Lua的数据类型

4.1__从Lua脚本中获得返回值__

在很多情况下我们都需要脚本可以有返回值,在脚本中可以使用return 语句将值返回给redis客户端,通过return语句来执行,如果没有执行return,默认返回为nil。

4.2__如何在redis中执行lua脚本__

Redis提供了EVAL命令可以使开发者像调用其他Redis内置命令一样调用脚本。
[EVAL]  [脚本内容] [key参数的数量]  [key …] [arg …]
可以通过key和arg这两个参数向脚本中传递数据,他们的值可以在脚本中分别使用__KEYS__和__ARGV__ 这两个类型的全局变量访问。比如我们通过脚本实现一个set命令,通过在redis客户端中调用,那么执行的语句是:

#参数传递 KEYS和ARGV必须大写
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 hello world
  • 1
  • 2

EVAL命令是根据 key参数的数量-也就是上面例子中的1来将后面所有参数分别存入脚本中KEYS和ARGV两个表类型的全局变量。当脚本不需要任何参数时也不能省略这个参数。如果没有参数则为0

eval "return redis.call('get','hello')" 0
  • 1

4.3__EVALSHA命令__

考虑到我们通过eval执行lua脚本,脚本比较长的情况下,每次调用脚本都需要把整个脚本传给redis,比较占用带宽。为了解决这个问题,redis提供了EVALSHA命令允许开发者通过脚本内容的SHA1摘要来执行脚本。该命令的用法和EVAL一样,只不过是将脚本内容替换成脚本内容的SHA1摘要
 
1. Redis在执行EVAL命令时会计算脚本的SHA1摘要并记录在脚本缓存中
2. 执行EVALSHA命令时Redis会根据提供的摘要从脚本缓存中查找对应的脚本内容,如果找到了就执行脚本,否则返回“NOSCRIPT No matching script,Please use EVAL”
 
通过以下案例来演示EVALSHA命令的效果
script load “return redis.call(‘get’,‘hello’)”          将脚本加入缓存并生成sha1命令
evalsha “a5a402e90df3eaeca2ff03d56d99982e05cf6574” 0
我们在调用eval命令之前,先执行evalsha命令,如果提示脚本不存在,则再调用eval命令

4.4__lua脚本实战__

4.4.1 linux上调用lua脚本

实现一个针对某个手机号的访问频次, 以下是lua脚本,保存为phone_limit.lua

local num=redis.call('incr',KEYS[1])
if tonumber(num)==1 then
   redis.call('expire',KEYS[1],ARGV[1])
   return 1
elseif tonumber(num)>tonumber(ARGV[2]) then
   return 0
else
   return 1
end

#通过如下命令调用
./redis-cli --eval phone_limit.lua rate.limiting:13700000000 , 10 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

语法为 ./redis-cli –eval [lua脚本] [key…]空格,空格[args…]

4.4.2 java使用redis调用lua

    public static void main(String[] args) {
        Jedis jedis = RedisManger.getJedis();
        String lua="local num=redis.call('incr',KEYS[1])\n" +
                "if tonumber(num)==1 then\n" +
                "   redis.call('expire',KEYS[1],ARGV[1])\n" +
                "   return 1\n" +
                "elseif tonumber(num)>tonumber(ARGV[2]) then\n" +
                "   return 0\n" +
                "else\n" +
                "   return 1\n" +
                "end";
        List<String> keys = new ArrayList<>();
        keys.add("ip:limit:127.0.0.1");
        List<String> argss = new ArrayList<>();
        argss.add("6000");
        argss.add("10");
//        Object obj = jedis.eval(lua, keys, argss);
        Object obj = jedis.evalsha(jedis.scriptLoad(lua), keys, argss);//把脚本缓存起来
        System.out.println(obj);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4.5__脚本的原子性__

redis的脚本执行是原子的,即脚本执行期间Redis不会执行其他命令。所有的命令必须等待脚本执行完以后才能执行。为了防止某个脚本执行时间过程导致Redis无法提供服务。Redis提供了lua-time-limit参数限制脚本的最长运行时间。默认是5秒钟。
当脚本运行时间超过这个限制后,Redis将开始接受其他命令但不会执行(以确保脚本的原子性),而是返回BUSY的错误

实践操作

打开两个客户端窗口
在第一个窗口中执行lua脚本的死循环
eval “while true do end” 0
 
在第二个窗口中运行get hello
 
最后第二个窗口的运行结果是Busy, 可以通过script kill命令终止正在执行的脚本。如果当前执行的lua脚本对redis的数据进行了修改,比如(set)操作,那么script kill命令没办法终止脚本的运行,因为要保证lua脚本的原子性。如果执行一部分终止了,就违背了这一个原则
在这种情况下,只能通过 __shutdown nosave__命令强行终止

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号