RedisSet缓存List数据并设置过期时间——Lua实现

RedisSet缓存List数据操作并设置过期时间—Lua脚本实现

工作中自己开发需要,现有的工具不能满足,于是自己定制了一套

注意:集群环境下Lua脚本还有pipeline是不支持在所有节点上执行的

批量缓存List内容数据,而不是将整个List作为一个value缓存,并且删除原有的数据,同时设置过期时间,

定义方法内容

    /**
     * @description 批量缓存List数据,并且删除原有的数据,同时设置过期时间
     * @param clazz List的数据泛型
     * @param duration 过期时间 单位/秒
     * @author Lutong Sun
     * @date 2020/11/25
     */
    public static <T> void listRightPushAllNewAndDeleteOldALL(List<String> keyList, Class<T> clazz, Long duration, List lists) {
        if (!CollectionUtils.isEmpty(lists)) {
            String scriptString = "local key = KEYS[1];\n" +
                    "local duration = tonumber(ARGV[1]);\n" +
                    "table.remove(ARGV, 1);\n" +
                    "redis.call(\"DEL\", key)\n" +  // 删除原有的数据 不需要可以删除这一行
                    "for i=1, #(ARGV) do\n" +
                    "\tredis.call(\"RPUSH\", key, ARGV[i]);\n" +
                    "end\n" +
                    "redis.call(\"EXPIRE\", key, duration)";
            RedisScript<String> redisScript = new DefaultRedisScript(scriptString, String.class);
            
            // 自定义脚本执行器,为了将所有arg作为一个数组处理
            redisTemplate.setScriptExecutor(new MultipleArgumentsScriptExecutor(redisTemplate));
            redisTemplate.execute(redisScript, new FastJsonRedisSerializer<>(clazz), new StringRedisSerializer(), keyList, duration, lists);
        }
    }

MultipleArgumentsScriptExecutor自定义Lua脚本执行器

import com.google.common.collect.Lists;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultScriptExecutor;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

public class MultipleArgumentsScriptExecutor extends DefaultScriptExecutor {
    /**
     * @param template The {@link RedisTemplate} to use
     */
    public MultipleArgumentsScriptExecutor(RedisTemplate template) {
        super(template);
    }

    @Override
    protected byte[][] keysAndArgs(RedisSerializer argsSerializer, List keys, Object[] args) {
        List<Object> argList = Lists.newArrayList();
        if (!Objects.isNull(args) && args.length > 0) {
            Arrays.stream(args)
                    .forEach(arg -> {
                        if (arg instanceof Collection) {
                            argList.addAll((Collection<?>) arg);
                        } else {
                            argList.add(arg);
                        }
                    });
        }
        return super.keysAndArgs(argsSerializer, keys, argList.toArray());
    }
}

使用方法

RedisUtils.listRightPushAllNewAndDeleteOldALL(
Lists.newArrayList(cacheKey), // cacheKey是Redis key
ProductBehaviourDomain.class, // 缓存数据类型
100l, // 过期周期 单位s
productBehaviourDomainList  // 要缓存的数据, 类型是List<ProductBehaviourDomain>
);

删除并返回对应下标的数据, 如果下标超过现有的最大下标则按现有最大下标操作—Lua脚本实现

定义方法内容

    /**
     * @description 删除并返回对应下标的数据, 如果下标超过现有的最大下标则按现有最大下标操作
     * @author Lutong Sun
     * @date 2020/11/25
     */
    public static <T> List<T> listRangeAndTrim(List<String> keyList,  Class<T> clazz, String...argvs)  {
        String scriptString = "local key = KEYS[1];\n" +
                "local fromIndex = tonumber(ARGV[1]);\n" +
                "local endIndex = tonumber(ARGV[2]);\n" +
                "local len = redis.call(\"LLEN\", key);\n" +
                "if len <= endIndex then\n" +
                "\tendIndex = len;\n" +
                "end\n" +
                "local array = redis.call(\"LRANGE\", key, fromIndex, endIndex)\n" +
                "redis.call(\"LTRIM\", key, endIndex+1, -1)\n" +
                "local result = \"[\";\n" +
                "for i=1, #(array) do\n" +
                "\tresult = result .. array[i] .. \",\";\n" +
                "end\n" +
                "string.sub(result, 1, string.len(result)-1);\n" +
                "result = result .. \"]\";\n" +
                "return result;";
        RedisScript<String> redisScript = new DefaultRedisScript(scriptString, String.class);
        String jsonString = (String)redisTemplate.execute(redisScript, new StringRedisSerializer(), new FastJsonRedisSerializer(String.class), keyList, argvs);
        return JSONObject.parseArray(jsonString, clazz);
    }

使用方法

List<ProductBehaviourDomain> productBehaviourDomainCacheList = RedisUtils.listRangeAndTrim(
Lists.newArrayList(cacheKey), // cacheKey是redis key
ProductBehaviourDomain.class, // 返回的数据元素类型
"0", // 要返回数据的起始下标 
"100" // 要返回数据的结束下标
//以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推, 返回数据是闭区间,就是0,100.会返回101个数据
);

缓存MAP同时设置过期时间—Lua脚本实现

定义方法内容

    public static void hmsetWithExpire(String key, Map<String, String> map, Long expire) {
        String scriptString =
                "local key = KEYS[1];\n" +
                        "local mapSize = tonumber(ARGV[1]);\n" +
                        "table.remove(ARGV, 1);\n" +
                        "local expire = tonumber(ARGV[1]);\n" +
                        "table.remove(ARGV, 1);\n" +
                        "for i=1, mapSize do\n" +
                        "\tlocal mapKey = string.sub(ARGV[i], 2, string.len(ARGV[i])-1);\n" +
                        "\tredis.call(\"HSET\", key, mapKey, ARGV[i+mapSize]);\n" +
                        "end\n" +
                        "redis.call(\"EXPIRE\", key, expire);";
        RedisScript<String> redisScript = new DefaultRedisScript(scriptString, String.class);
        redisTemplate.setScriptExecutor(new MultipleArgumentsScriptExecutor(redisTemplate));
        redisTemplate.execute(redisScript, Lists.newArrayList(key), map.size(), expire, map.keySet(), map.values());
    }

使用方法:

        Map<String, String> map = new HashMap<>();
        map.put("name1", "swk1");
        map.put("name2", "swk2");
//        RedisUtils.hmset("test", map);
        RedisUtils.hmsetWithExpire(
        cacheKey, // 缓存key
        map, // 缓存map
        2000l); // 过期时间 单位s

版权声明:本文为qq_15022971原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。