redis之lua的使用

1 篇文章 0 订阅

1.lua的概述

https://www.runoob.com/lua/lua-tutorial.html

2.redis中eval调用lua的语法格式:eval lua-script key-num keys[],arg[]

eval lua-script key-num keys[],arg[]
1.lua-script:这个是我们lua写的脚本命令
2.key-num:这个是我们后面keys参数的个数
3.keys[]:是我们key-num标明的参数个数明细,当key-num为0时,这个可以省略
4.arg[]:是我们keys[]对应的value值,当key-num为0时,这个可以省略

2.1.举例:eval “return ‘hello gaoxinfu’” 0

参数个数为0,所以后面keys[],args[] 都没有

127.0.0.1:6379> eval "return 'hello gaoxinfu'" 0
"hello gaoxinfu"
127.0.0.1:6379> 

3.在lua脚本中调用redis命令(通过call方法):redis.call(command, key [param1, param2…])

redis.call(command, key [param1, param2…])

3.1.举例:EVAL “redis.call(‘set’,KEYS[1],ARGV[1])” 1 ename frank

127.0.0.1:6379> EVAL "redis.call('set',KEYS[1],ARGV[1])" 1 ename frank
(nil)
127.0.0.1:6379> get ename
"frank"

备注说明

1.大家要注意下 KEYS和ARGV这两个参数的名字是固定的,必须是大写,这样写
2.KEYS[1],第一个key
3.ARGV[1],标示我们需要使用的value,这个有可能是我们setkey的时候使用,也有可能在脚本调用的时候使用
4.实际上相当于redis当中的set en
127.0.0.1:6379> EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 sex m
OK
127.0.0.1:6379> get sex
"m"
127.0.0.1:6379> 

4.将lua脚本放到文件中去,然后redis去调用:redis-cli –eval [lua 脚本] [key…]空格,空格[args…]

4.1.案例1

4.1.1.创建name.lua脚本

在这里插入图片描述
name.lua 文件内容

redis.call('set','ename','gaoxinfu')
return redis.call('get','ename')                                  

4.1.2.执行命令验证:/usr/local/bin/redis-cli --eval name.lua

同样最后面的0是参数个数

localhost:demo gaoxinfu$ /usr/local/bin/redis-cli --eval name.lua
"gaoxinfu"

4.2.案例2

4.2.1.创建ip-limit.lua脚本

-- ip_limit.lua
-- IP 限流,对某个IP 频率进行限制 ,6 秒钟访问 10 次
其中KEYS[1]是ip
ARGV[1]:为设置ip的有效时间
ARGV[2]:为设置限制的访问次数
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      

4.2.2.执行命令

比如 6秒钟之内只能访问2次,很显然,前面两次成功了,后面就不成功了

localhost:demo gaoxinfu$ /usr/local/bin/redis-cli  --eval ip-limit.lua 127.0.0.1 , 6 2
(integer) 1
localhost:demo gaoxinfu$ /usr/local/bin/redis-cli  --eval ip-limit.lua 127.0.0.1 , 6 2
(integer) 1
localhost:demo gaoxinfu$ /usr/local/bin/redis-cli  --eval ip-limit.lua 127.0.0.1 , 6 2
(integer) 0
localhost:demo gaoxinfu$ /usr/local/bin/redis-cli  --eval ip-limit.lua 127.0.0.1 , 6 2
(integer) 0
localhost:demo gaoxinfu$ /usr/local/bin/redis-cli  --eval ip-limit.lua 127.0.0.1 , 6 2
(integer) 0
localhost:demo gaoxinfu$ 

5.将lua脚本缓存到redis服务端中

5.1.为什么要将lua脚本缓存到redis服务端

1.在执行redis的命令的时候,因为lua脚本是命令或者文件的形式,每次都需要将lua脚本命令传递给redis服务端,
  每一次调用,会产生比较大的网络开销;

5.2.如何缓存lua脚本:SCRIPT load “return ‘hello world’”

5.2.1.案例1

1.为了解决这个交互导致的网络开销问题,redis提供了一个evalsha命令;
127.0.0.1:6379> SCRIPT load "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
127.0.0.1:6379> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
"hello world"

5.3.如何调用缓存的lua脚本:EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0

5.3.1.案例1

1.为了解决这个交互导致的网络开销问题,redis提供了一个evalsha命令;
127.0.0.1:6379> SCRIPT load "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
127.0.0.1:6379> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
"hello world"

