Redis 维持一个固定大小的 list

本文将介绍怎样使用 Spring Data Redis 来维持一个固定大小的 list。

工作经常会用到 redis 的 list,并且要求 list 保留最新的 n 条数据的问题。下面提供一些解决方案供大家参考:

解决方案 1.0

每次添加新元素后,都判断此缓存中的元素个数,如果大于 n 个,则删除多余元素。示例代码:

/**
 * 从 list 的左边 push 一个元素,且将 list 的大小维护在 expectSize 大小
 * @param key list 的 key
 * @param value 元素
 * @param expectSize 期望 list 的大小
 */
public void leftPushForLimit(String key, String value, int expectSize) {
    if(expectSize <= 0) {
        throw new IllegalArgumentException("list 期望大小必须大于0");
    }

    BoundListOperations<String,String> ops = redisTemplate.boundListOps(key);
    // 左边push一个元素到list
    ops.leftPush(value);

    // 查看list大小
    Long size = ops.size();
    if(null != size && size > expectSize) {
        // 如果list大于期望大小,进行list修剪
        ops.trim(0, expectSize - 1);
    }
}

测试代码:

@Test
public void test() {
    for(int i = 1; i <= 15; i++) {
        leftPushForLimit("myList", "value-" + i, 10);
    }

    BoundListOperations<String,String> ops = redisTemplate.boundListOps("myList");
    List<String> list = ops.range(0, -1);
    if(null != list) {
        for (String str : list) {
            System.out.println(str);
        }
    }
    // 结果:
    // value-15
    // value-14
    // value-13
    // value-12
    // value-11
    // value-10
    // value-9
    // value-8
    // value-7
    // value-6
}

解决方案 2.0

在解决方案 1.0 中,需要与 Redis 通讯至少 2 次(添加元素、判断元素,元素个数不大于 n),至多 3 次(添加元素、判断元素,且元素个数大于 n、删除多余元素)。

当然我们可以用 Redis 事务把前两个 Redis 操作合成一次通讯,示例代码:

/**
 * 从 list 的左边 push 一个元素,且将 list 的大小维护在 expectSize 大小
 * @param key list 的 key
 * @param value 元素
 * @param expectSize 期望 list 的大小
 */
public void leftPushForLimit(String key, String value, int expectSize) {
    if(expectSize <= 0) {
        throw new IllegalArgumentException("list 期望大小必须大于0");
    }

    // 开启事务
    redisTemplate.multi();
    BoundListOperations<String,String> ops = redisTemplate.boundListOps(key);
    // 左边push一个元素到list
    ops.leftPush(value);
    // 查看list大小
    ops.size();
    List<Object> resultList = redisTemplate.exec();

    Long size = (Long) resultList.get(1);
    if(null != size && size > expectSize) {
        // 如果list大于期望大小,进行list修剪
        ops.trim(0, expectSize - 1);
    }
}

测试代码:

@Test
public void test() {
    for(int i = 1; i <= 15; i++) {
        leftPushForLimit("myList", "value-" + i, 10);
    }

    BoundListOperations<String,String> ops = redisTemplate.boundListOps("myList");
    List<String> list = ops.range(0, -1);
    if(null != list) {
        for (String str : list) {
            System.out.println(str);
        }
    }
    // 结果:
    // value-15
    // value-14
    // value-13
    // value-12
    // value-11
    // value-10
    // value-9
    // value-8
    // value-7
    // value-6
}

解决方案 3.0

在解决方案 2.0 中,通信次数最多时还有可能是 2 次(添加和判断个数、删除多余元素)。能不能只进行一次通讯呢?答案是肯定的,使用 redis 的 ltrim 命令去实现,示例代码:

/**
 * 从 list 的左边 push 一个元素,且将 list 的大小维护在 expectSize 大小
 * @param key list 的 key
 * @param value 元素
 * @param expectSize 期望 list 的大小
 */
public void leftPushForLimit(String key, String value, int expectSize) {
    if(expectSize <= 0) {
        throw new IllegalArgumentException("list 期望大小必须大于0");
    }

    // 开启事务
    redisTemplate.multi();
    BoundListOperations<String,String> ops = redisTemplate.boundListOps(key);
    // 左边push一个元素到list
    ops.leftPush(value);
    // 修剪list大小
    ops.trim(0, expectSize - 1);
    // 提交事务
    redisTemplate.exec();
}

测试代码:

@Test
public void test() {
    for(int i = 1; i <= 15; i++) {
        leftPushForLimit("myList", "value-" + i, 10);
    }

    BoundListOperations<String,String> ops = redisTemplate.boundListOps("myList");
    List<String> list = ops.range(0, -1);
    if(null != list) {
        for (String str : list) {
            System.out.println(str);
        }
    }
    // 结果:
    // value-15
    // value-14
    // value-13
    // value-12
    // value-11
    // value-10
    // value-9
    // value-8
    // value-7
    // value-6
}
学习从来无捷径,循序渐进登高峰。 —— 高永祚
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号