工作经常会用到 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
}