15 redis如何做分页查询

vvEcho 2024-02-20 23:23:55
Categories: Tags:

1.使用redis的list(列表)数据结构

将需要分页展示的数据的id或者主键作为list中的元素,然后根据用户的分页请求,
使用LRANGE命令来获取指定范围的元素,再根据元素来获取具体的数据

1
2
3
4
5
6
7
8
9
10
11
@Service
public class RedisPaginationService {
@Autowired
private RedisTemplate<String, String> redisTemplate;

public List<String> getPaginatedData(String key, int pageNumber, int pageSize) {
long start = (pageNumber - 1) * pageSize;
long end = start + pageSize - 1;
return redisTemplate.opsForList().range(key, start, end);
}
}

2.使用redis的ZSet(有序集合)数据结构

将需要分页展示的数据的id或者主键作为ZSet中的value,将数据的排序依据(比如时间、热度、评分等)作为ZSet中的score,然后根据用户的分页请求,
使用ZRANGE或者ZREVRANGE命令来获取指定范围的value,再根据value来获取具体的数据

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
public PageResult<Article> getArticlesByPage(Long userId, Long lastScore, Integer offset, int pageSize) {
String cacheKey = "user:articles:" + userId;
ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();

// 分页查询(倒序)
Set<ZSetOperations.TypedTuple<String>> tuples = zSetOps.reverseRangeByScoreWithScores(
cacheKey, 0, lastScore, offset, pageSize
);

if (tuples == null || tuples.isEmpty()) {
// 缓存无数据,触发重载逻辑(参考网页1)
return reloadFromDatabase(userId, pageSize);
}

// 解析数据与偏移量
List<Article> articles = new ArrayList<>();
long minScore = 0;
int newOffset = 1;
for (ZSetOperations.TypedTuple<String> tuple : tuples) {
Article article = deserialize(tuple.getValue());
articles.add(article);
long score = tuple.getScore().longValue();
if (score == minScore) {
newOffset++; // 统计相同score的数量(网页4)
} else {
minScore = score;
newOffset = 1;
}
}

return new PageResult<>(articles, minScore, newOffset);
}

3.使用redis的hash(哈希)数据结构

将需要分页展示的数据的id或者主键作为hash中的field,将数据的排序依据(比如时间、热度、评分等)作为hash中的value,然后根据用户的分页请求,
使用HSCAN命令来获取指定范围的field和value,再根据field来获取具体的数据。

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
@Autowired
private RedisTemplate<String, Object> redisTemplate;

/**
* 添加数据到ZSet和Hash
*/
public void addData(String hashKey, String itemId, Object data, double score) {
// 存储到Hash
redisTemplate.opsForHash().put(hashKey, itemId, data);
// 维护ZSet有序键
redisTemplate.opsForZSet().add(hashKey + ":index", itemId, score);
}

/**
* 分页查询
*/
public List<Object> getPage(String hashKey, int page, int pageSize) {
String zsetKey = hashKey + ":index";
// 计算分页范围
long start = (page - 1) * (long) pageSize;
long end = start + pageSize - 1;

// 从ZSet获取分页键列表
Set<String> itemIds = redisTemplate.opsForZSet().range(zsetKey, start, end);
if (itemIds == null) return Collections.emptyList();

// 从Hash批量获取数据
return redisTemplate.opsForHash().multiGet(hashKey, itemIds);
}

4.扩展ZREVRANGE

ZREVRANGE命令用于返回有序集合中,指定区间内的成员,其中成员的位置按分数值递减(从大到小)来排列。具有相同分数值的成员按字典序的逆序(reverse lexicographical order)排列。

ZREVRANGE命令的基本语法如下:

1
redis> ZREVRANGE key start stop [WITHSCORES]

具体可以参考博客:
如何使用redis实现高效的分页功能