SpringBoot中 Lua函數操作redis

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操作就說到這裏,歡迎繼續關注

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