之前已经完成了redis的配置和常用工具类的编写redis配置以及相关工具类
本文介绍如何在具体业务中使用。
缓存的使用方式通常分为两种:Cache-Aside(旁路缓存)和Cache-Through(缓存即数据源)。简而言之,前者通常用于读取操作,而后者则适用于读写操作。
盘路缓存
对于读,首先从缓存读取数据,如果没有命中则回源数据库读取并更新缓存。对于写操作,先写 数据库,再写缓存。
逻辑代码:
//读操作
data = Cache.get(key);
if(data == NULL)
{
data = SoR.load(key);
Cache.set(key, data);
}
//写操作
if(SoR.save(key, data))
{
Cache.set(key, data);
}
在spring中提供了好用的盘路缓存框架spring-cache。只需要写load逻辑。加上个注解,就能实现盘路缓存的效果。
这种缓存仍然有个缺陷,在极端情况下。我需要获取一批帖子的信息,碰巧所有的帖子缓存都失效了,都需全部重新加载。
for (Long postId : postIdList) {
Post post= postCache.getById(postId);
post.add(post);
}
那么这样一个本来性能很高的循环,就等同于全部查了数据库。
批量缓存查询
对于这种批量查询缓存的需求,传统的旁路缓存框架无法达到我们的需求。我们需要让他能够批量的get or load。
批量get发现没有的数据,再批量的load一次,这样和redis以及数据库的交互都只会有一次。
@Autowired
private PostService postService;
@Cacheable(cacheNames = "post", key = "#postId")
public Post getById(Long postId){
return postService.getById(postId);
}
private static final String BASE_KEY = "base:";
private static final String POST_INFO_STRING = "post:info:";
/**
* 获取帖子信息,采用Redis缓存模式
*/
public Map<Long, Post> getPostInfoBatch(Set<Long> postIds) {
// 批量组装key
List<String> keys = postIds.stream().map(a -> String.format(BASE_KEY, POST_INFO_STRING, a)).collect(Collectors.toList());
// 批量get
List<Post> mget = RedisUtils.mget(keys, Post.class);
Map<Long, Post> map = mget.stream().filter(Objects::nonNull).collect(Collectors.toMap(Post::getId, Function.identity()));
// 发现差集——还需要从数据库中加载的postId
List<Long> needLoadPostIdList = postIds.stream().filter(a -> !map.containsKey(a)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(needLoadPostIdList)) {
// 批量load
List<Post> needLoadPostList = postService.listByIds(needLoadPostIdList);
Map<String, Post> redisMap = needLoadPostList.stream().collect(Collectors.toMap(a -> String.format(BASE_KEY, POST_INFO_STRING, a), Function.identity()));
// 将数据批量存入Redis,并设置过期时间(这里假设为5分钟)
RedisUtils.mset(redisMap, 5 * 60);
//加载回redis
map.putAll(needLoadPostList.stream().collect(Collectors.toMap(Post::getId, Function.identity())));
}
return map;
}
RedisUtils
参考前文redis配置以及相关工具类
总结
通过本文的剖析,我们深入了解了缓存的常用使用方式,并探讨了传统旁路缓存框架的局限性。鉴于这些不足,实现了新的批量缓存框架,旨在提供更高效、更可靠的缓存解决方案。