Springboot redis使用lua和pipeline
阿新 • • 發佈:2021-12-24
LUA
Lua是作為嵌入式指令碼的最佳選擇,速度非常快
在redis命令列中,可以直接呼叫lua指令碼,比如
127.0.0.1:6380> eval "local result={} for loop=1, #(KEYS) do result[loop]=redis.call('hget',KEYS[loop], ARGV[1]) end return result" 3 xiaoa xiaob xiaoc count 1) "2" 2) "5" 3) "1" 127.0.0.1:6380>
使用SpringBoot呼叫lua指令碼,存在兩種方式
一、直接在程式碼中通過字串呼叫
StringBuilder lua = newStringBuilder(); lua.append("local result={} "); lua.append("for loop=1, #(KEYS) "); lua.append("do result[loop]=redis.call('hget',KEYS[loop], ARGV[1]) "); lua.append("end "); lua.append("return result"); RedisScript<List> script = RedisScript.of(lua.toString(), List.class); List<String> keys = new ArrayList<String>(); keys.add("xiaoa"); keys.add("xiaob"); keys.add("xiaoc"); List<Object> args = new ArrayList<>(); args.add("count"); List<String> result = redisTemplate.execute(script, keys, args.toArray()); logger.info("luaTest: {}", result);
二、通過lua指令碼檔案呼叫
1、指令碼檔案,count_qry.lua
local result={} for loop = 1,#(KEYS) do result[loop]= redis.call('hget',KEYS[loop], ARGV[1]) end return result
// src/main/resources下 ClassPathResource resource = new ClassPathResource("lua/count_qry.lua"); RedisScript<List> script = RedisScript.of(resource, List.class); List<String> keys = new ArrayList<String>(); keys.add("xiaoa"); keys.add("xiaob"); keys.add("xiaoc"); List<Object> args = new ArrayList<>(); args.add("count"); List<String> result = redisTemplate.execute(script, keys, args.toArray()); logger.info("luaFileTest: {}", result);
使用springboot pipeline也能實現上述的功能
List<Object> result = redisTemplate.executePipelined(new RedisCallback() { @Override public List<String> doInRedis(RedisConnection connection) throws DataAccessException { List<String> keys = new ArrayList<String>(); keys.add("xiaoa"); keys.add("xiaob"); keys.add("xiaoc"); for (int i = 0; i < keys.size(); i++) { connection.hGet(keys.get(i).getBytes(), "count".getBytes()); } // 必須返回null,否則將出現異常 return null; } }); logger.info("result: {}", result);
使用lua指令碼和pipeline都可以實現簡單的原子性,但lua指令碼比pipelin更靈活,實現功能更多,比如有個佇列,存放的資料不能超過N個,超過的將不會被儲存,這種實現方式很多,其中一種方式比如在插入佇列資料之前,先查詢下資料個數,大於等於N個,則不新增,這個邏輯用pipeline不能夠實現,但可以用lua實現,假設N=3
local len=redis.call('llen',KEYS[1]) if len >= 3 then return false else redis.call('lpush',KEYS[1],ARGV[1]) return true end
// src/main/resources下 ClassPathResource resource = new ClassPathResource("lua/queue.lua"); RedisScript<Boolean> script = RedisScript.of(resource, Boolean.class); List<String> keys = new ArrayList<String>(); keys.add("testqueue"); List<Object> args = new ArrayList<>(); args.add("val"); for (int i = 0; i < 4; i++) { boolean result = redisTemplate.execute(script, keys, args.toArray()); logger.info("add val to queue result: {}", result); }
結果,前三個資料成功插入,第四個資料被拒絕
2021-12-24 15:29:36.961 INFO 34276 --- [ main] com.demo.redistest.RedisLuaTest : add val to queue result: true 2021-12-24 15:29:36.964 INFO 34276 --- [ main] com.demo.redistest.RedisLuaTest : add val to queue result: true 2021-12-24 15:29:36.966 INFO 34276 --- [ main] com.demo.redistest.RedisLuaTest : add val to queue result: true 2021-12-24 15:29:36.968 INFO 34276 --- [ main] com.demo.redistest.RedisLuaTest : add val to queue result: false