Redis進階之使用Lua腳本自定義Redis命令

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源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章