今天做一个小项目要用到redis查询,遇到一个很奇怪的现象:
使用redis缓存一个list,然而我发现每次缓存完后,第二次获取是正常的,第三次的时候缓存就又丢了,如此反复。
代码如下:
public List<ItemCat> selectAllByStatus(int status) {
List<ItemCat> itemCats = null;
try {
//从redis中获取存放分类信息的缓存
itemCats = (List<ItemCat>) redisTemplate.opsForList().leftPop("itemCats",0,0);
//没有缓存,从数据库中获取并存入缓存
if(itemCats==null || itemCats.isEmpty()){
itemCats = itemCatMapper.selectAllByStatus(status);
redisTemplate.opsForList().leftPush("itemCats",itemCats);
}
} catch (Exception e) {
//连接服务器有异常
itemCats = itemCatMapper.selectAllByStatus(status);
e.printStackTrace();
} finally {
return itemCats;
}
}
我越看越奇怪,虽然是spring提供的redis工具,但是leftPop应该和原生指令是一致的也即是:LPop指令
LPOP key
移除并返回列表 key 的头元素。
可用版本:= 1.0.0
时间复杂度:
O(1)
返回值:
列表的头元素。
当 key 不存在时,返回 nil 。
pop是弹出操作,弹出后回删此元素,也就解释了为什么会一次有缓存一次没有缓存的问题。
解决思路:查阅了redis的api,想起来获取而不删除的命令应该是range,再去翻阅spring RedisTemple的api文档,果然有range方法。
下面是来自didier-spezia的回答
You can easily peek at an item rather than popping it by using the range command.
With Spring, from a RedisTemplate instance, you can get a ListOperations instance by using the opsForList() method, and then:
listOp.range(key, 0, 0) will return the first (left) item without popping it
listOp.range(key, -1, -1) will return the last (right) item without popping it
See documentation at:
http://static.springsource.org/spring-data/data-keyvalue/docs/1.0.x/api/org/springframework/data/keyvalue/redis/core/RedisTemplate.html http://static.springsource.org/spring-data/data-keyvalue/docs/1.0.x/api/org/springframework/data/keyvalue/redis/core/ListOperations.html
大致意思是,如果想根据key获取某个元素,应该是使用 listOp.range(key, 0, 0)
方法或者 listOp.range(key, -1, -1)
,主要区别是一个是从第一个(左边)还是从最后一个(右边)位置来获取。
//将leftPop改为range方法就欧克
itemCats = (List<ItemCat>) redisTemplate.opsForList().range("itemCats",0,0);
2020-11-7更新:经测试后发现range方法把list封装过一次,导致前端有问题,新的解决思路是:使用index方法!!
//从redis中获取存放分类信息的缓存 要用index方法!!!!
itemCats = (List<ItemCat>) redisTemplate.opsForList().index("itemCats",-1);