5.2.2.案例2

同样上面我们的4.2.案例2也可以 先缓存,然后调用
这里需要注意下,之前在文件中,现在弄成一行,需要注意是否可以用;分号

127.0.0.1:6379> SCRIPT LOAD "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"
"766696820dc636809147aba60dcab8c488860ff5"
127.0.0.1:6379>

这里调用的时候要注意下,格式,766696820dc636809147aba60dcab8c488860ff5后面第一个是参数key个数,后面的就是1个key2个值

127.0.0.1:6379> EVALSHA 766696820dc636809147aba60dcab8c488860ff5 1 127.0.0.1 6 2
(integer) 0
127.0.0.1:6379> 

6.使用lua实现redis的自乘(redis有自增自减,但是无乘)

将key[1]的值乘以ARGV[1]倍数

local curVal = redis.call("get", KEYS[1]); 
if curVal == false 
   then curVal = 0 
   else curVal = tonumber(curVal) 
end;
curVal = curVal * tonumber(ARGV[1]); 
redis.call("set", KEYS[1], curVal);
return curVal;

执行验证,上面的lua脚本去掉换行

127.0.0.1:6379> SCRIPT LOAD 'local curVal = redis.call("get", KEYS[1]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end;curVal = curVal * tonumber(ARGV[1]);redis.call("set",KEYS[1], curVal);return curVal;'
"3ffdd17da7e77aa3249ac0d0b05d6e8fc348e8f3"
127.0.0.1:6379> 

设置年龄10岁,然后通过调用lua缓存命令乘以3

127.0.0.1:6379> SCRIPT LOAD 'local curVal = redis.call("get", KEYS[1]); if curVal == false then curVal = 0 else curVal = tonumber(curVal) end;curVal = curVal * tonumber(ARGV[1]);redis.call("set",KEYS[1], curVal);return curVal;'
"3ffdd17da7e77aa3249ac0d0b05d6e8fc348e8f3"
127.0.0.1:6379> set gaoxinfu_age 10
OK
127.0.0.1:6379> EVALSHA 3ffdd17da7e77aa3249ac0d0b05d6e8fc348e8f3 1 gaoxinfu_age 3
(integer) 30
127.0.0.1:6379> 

7.lua脚本超时问题

7.1.lua脚本默认超时时间:默认5s

1.当脚本运行时间超过这一限制后,Redis 将开始接受其他命令但不会执行(以确保脚本的原子性,因为此时脚本并没有被终止),而是会返回“BUSY”错误。
localhost:redis-5.0.5 gaoxinfu$ pwd
/Users/gaoxinfu/using/redis-5.0.5
localhost:redis-5.0.5 gaoxinfu$ ls -la|grep redis.conf
-rw-r--r--@   1 gaoxinfu  staff   61798  3 17 21:10 redis.conf
-rw-r--r--@   1 gaoxinfu  staff   61797  3 17 21:09 redis.conf-bak

在这里插入图片描述

7.2.如何手动停止lua脚本命令的执行:script kill

7.2.1.案例1:杀死正在执行lua脚本

127.0.0.1:6379> EVAL "while(true) do end" 0
127.0.0.1:6379> SCRIPT KILL
OK
127.0.0.1:6379> 
127.0.0.1:6379> EVAL "while(true) do end" 0
(error) ERR Error running script (call to f_eec1f08dafc6bfdf256e3820d971514a3a24267e): @user_script:1: Script killed by user with SCRIPT KILL... 
(10.16s)
127.0.0.1:6379> 

在这里插入图片描述

7.2.2.案例2:

执行下面的命令,第一个set是成功的,第二个进入了死循环

127.0.0.1:6379> eval "redis.call('set','gupao','666') while true do end" 0

我们使用script kill去啥子执行lua的脚本进程,但是发现没杀成功,原因是上面的命令是两步,第一步是执行成功了,但是第二步
处理失败了,只要有一步执行成功了,是无法杀死的lua进程的

127.0.0.1:6379> SCRIPT KILL
(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.
127.0.0.1:6379> 

在这里插入图片描述

7.3.针对上面script kill无法杀死lua脚本进程的方案:shutdown nosave

shutdown nosave 和 shutdown 的区别在于 shutdown nosave 不会进行持久化操作,意味着发生在上一次快照后的数据库修改都会丢失。
实际上就是把redis服务停掉,shutdown nosave不会进行持久化操作,一般不使用
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东山富哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值