Redis中的Lua 脚本
Lua/?lu?/是一種輕量級(jí)腳本語(yǔ)言,它是用C 語(yǔ)言編寫的,跟數(shù)據(jù)的存儲(chǔ)過(guò)程有點(diǎn)類似。使用Lua 腳本來(lái)執(zhí)行Redis 命令的好處:
1、一次發(fā)送多個(gè)命令,減少網(wǎng)絡(luò)開(kāi)銷。
2、Redis 會(huì)將整個(gè)腳本作為一個(gè)整體執(zhí)行,不會(huì)被其他請(qǐng)求打斷,保持原子性。
3、對(duì)于復(fù)雜的組合命令,我們可以放在文件中,可以實(shí)現(xiàn)程序之間的命令集復(fù)用。
?
在Redis 中調(diào)用Lua 腳本
使用eval /?'v?l/ 方法,語(yǔ)法格式:
redis> eval lua-script key-num [key1 key2 key3 ....] [value1 value2 value3 ....]eval 代表執(zhí)行Lua 語(yǔ)言的命令。
lua-script 代表Lua 語(yǔ)言腳本內(nèi)容。
key-num 表示參數(shù)中有多少個(gè)key,需要注意的是Redis 中key 是從1 開(kāi)始的,如果沒(méi)有key 的參數(shù),那么寫0。
[key1 key2 key3…]是key 作為參數(shù)傳遞給Lua 語(yǔ)言,也可以不填,但是需要和key-num 的個(gè)數(shù)對(duì)應(yīng)起來(lái)。
[value1 value2 value3 ….]這些參數(shù)傳遞給Lua 語(yǔ)言,它們是可填可不填的。
?
示例,返回一個(gè)字符串,0 個(gè)參數(shù):
redis> eval "return 'Hello World'" 0在Lua 腳本中調(diào)用Redis 命令
使用redis.call(command, key [param1, param2…])進(jìn)行操作。語(yǔ)法格式:
redis> eval "redis.call('set',KEYS[1],ARGV[1])" 1 lua-key lua-valuecommand 是命令,包括set、get、del 等。
key 是被操作的鍵。
param1,param2…代表給key 的參數(shù)。
注意跟Java 不一樣,定義只有形參,調(diào)用只有實(shí)參。
Lua 是在調(diào)用時(shí)用key 表示形參,argv 表示參數(shù)值(實(shí)參)
?
設(shè)置鍵值對(duì)
在Redis 中調(diào)用Lua 腳本執(zhí)行Redis 命令
redis> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 leon 2673 redis> get leon以上命令等價(jià)于set leon 2673。
在redis-cli 中直接寫Lua 腳本不夠方便,也不能實(shí)現(xiàn)編輯和復(fù)用,通常我們會(huì)把腳本放在文件里面,然后執(zhí)行這個(gè)文件。
在Redis 中調(diào)用Lua 腳本文件中的命令,操作Redis
創(chuàng)建Lua 腳本文件:
cd /usr/local/soft/redis5.0.5/src vim leon.luaLua 腳本內(nèi)容,先設(shè)置,再取值:
redis.call('set','leon','lua666') return redis.call('get','leon')在Redis 客戶端中調(diào)用Lua 腳本
cd /usr/local/soft/redis5.0.5/src redis-cli --eval leon.lua 0得到返回值:
[root@localhost src]# redis-cli --eval leon.lua 0 "lua666"案例:對(duì)IP 進(jìn)行限流
需求:在X 秒內(nèi)只能訪問(wèn)Y 次。
設(shè)計(jì)思路:用key 記錄IP,用value 記錄訪問(wèn)次數(shù)。
拿到IP 以后,對(duì)IP+1。如果是第一次訪問(wèn),對(duì)key 設(shè)置過(guò)期時(shí)間(參數(shù)1)。否則判斷次數(shù),超過(guò)限定的次數(shù)(參數(shù)2),返回0。如果沒(méi)有超過(guò)次數(shù)則返回1。超過(guò)時(shí)間,key 過(guò)期之后,可以再次訪問(wèn)。
KEY[1]是IP, ARGV[1]是過(guò)期時(shí)間X,ARGV[2]是限制訪問(wèn)的次數(shù)Y。
-- ip_limit.lua -- IP 限流,對(duì)某個(gè)IP 頻率進(jìn)行限制,6 秒鐘訪問(wèn)10 次 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 end6 秒鐘內(nèi)限制訪問(wèn)10 次,調(diào)用測(cè)試(連續(xù)調(diào)用10 次):
./redis-cli --eval "ip_limit.lua" app:ip:limit:192.168.8.111 , 6 10app:ip:limit:192.168.8.111 是key 值,后面是參數(shù)值,中間要加上一個(gè)空格和一個(gè)逗號(hào),再加上一個(gè)空格。
即:./redis-cli –eval [lua 腳本] [key…]空格,空格[args…]
多個(gè)參數(shù)之間用一個(gè)空格分割。
代碼:LuaTest.java
public class LuaTest {public static void main(String[] args) {Jedis jedis = getJedisUtil();jedis.eval("return redis.call('set',KEYS[1],ARGV[1])", 1,"test:lua:key","qingshan2673lua");System.out.println(jedis.get("test:lua:key"));for(int i=0; i<10; i++){limit();}}/*** 10秒內(nèi)限制訪問(wèn)5次*/public static void limit(){Jedis jedis = getJedisUtil();// 只在第一次對(duì)key設(shè)置過(guò)期時(shí)間String lua = "local num = redis.call('incr', KEYS[1])\n" +"if tonumber(num) == 1 then\n" +"\tredis.call('expire', KEYS[1], ARGV[1])\n" +"\treturn 1\n" +"elseif tonumber(num) > tonumber(ARGV[2]) then\n" +"\treturn 0\n" +"else \n" +"\treturn 1\n" +"end\n";Object result = jedis.evalsha(jedis.scriptLoad(lua), Arrays.asList("localhost"), Arrays.asList("10", "5"));System.out.println(result);}private static Jedis getJedisUtil() {String ip = ResourceUtil.getKey("redis.host");int port = Integer.valueOf(ResourceUtil.getKey("redis.port"));String password = ResourceUtil.getKey("redis.password");JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();JedisPool pool = new JedisPool(jedisPoolConfig, ip, port, 10000, password);return pool.getResource();}}?
總結(jié)
以上是生活随笔為你收集整理的Redis中的Lua 脚本的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Redis中的发布订阅模式
- 下一篇: Redis中缓存Lua 脚本