背景
之前分享了一篇利用lua腳本批量刪除redis的key的文章.現在項目中我打算使用spring的緩存,而Spring緩存以前我是用ehcache來做實現的.沒發現什么問題..這次我換成redis來做緩存了..一般也沒什么大問題...目前唯一遇到的一個比較坑的問題就是緩存通過注解刪除不了..我想想好像也算正常吧.因為java里面做緩存的話可能會使用map類似的結構,我remove掉一個key,不管對應的value是什么結構都能刪除掉..但是redis的key是沒有層級的...比如2個key分別是a:a1和a:a2.雖然可視化工具上看上去都是屬於a下面的..但是redis的del a並不會刪除a下屬的所有key而是只會刪除key為a的這個數據.所以我得自己實現Spring Cache中刪除緩存的那部分邏輯.而redis本身是沒有批量刪除key的功能的.
問題
所以現在的問題就是 如何在Spring的redisTemplate中使用lua腳本刪除key
解決辦法
我覺得可以這么做:
1.首先把之前文章寫的批量刪除的lua腳本放到maven項目的resources下.
2.然后寫一個Bean implements InitializingBean在啟動的時候加載這些腳本.
1 /** 2 * 加載批量lua腳本 3 * 4 * @throws Exception 5 */ 6 @Override 7 public void afterPropertiesSet() throws Exception { 8 loadDelScript(); 9 } 10 11 /** 12 * 加載批量刪除腳本 13 */ 14 private void loadDelScript() throws IOException { 15 String s = FileUtils.readFileToString(new ClassPathResource(CRedisCacheConstant.SCRIPT_PATH + "/" + "dels.lua").getFile(), Charset.forName("UTF-8")); 16 DefaultRedisScript<List> sc = new DefaultRedisScript<>(s, List.class); 17 scripts.put("dels", sc); 18 }
3.需要批量刪除的時候通過DefaultRedisScript去使用這個腳本
1 DefaultRedisScript<List> sc = scripts.get("dels"); 2 List<String> cache = (List<String>) redisTemplate.execute(sc, stringRedisSerializer, stringRedisSerializer, Collections.singletonList(wholeKey)); 3 log.info("刪除page緩存 {}", cache);
這樣就可以在Java代碼中使用lua腳本成功批量刪除緩存啦.
有些小朋友可能會問.這樣做每次都需要把腳本序列化傳給redis嗎?那腳本大了不是很占用網絡嗎?
其實並不會...
DefaultScriptExecutor中有一段代碼:
1 protected <T> T eval(RedisConnection connection, RedisScript<T> script, ReturnType returnType, int numKeys, 2 byte[][] keysAndArgs, RedisSerializer<T> resultSerializer) { 3 4 Object result; 5 try { 6 result = connection.evalSha(script.getSha1(), returnType, numKeys, keysAndArgs); 7 } catch (Exception e) { 8 9 if (!exceptionContainsNoScriptError(e)) { 10 throw e instanceof RuntimeException ? (RuntimeException) e : new RedisSystemException(e.getMessage(), e); 11 } 12 13 result = connection.eval(scriptBytes(script), returnType, numKeys, keysAndArgs); 14 } 15 16 if (script.getResultType() == null) { 17 return null; 18 } 19 20 return deserializeResult(resultSerializer, result); 21 }
會先計算這個腳本的sha1的值,通過redis的EVALSHA去允許腳本..如果失敗了,比如第一次沒有加載.就把腳本序列化傳過去執行...往后都通過這個sha1值直接調用.
127.0.0.1:6379> SCRIPT EXISTS f7cb6ede3d6d2e14b812f32f129633443197b42c 1) (integer) 1
小結
通過使用DefaultRedisScript可以比較方便的在java中使用lua腳本操作redis
