zoukankan      html  css  js  c++  java
  • 在Spring中使用Redis Lua脚本批量删除缓存

    背景

      之前分享了一篇利用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

  • 相关阅读:
    行转列
    multipath 安装配置
    网卡绑定
    numa对MySQL多实例性能影响
    Fatal NI connect error 12170
    REVOKE DBA权限要小心
    Oracle 数据库整理表碎片
    listagg 函数
    10046 事件补充
    tkprof 解释
  • 原文地址:https://www.cnblogs.com/abcwt112/p/8397587.html
Copyright © 2011-2022 走看看