海量数据分页方案对比
LIMIT 1000000, 20需要扫描 100 万行再丢弃,这就是 OFFSET 分页越往后越慢的根本原因。
一、OFFSET 分页的问题
- 1.1 原理——
LIMIT offset, count→ 扫描 offset + count 行 - 1.2 offset=0:扫描 20 行
- 1.3 offset=1000000:扫描 1000020 行
- 1.4 为什么不能跳过前面的行——B+Tree 有序但非跳跃
二、方案 1:游标分页(推荐)
- 2.1 原理——
WHERE id > last_id ORDER BY id LIMIT 20 - 2.2 使用最后一条记录的 ID 作为游标
- 2.3 优点——每次只扫描 20 行,效率恒定
- 2.4 缺点——不能跳页
- 2.5 适用场景——无限滚动、App 端
三、方案 2:ID 范围分页
- 3.1 先查询 ID 范围——
SELECT id FROM order WHERE ... LIMIT 100000, 20 - 3.2 再查询详情——
SELECT * FROM order WHERE id IN (...) - 3.3 优化点——覆盖索引 + 延迟关联
四、方案 3:ES 的 search_after
- 4.1 ES 的深分页问题——
from + size上限 10000 - 4.2
search_after替代from - 4.3 原理——类似于游标分页
五、方案 4:预计算总页数
- 5.1 不要每次查询 COUNT——用 Redis 缓存
- 5.2 近似计数——HyperLogLog
六、选型建议
- 管理后台需要跳页 → ID 范围分页
- App/Feed 流无限滚动 → 游标分页
- 搜索场景 → ES search_after
七、总结
- OFFSET 分页是反模式,应尽量避免大 offset
- 游标分页是最佳实践
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 谜思录!