https://www.runoob.com/lua/lua-tutorial.html
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时,这个可以省略
参数个数为0,所以后面keys[],args[] 都没有
127.0.0.1:6379> eval "return 'hello gaoxinfu'" 0
"hello gaoxinfu"
127.0.0.1:6379>
redis.call(command, key [param1, param2…])
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>
redis.call('set','ename','gaoxinfu')
return redis.call('get','ename')
同样最后面的0是参数个数
localhost:demo gaoxinfu$ /usr/local/bin/redis-cli --eval name.lua
"gaoxinfu"
-- 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
比如 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$
1.在执行redis的命令的时候,因为lua脚本是命令或者文件的形式,每次都需要将lua脚本命令传递给redis服务端,
每一次调用,会产生比较大的网络开销;
1.为了解决这个交互导致的网络开销问题,redis提供了一个evalsha命令;
127.0.0.1:6379> SCRIPT load "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
127.0.0.1:6379> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
"hello world"
1.为了解决这个交互导致的网络开销问题,redis提供了一个evalsha命令;
127.0.0.1:6379> SCRIPT load "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
127.0.0.1:6379> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
"hello world"
同样上面我们的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>
将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>
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
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>
执行下面的命令,第一个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>
shutdown nosave 和 shutdown 的区别在于 shutdown nosave 不会进行持久化操作,意味着发生在上一次快照后的数据库修改都会丢失。
实际上就是把redis服务停掉,shutdown nosave不会进行持久化操作,一般不使用