Redis + Lua 实现 sorted set 集合保证固定数量的数据,并保留新数据剔除旧数据
场景:公司某个场景要求保留前20个最新的数据,用作查询展示,用MySQL每次频繁查询太耗费性能,所以用Redis处理一下。直接上代码:local key = KEYS[1] -- 定义从参数中获取的keylocal value = ARGV[1] -- 定义从参数中获取的valuelocal maxLen= tonumber(ARGV[2]) -- 定义从参数中获取的最大长度local resul
·
场景:公司某个场景要求保留前20个最新的数据,用作查询展示,用MySQL每次频繁查询太耗费性能,所以用Redis处理一下。
直接上代码:
local key = KEYS[1] -- 定义从参数中获取的key
local value = ARGV[1] -- 定义从参数中获取的value
local maxLen= tonumber(ARGV[2]) -- 定义从参数中获取的最大长度
local result = redis.call("ZCARD", key) -- 使用ZCARD获取key对应的集合最大长度
local lastScore = result + 1 -- 累加本次保存数据的分数排序
if(result >= maxLen) -- 如果result已经超出最大长度,则进去裁剪程序
then
local zrangeData = redis.call("ZRANGE", key , -1 , -1 , "WITHSCORES")[2] -- 获取集合中最后一个数据的分数,也就是最大分时
lastScore = zrangeData + 1 -- 本次保存数据的分数是最大分 + 1
redis.call("ZREMRANGEBYRANK", key , 0 , result - maxLen) -- 裁剪掉集合前面超出maxLen的数据,也就是旧数据
end
redis.call("ZADD", key , lastScore , value) -- 将本次数据保存到集合中
下面根据我的业务场景,我其实是把 score 分数根据传入的时间戳作为值得,而不是上面的数字累加,因为时间戳放进来天然就能根据大小进行排序,不需要咱们自己计算一次了,上代码,自己选择合适的场景使用:
local key = KEYS[1] -- 定义从参数中获取的key
local value = ARGV[1] -- 定义从参数中获取的value
local maxLen = tonumber(ARGV[2]) -- 定义从参数中获取的最大长度
local score = tonumber(ARGV[3]) -- 使用ZCARD获取key对应的集合最大长度
redis.call("ZADD", key , score , value) -- 这里不太一样,直接把本次的数据放入进来了
local result = redis.call("ZCARD", key) -- 然后直接获取集合的长度
if(result > maxLen) -- 超过直接裁剪,因为时间戳放进去后,直接就排好序了,放心裁剪
then
redis.call("ZREMRANGEBYRANK", key , 0 , result - maxLen) -- 裁剪掉集合前面超出maxLen的数据,也就是旧数据
end
使用Java代码调用脚本,lettuce版本(其他语言或者客户端自行搜索一下如何调用lua脚本):
public class Application {
public static void main(String[] args) {
RedisURI redisUri = RedisURI.builder() // <1> 创建单机连接的连接信息
.withHost("10.0.0.155")
.withPort(6379)
.withDatabase(0)
.withTimeout(Duration.of(10, ChronoUnit.SECONDS))
.build();
RedisClient redisClient = RedisClient.create(redisUri); // <2> 创建客户端
StatefulRedisConnection<String, String> connection = redisClient.connect(); // <3> 创建线程安全的连接
RedisCommands<String, String> redisCommands = connection.sync(); // <4> 创建同步命令
// 循环一百次,可以验证是否保留了最大长度
for (int i = 1; i <= 100; i++) {
// key
String[] keys = new String[]{"imKey"};
// value 、 最大长度 、时间戳
String[] values = new String[]{"imValue" + i , "20",new Date().getTime() + ""};
String result = redisCommands.eval("\n" +
"local key = KEYS[1]\n" +
"local value = ARGV[1]\n" +
"local maxLen = tonumber(ARGV[2])\n" +
"local score = tonumber(ARGV[3])\n" +
"redis.call(\"ZADD\", key , score , value)\n" +
"local result = redis.call(\"ZCARD\", key)\n" +
"if(result > maxLen) \n" +
"then\n" +
"\tredis.call(\"ZREMRANGEBYRANK\", key , 0 , result - maxLen)\n" +
"end\n", ScriptOutputType.VALUE, keys, values);
System.out.println(result);
}
connection.close(); // <5> 关闭连接
redisClient.shutdown(); // <6> 关闭客户端
}
}
结果满足我们的需求。
最后注意一下,程序中取出这个集合后,根据情况判断是否要倒序返回,因为这个集合越往后的数据,才是越新的。
欢迎指正,谢谢。
demo已上传至:https://github.com/qiaomengnan16/spring-boot-rest-redis-lua
更多推荐
已为社区贡献2条内容
所有评论(0)