
一、深度分页是什么为什么会出现问题1.1 什么是深度分页查询耗时随页码深度指数级增长内存与 CPU 资源消耗剧增超过阈值后直接被 ES 拒绝。1.2 ES 分页的底层执行机制from sizeES 是分布式搜索引擎数据分布在多个分片上。标准分页from size的执行流程如下请求分发协调节点将查询广播至所有分片分片执行每个分片独立查询并返回前from size条记录到内存结果汇总协调节点收集所有分片的结果共N × (from size)条二次排序 裁剪全局排序后仅保留[from, from size)区间的数据返回。 核心痛点当from 10000,size 100时每个分片需加载10100 条数据协调节点需对所有分片的 10100 条进行全局排序——最终却只返回 100 条资源浪费极其严重。1.3 ES 的保护机制max_result_window为防止 OOMES 默认设置index.max_result_window10000即from size ≤ 10000否则抛出异常。示例每页 20 条 → 最多翻到第 500 页可通过以下方式修改不推荐随意调大PUT/your_index/_settings{index.max_result_window:20000}1.4 为什么不能简单调大max_result_window调大阈值只是 **“掩盖问题”**而非解决问题分片仍需加载大量数据到堆内存协调节点排序压力剧增极易触发 Full GC 或 OOM分布式环境下分片越多性能衰减越快。二、ES 三大分页方式深度对比维度from sizescrollAPIsearch_after性能❌ 深度翻页性能差⚠️ 中等快照开销✅高性能翻页能力✅ 支持随机跳页 ❌ 受限于 10000❌ 仅向后翻页 ✅ 支持全量遍历❌ 仅向后翻页 ✅无限深度实时性✅ 实时❌非实时快照✅近实时PIT 轻量视图资源占用❌ 高深度时⚠️ 中上下文驻留内存✅低ES 官方推荐基础场景❌ES7 已不推荐✅ES7.10 主推方案使用复杂度✅ 简单⚠️ 需管理scroll_id⚠️ 需管理PIT 排序值2.1from size基础但危险语法示例GET/index/_search{query:{match_all:{}},from:0,size:10}适用场景小数据集≤10000 条PC 端支持随机跳页的搜索如百度、京东后台管理系统。⚠️ 限制绝对不可用于深度分页超过max_result_window直接失败。2.2scrollAPI全量遍历的“老方案”核心原理首次查询创建 数据快照snapshot后续通过scroll_id获取下一批数据快照基于首次查询时刻后续写入不可见。使用流程首次查询带scroll参数GET/index/_search?scroll5m{query:{...},size:100}后续翻页POST/_search/scroll{scroll:5m,scroll_id:xxx}手动清理重要DELETE/_search/scroll{scroll_id:xxx}适用场景数据导出、迁移、批量处理不要求实时性的后台任务。❌ 缺陷非实时上下文长期驻留内存易造成资源泄漏ES 7 官方已不推荐用于分页。2.3search_after官方主推的深度分页方案 ✅核心优势基于游标cursor思想无深度限制使用Point In Time (PIT)保证一致性资源占用远低于scroll支持 近实时查询。关键要求必须指定排序字段**必须包含唯一决胜字段tiebreaker**如_id避免重复或跳过数据。使用流程创建 PITPoint In TimePOST/index/_pit?keep_alive5m→ 返回pit_id首次查询GET/_search{query:{term:{status:active}},pit:{id:pit_id,keep_alive:1m},size:20,sort:[{timestamp:asc},{_id:asc}// ← 唯一决胜字段]}后续翻页使用上一页最后一条的排序值GET/_search{query:{term:{status:active}},pit:{id:pit_id,keep_alive:5m},size:20,sort:[{timestamp:asc},{_id:asc}],search_after:[1678901234567,doc_999]// ← 上一页最后一条的值}释放 PIT可选但推荐DELETE/_pit{id:pit_id}适用场景APP 下拉加载更多天然向后翻页电商商品列表、内容流超 10000 条对性能和一致性有要求的 C 端业务。三、深度分页的根本解决策略✅ 策略 1产品设计上规避深度分页最优解最好的优化是不让问题发生。PC 端隐藏“跳转到第 N 页”仅保留“上一页/下一页”限制最大页数如淘宝、京东仅展示前 100 页移动端采用 无限下拉加载天然适配search_after。✅ 策略 2按业务场景精准选型业务场景推荐方案原因PC 搜索 / 后台管理需跳页from size支持随机跳页且控制在 10000 内数据导出 / 批量处理scroll全量遍历无需实时APP 下拉 / 超长列表search_after高性能、无限深度、近实时✅ 策略 3用search_after实现高性能深度分页若必须深度分页请严格遵循以下最佳实践排序字段必须含唯一决胜字段如_idPIT 过期时间合理设置如 1~5 分钟可在每次请求中续期单次size控制在 20~100平衡性能与请求次数查询结束后主动删除 PIT避免资源泄漏。✅ 策略 4集群级辅助优化合理分片避免过多分片建议 ≤ 节点数 × 2提升协调节点配置高内存 多核 CPU关闭非必要排序若无需排序可按_doc最快**使用路由routing**减少查询涉及的分片数。四、总结与核心建议 核心结论from size≠ 深度分页方案—— 仅适用于 ≤10000 条的随机翻页scroll已过时—— 仅用于非实时全量处理search_after是未来—— ES 7.10 官方主推性能与一致性兼得产品设计 技术调优—— 限制用户行为是最高效解法。 落地建议产品侧PC 限页数APP 用下拉开发侧封装search_after工具类统一处理 PIT 与游标运维侧监控堆内存、GC 频率防止 OOM架构侧新项目**直接放弃scroll**全面拥抱search_after。