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

该博客介绍了如何使用Redis的Lua脚本进行高效的数据缓存操作,包括批量缓存List数据并删除原有数据、删除并返回指定范围的数据以及缓存Map的同时设置过期时间。文中提供了自定义的Lua脚本执行器及具体使用示例,适用于需要在集群环境中进行定制化缓存管理的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
在Java中,使用Spring Data Redis的`RedisTemplate`操作Redis时,可以利用Lua脚本实现批量设置键值对且同时设置它们的过期时间Lua脚本通常比纯命令行更高效,因为它可以在服务器端执行,减少网络往返次数。 首先,你需要创建一个Lua脚本,例如: ```lua local keys = {...} -- 需要设置键的列表 local values = {...} -- 对应的值的列表 for i, key in ipairs(keys) do redis.call('set', key, values[i], 'EX', expireTime) -- 设置键值,指定过期时间expireTime) end return true ``` 这里假设`keys`和`values`是两个相等长度的数组,分别表示需要设置的键名和对应的值。`EX`关键字用于设置过期时间,单位是秒。 然后,在Java代码中,你可以这样做: ```java List<String> keys = Arrays.asList("key1", "key2", ...); List<String> values = Arrays.asList("value1", "value2", ...); long expireTime = 60; // 过期时间为60秒 String luaScript = "...\n" + // 将上面的Lua脚本拼接到一起 "return true"; List<Object> keysAndArgs = new ArrayList<>(); keysAndArgs.addAll(keys); keysAndArgs.addAll(values); keysAndArgs.add(expireTime); Object result = redisTemplate.execute((RedisCallback<Object>) connection -> { return connection.evalsha(luaScript, keys.size(), keysAndArgs.toArray()); }, keys.get(0)); // 使用第一个键作为触发执行的键 // 检查结果是否为true,表示脚本执行成功 if (result == Boolean.TRUE) { System.out.println("设置过期时间完成"); } else { System.err.println("Lua脚本执行失败"); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值