这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
分页优化
分页的性能问题
排行榜上线后,用户反馈遇到了新问题:
【用户反馈】用户ID: 5678
问题:查看第 100 页时,加载非常慢
【用户反馈】用户ID: 1234
问题:排行榜有 10000 篇文章,但只能翻到第 500 页
【监控数据】
第 1 页:响应时间 50ms
第 10 页:响应时间 200ms
第 50 页:响应时间 2s
第 100 页:响应时间 8s
第 500 页:响应时间 30s+
问题:深分页性能差。
问题分析
传统分页的问题
性能问题:
ZREVRANGE 时间复杂度:O(log N + M)
N: 集合总大小
M: 返回元素数量
第 1 页:
- log N + M ≈ log 10000 + 20 ≈ 14 + 20 = 34
第 100 页:
- log N + M ≈ log 10000 + 20 ≈ 34
- 但需要先找到第 1980 个元素
- 实际复杂度更高
问题:
- Redis 仍需遍历到起始位置
- 深分页慢
- 大数据量时性能差
数据库分页的问题
如果使用数据库分页:
数据设计要点
- 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
性能问题:
OFFSET 的问题:
- 需要扫描前 1980 条记录
- 然后返回后 20 条
- 扫描的数据白白浪费
第 1 页:扫描 20 条
第 100 页:扫描 2000 条
第 500 页:扫描 10000 条
性能随页数增长而下降
优化方案
方案 1:限制最大页数
优点:
- 实现简单
- 性能可控
缺点:
- 用户无法查看所有数据
- 可能影响用户体验
方案 2:游标分页(推荐)
优点:
- 性能稳定(每次查询都是 O(log N + M))
- 支持无限翻页
- 用户体验好
缺点:
- 不能跳到指定页
- 需要前端配合
方案 3:双向游标
方案 4:分片缓存
深分页优化
问题:数据库深分页
如果排行榜数据在数据库中,如何优化深分页?
数据设计要点
- 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
方案:使用覆盖索引
数据设计要点
- 索引服务于高频查询,重点是缩小扫描范围,而不是堆更多字段。
- 查询目标是快速定位状态、任务或资源,避免在关键路径上做大范围扫描。
性能对比:
传统方式:
- 扫描行数:2020 行
- 执行时间:约 500ms
覆盖索引方式:
- 扫描行数:20 行(只查索引)
- 执行时间:约 50ms
- 提升约 10 倍
前端优化
虚拟滚动
无限加载
性能对比
不同分页方案对比
场景:10,000 篇文章,查看第 100 页
传统 OFFSET 分页:
- 响应时间:2.5s
- 数据库扫描:2000 行
- 用户体验:差
游标分页:
- 响应时间:50ms
- 数据库扫描:20 行
- 用户体验:好
- 限制:不能跳页
分片缓存:
- 响应时间:10ms
- 数据库扫描:0 行(命中缓存)
- 用户体验:很好
- 限制:需要预热
推荐方案
建议:游标分页 + 分片缓存
前端:
- 使用游标分页
- 支持无限滚动
- 用户体验好
后端:
- 热门页面使用缓存
- 冷门页面使用游标查询
- 性能稳定
课后练习
练习 1
如何实现”快速跳转”功能(跳转到指定页)?
参考答案(3 个标签)
分页性能优化Redis
方案:使用 ZRANGEBYSCORE
练习 2
如何实现”每页大小可变”的分页?
参考答案(3 个标签)
分页灵活设计用户体验
方案:使用游标,支持动态页面大小
练习 3
如何处理”排名并列”的情况?
参考答案(3 个标签)
排行榜并列排名数据处理
方案:相同分数的处理
练习 4
如何实现”分页缓存”的自动刷新?
参考答案(3 个标签)
缓存自动刷新定时任务
方案:定时刷新缓存
练习 5
如何实现”分页预加载”?
参考答案(3 个标签)
分页预加载性能优化
方案:预加载下一页
思考题
-
如何实现”分页导出”功能(导出 Excel)?
-
如何处理”分页数据实时变化”的问题?
-
如何实现”多维度排序”的分页(按时间、按热度等)?
💡 提示:这些问题没有标准答案,建议结合实际情况深入思考。
