redis-zset分页查询去重组件
背景:
在工作中大量用到redis做数据存储,特别是zset结构非常适合做用户排行榜等包含权重的列表。假设一个场景:
- 实现一个用户排序列表
- 使用了zset结构存储这个列表,member元素为用户id,score分值为权重
- 分页查询:假设查询到第2页时,第1页数据发生变化,或者第1页有一条数据被顶到第二页,就可能产生重复数据
- 要解决分页数据重复的问题,一般有两种方案:
- 缓存快照,每个线程查询第一页时,缓存到快照,设定失效时间。保证这一次分页查询的生命周期内数据不会重复
- 已读缓存,每个线程查询第一页时,生成去重缓存(设定失效时间,下次请求第一页时也会失效),每返回一页数据时先根据已读缓存过滤一遍,再将新一页数据返回并添加到已读缓存。
- 下面针对已读缓存的方案,实现组件
引入依赖:
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
实现代码:
接口:
package com.cache.service;
import org.springframework.data.redis.core.ZSetOperations;
import java.util.List;
import java.util.Set;
/**
* redis排名列表操作接口
*/
public interface RedisZSetRankingService {
/**
* 查询有序列表(过滤重复数据)
* @param key zset key
* @param id 唯一id(用户id、当前线程id)
* @param lastScore 上一页的最后一个score值
* @param pageSize 查询页大小
* @param sort 排序方式 1:升序;-1:降序
* @param blackSet 需要过滤的黑名单列表
* @return 返回结果
*/
List<ZSetOperations.TypedTuple<Object>> findRankList(String key, Object id, Double lastScore, long pageSize, long sort, Set<Long> blackSet);
/**
* 查询有序列表根据上一页最后一个元素(过滤重复数据)
* @param key zset key
* @param id 唯一id(用户id、当前线程id)
* @param lastMember 上一页最后第一个元素
* @param pageSize 查询页大小
* @param sort 排序方式 1:升序;-1:降序
* @param blackSet 需要过滤的黑名单列表
* @return 返回结果
*/
List<ZSetOperations.TypedTuple<Object>> findRankListByLastMember(String key, Object id, Object lastMember, long pageSize, long sort, Set<Long> blackSet);
}
实现类:
package com.cache.service.impl;
import com.vdpub.cache.config.RedisZSetRankConfig;
import com.vdpub.cache.service.RedisZSetRankingService;
import com.vdpub.common.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import