工作经常会用到 redis 的 list,并且要求 list 保留最新的 n 条数据的问题。下面提供一些解决方案供大家参考:
每次添加新元素后,都判断此缓存中的元素个数,如果大于 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 }
在解决方案 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 }
在解决方案 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 }