场景:公司某个场景要求保留前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

Logo

更多推荐