1.在Redis中使用Lua
在Redis中執行Lua腳本有兩種方法:eval和evalsha。
1.1 eval
eval 腳本內容 key個數 key列表 參數列表
下面例子使用了key列表和參數列表來爲Lua腳本提供更多的靈活性:
127.0.0.1:6379> eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world
"hello redisworld"
此時KEYS[1]="redis",ARGV[1]="world",所以最終的返回結果是"hello redisworld"。
如果Lua腳本較長,還可以使用redis-cli--eval直接執行文件。
$ redis-cli --eval hello.lua mykey , myargv
注意,這種方式不需要指定key的數量,用 , 號劃分key和arg,注意逗號左右的空格。
eval命令和--eval參數本質是一樣的,客戶端如果想執行Lua腳本,首先在客戶端編寫好Lua腳本代碼,然後把腳本作爲字符串發送給服務端,服務端會將執行結果返回給客戶端。
1.2 evalsha
除了使用eval,Redis還提供了evalsha命令來執行Lua腳本。
首先要將Lua腳本加載到Redis服務端,得到該腳本的SHA1校驗和,evalsha命令使用SHA1作爲參數可以直接執行對應Lua腳本,避免每次發送Lua腳本的開銷。這樣客戶端就不需要每次執行腳本內容,而腳本也會常駐在服務端,腳本功能得到了複用。
加載腳本
script load命令可以將腳本內容加載到Redis內存中,例如下面將lua_get.lua加載到Redis中,得到SHA1爲:"7413dc2440db1fea7c0a0bde841fa68eefaf149c"
$ redis-cli script load "$(cat lua_get.lua)"
"7413dc2440db1fea7c0a0bde841fa68eefaf149c"
執行腳本
evalsha的使用方法如下,參數使用SHA1值,執行邏輯和eval一致。
evalsha 腳本SHA1值 key個數 key列表 參數列表
所以只需要執行如下操作,就可以調用lua_get.lua腳本:
127.0.0.1:6379> evalsha 7413dc2440db1fea7c0a0bde841fa68eefaf149c 1 redis world
"hello redisworld"
2.Lua的RedisAPI
Lua可以使用redis.call函數實現對Redis的訪問,例如下面代碼是Lua使用redis.call調用了Redis的set和get操作:
redis.call("set", "hello", "world")
redis.call("get", "hello")
放在Redis的執行效果如下:
127.0.0.1:6379> eval 'return redis.call("get", KEYS[1])' 1 hello
"world"
除此之外Lua還可以使用redis.pcall函數實現對Redis的調用,redis.call和redis.pcall的不同在於,如果redis.call執行失敗,那麼腳本執行結束會直接返回錯誤,而redis.pcall會忽略錯誤繼續執行腳本,所以在實際開發中要根據具體的應用場景進行函數的選擇。
獲取KEY可以通過 KEYS[1],獲取 Value 可以通過 ARGV[1] 。
3.開發提示
Lua可以使用redis.log函數將Lua腳本的日誌輸出到Redis的日誌文件中,但是一定要控制日誌級別。Redis3.2提供了Lua Script Debugger功能用來調試複雜的Lua腳本,具體可以參考:http://redis.io/topics/ldb。
redis.log(redis.LOG_DEBUG,key1)
其他還有:
-
redis.LOG_DEBUG
-
redis.LOG_VERBOSE
-
redis.LOG_NOTICE
-
redis.LOG_WARNING
SpringBoot通過RedisTemplate執行Lua腳本
1.RedisScript
首先你得引入spring-boot-starter-data-redis依賴,其次把lua腳本放在resources目錄下。
@Bean
public DefaultRedisScript<List> defaultRedisScript() {
DefaultRedisScript<List> defaultRedisScript = new DefaultRedisScript<>();
defaultRedisScript.setResultType(List.class);
// defaultRedisScript.setScriptText("");
defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/demo.lua")));
return defaultRedisScript;
}
在Spring Boot2.0的時候,上述配置沒有問題,但在Spring Boot1.5測試會出錯,需要將List.class改爲具體的返回類型(如Long.class)。
RedisScript的getSha1()方法可以獲取腳本摘要。
2.調用腳本
/**
* List設置lua的KEYS
*/
List<String> keyList = new ArrayList();
keyList.add("count");
keyList.add("rate.limiting:127.0.0.1");
/**
* 用Mpa設置Lua的ARGV[1]
*/
Map<String, Object> argvMap = new HashMap<String, Object>();
argvMap.put("expire", 10000);
argvMap.put("times", 10);
/**
* 調用腳本並執行
*/
List result = redisTemplate1.execute(redisScript, keyList, argvMap);
System.out.println(result);
若是出現序列化問題,可以指定序列化方式。
public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer,
List<K> keys, Object... args) {
return scriptExecutor.execute(script, argsSerializer, resultSerializer, keys, args);
}
3.Lua腳本
--獲取KEY
local key1 = KEYS[1]
local key2 = KEYS[2]
-- 獲取ARGV[1],這裏對應到應用端是一個List<Map>.
-- 注意,這裏接收到是的字符串,所以需要用csjon庫解碼成table類型
local receive_arg_json = cjson.decode(ARGV[1])
--獲取ARGV內的參數並打印
local expire = receive_arg_json.expire
local times = receive_arg_json.times
作者:薛勤,互聯網從業者,編程愛好者。
本文首發自公衆號:代碼藝術(ID:onblog)未經許可,不可轉載
本文分享自微信公衆號 - 代碼藝術(onblog)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。