Lua
Lua 是一個簡潔、輕量、可擴展的腳本語言,它的特性有
- 輕量:源碼包只有核心庫,編譯後體積很小
- 高效:由 ANSI C 寫的,啓動快、運行快
- 內嵌:可內嵌到各種編程語言或系統中運行,提升靜態語言的靈活性。如 OpenResty 就是將 Lua 嵌入到 nginx 中執行
Redis 在 2.6 版本後,開始支持Lua腳本
優點
-
減少網絡開銷:多個請求通過腳本一次發送,減少網絡延遲
-
原子操作:將腳本作爲一個整體執行,中間不會插入其他命令,無需使用事務
-
複用:客戶端發送的腳本永久存在redis中,其他客戶端可以複用腳本
-
可嵌入性:可嵌入JAVA,C#等多種編程語言,支持不同操作系統跨平臺交互
執行步驟
- 檢查腳本是否執行過,沒執行過使用腳本的 sha1 校驗和生成一個 Lua 函數
- 爲函數綁定超時、錯誤處理勾子
- 創建一個僞客戶端,通過這個僞客戶端執行 Lua 中的 Redis 命令
- 處理僞客戶端的返回值,最終返回給客戶端
Spring Boot 實現
redis 數據結構zset 是一個有序集合,我們可以通過zadd 根據score 有序存儲,通過zrange 查詢,通過zrem 刪除。但有個問題,我們在有序集合取值的時候,無法做到像隊列、棧一樣,做到彈棧,即需要先通過zrange 拿到最大(小)值,然後通過zrem刪除剛剛取得值,考慮到併發問題,zrange和zrem操作加併發鎖。中間至少四次操作redis網絡請求(獲取鎖、zrange、 zrem、釋放鎖),那有沒有什麼辦法通過一次網絡請求就可以完成,那就是Lua函數,
Lua函數
//獲取最大(小)值
local object = redis.call('ZRANGE',KEYS[1],0,0);
//刪除最大(小)值
redis.call('ZREMRANGEBYRANK',KEYS[1],0,0);
//返回目標值
return object;
存儲有序元素 LuaBean
[@Data](https://my.oschina.net/difrik)
@NoArgsConstructor
@AllArgsConstructor
public class LuaBean {
private Integer score;
private String name;
private Integer order;
}
存儲有序集合
@GetMapping("/add")
public Object set(){
LuaBean luaBean = new LuaBean(SCORE,"su"+SCORE,SCORE);
Boolean add = redisTemplate.opsForZSet().add(REDIS_KEY, luaBean, luaBean.getScore());
++SCORE;
return add;
}
有序結果集
[
{
"score": 1,
"name": "su1",
"order": 1
},
{
"score": 2,
"name": "su2",
"order": 2
},
{
"score": 3,
"name": "su3",
"order": 3
},
{
"score": 4,
"name": "su4",
"order": 4
},
{
"score": 5,
"name": "su5",
"order": 5
}
]
執行語句集
@GetMapping("/pop")
public Object pop(){
String scriptText = "local object = redis.call('ZRANGE',KEYS[1],0,0);\n" +
"redis.call('ZREMRANGEBYRANK',KEYS[1],0,0);\n" +
"return object;";
DefaultRedisScript<LuaBean> redisScript = new DefaultRedisScript<>();
redisScript.setResultType(LuaBean.class);
redisScript.setScriptText(scriptText);
List<String> keys = new ArrayList<>();
keys.add(REDIS_KEY);
Object execute = redisTemplate.execute(redisScript, keys);
return execute;
}
可以發現每次操作我們模擬的pop
我們可以拿到
{
"score": 1,
"name": "su1",
"order": 1
}
第二次
{
"score": 2,
"name": "su2",
"order": 2
}
此時redis 剩餘集合
[
{
"score": 3,
"name": "su3",
"order": 3
},
{
"score": 4,
"name": "su4",
"order": 4
},
{
"score": 5,
"name": "su5",
"order": 5
}
]
ok,目前爲止,redis 系列的Lua操作就說到這裏,歡迎繼續關注