利用Redis完成论坛帖子的分页加载
利用Redis完成论坛帖子的分页加载
核心思路
- 利用Redis的ZSet(SortedSet)和Hash做分页。
- SortedSet会给每个添加的元素member绑定一个用于排序的值score,SortedSet就会根据score值的大小对元素进行排序,在论坛的应用场景中可以将发布日期当作score用于排序。在RedisTemplate中提供了相关的函数来进行分页取出操作。详情见下。
- 在Hash里对应存储ID->帖子信息,在SortedSet中存储帖子ID,分页取出每个帖子ID后在Hash中直接根据ID找到对应的帖子信息,返回数组即可。
实现操作
- 设置定时任务每天在访问低峰期更新数据。
- 使用@EnableScheduling开启基于注解的定时任务
- 使用@Scheduled搭配cron表达式设置时间
- 加载1000条帖子到本地,用getTime()取到Long类型的时间数据作为排序依据,用postID作值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14//每天凌晨四点更新缓存,把数据库里前1000条更新到缓存里。(没人能这么闲看完这么多帖子吧)
List<CommunityPostVo> communityPostVo = postMapper.loadPostWithPage(0,999);
Map<String, Object> postMap = new HashMap<>();
Set<ZSetOperations.TypedTuple<Integer>> tempSet = new HashSet<>();
for (CommunityPostVo postVo : communityPostVo) {
//先处理图片
if (!Objects.isNull(postVo.getImageUrl()))
postVo.setImageUrlList(postVo.getImageUrl().split(","));
//根据帖子发布时间进行排序
ZSetOperations.TypedTuple<Integer> temp = new DefaultTypedTuple<>(postVo.getPostID(),Double.valueOf(postVo.getPostDate().getTime()));
tempSet.add(temp);
//方便根据帖子ID查找对应的帖子信息
postMap.put(postVo.getPostID().toString(),postVo);
} - 将数据存入Redis
1
2
3
4redisTemplate.opsForZSet().add("postSet", tempSet);
redisTemplate.opsForHash().putAll("postMap", postMap);
redisTemplate.expire("postMap",1500, TimeUnit.MINUTES);
redisTemplate.expire("postSet",1501, TimeUnit.MINUTES); - 在业务层根据传入的页面大小(pageSize)和当前页(curPage)取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public Map<String ,Object> loadPost(Integer curPage, Integer pageSize) {
//根据页号加载帖子
Integer left = (curPage - 1) * pageSize;
Integer right = curPage * pageSize - 1;
log.info("加载数据 {}-{}",left,right);
Map<String ,Object> result = new HashMap<>();
//帖子数据是否加载完毕
Boolean isEnd = false;
try{
Set<Integer> postSet = redisTemplate.opsForZSet().reverseRange("postSet", left, right);
Map<String ,Object> postMap = redisTemplate.opsForHash().entries("postMap");
List<CommunityPostVo> postVos = new ArrayList<>();
for (Integer integer : postSet) {
postVos.add((CommunityPostVo) postMap.get(integer.toString()));
}
//如果Redis里的数据加载到底了,就返回已全部加载(目前只允许浏览前1000条)
if (postSet.size() < (right - left)) {
isEnd = true;
}
result.put("postList", postVos);
result.put("isEnd", isEnd);
}catch (Exception e){
log.error("帖子数据分页加载出现错误:");
e.printStackTrace();
return null;
}
// IPage<Post> iPage = postMapper.selectPa;
return result;
}
总结
- 减少了对数据库的访问压力,但是要注意数据同步问题,以及一开始的缓存加载多少条、缓存访问完了之后怎么办需要定义其他函数解决。
- 还要尽量减少对Redis的读写操作,数据加载到本地再进行筛选。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Katashi的博客!