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