为什么要使用ZSET? Redis常用的存储多个数据的数据结构对比
List
Set
SortedSet(ZSET)
排序方式
按添加顺序排序
无法排序
根据score值排序
唯一性
不唯一
唯一
唯一
查找方式
按索引或首位查找
根据元素查找
根据元素查找
查找性能
差
好
好
需求分析
需要实现快速检索:判断当前用户是否已经点过赞
需要实现排序:实现topN
很显然,我们要选择ZSET
如何实现排序? 我们需要按照用户点赞时间的先后顺序,取出前n个用户,并在数据库中查找相关信息。 所以,我们可以把时间戳作为score
来存入ZSET
初步代码实现 逻辑很简单,先取出ZSET中前n个数据,然后在数据库中完成查询即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public List<UserDTO> queryByLikes (Long id) { String key = RedisConstants.BLOG_LIKED_KEY + id; Set<String> top5 = redisTemplate.opsForZSet().range(key, 0 , 4 ); if (top5 == null || top5.isEmpty()) { return Collections.emptyList(); } List<Long> ids = top5.stream().map(Long::valueOf).toList(); List<UserDTO> userDTOS = userService.listByIds(ids) .stream() .map(user -> BeanUtil.copyProperties(user, UserDTO.class)) .toList(); return userDTOS; }
但是,这么写有个问题 MyBatisPlus的listByIds
方法,执行的Sql语句是select * from tb where id in (id1, id2, id3 ....)
,这样的话,得到的结果是MySql数据库的默认顺序 ,我们想要的是排序后的topN,而不是无序的。那么,该怎么办呢? 我们可以用Sql语句select * from tb where id in (id1, id2, id3...) order by field(id3, id2, id1..)
,field
里的内容是我们希望得到的顺序,于是,我们有了这样的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Override public List<UserDTO> queryByLikes (Long id) { String key = RedisConstants.BLOG_LIKED_KEY + id; Set<String> top5 = redisTemplate.opsForZSet().range(key, 0 , 4 ); if (top5 == null || top5.isEmpty()) { return Collections.emptyList(); } List<Long> ids = top5.stream().map(Long::valueOf).toList(); String idStr = StrUtil.join("," , ids); List<UserDTO> userDTOS = userService.lambdaQuery() .in(User::getId, ids) .last("order by field(id, " + idStr + ")" ).list() .stream() .map(user -> BeanUtil.copyProperties(user, UserDTO.class)) .toList(); return userDTOS; }
于是,我们的topN功能就实现啦!