
1. ElasticSearch分页的痛点与常见方案当你用ElasticSearch处理超过1万条数据的分页查询时是不是经常遇到这样的报错Result window is too large, from size must be less than or equal to: [10000]...这其实是ES的自我保护机制在起作用。传统分页方式就像在图书馆找书——要拿第100本书得先把前99本都搬到面前数一遍效率极低。我处理过的一个电商项目就踩过这个坑。商品评论表超过50万条数据用户翻到第50页时系统直接崩溃。后来我们测试发现使用fromsize查询第100页每页20条时ES实际处理了2000条数据100页×20条而集群有5个分片相当于每个分片都要处理2000条数据协调节点最终要处理10000条数据目前ES官方提供的分页方案主要有三种fromsize最直观但性能最差适合浅分页100页scroll适合离线导出全部数据但会占用大量资源search_after实时性强适合深度分页但无法跳页// 典型的fromsize分页查询 GET /products/_search { from: 10000, size: 20, query: { match_all: {} } }2. search_after原理解析与实战search_after的工作原理就像书签——记住当前页最后一条记录的位置下次直接从这个位置继续读取。我去年优化过一个日志分析系统使用search_after后查询10万条数据的耗时从12秒降到了1.8秒。核心实现步骤必须指定至少一个唯一字段排序如_id或时间戳ID组合首次查询获取第一页数据和sort值将最后一条记录的sort值作为search_after参数查询下一页// 首次查询 GET /logs/_search { size: 100, sort: [ {timestamp: desc}, {_id: asc} ], query: { range: { timestamp: { gte: now-1d/d } } } } // 后续查询使用上页最后记录的sort值 GET /logs/_search { size: 100, search_after: [1651726858000, abcd1234], sort: [ {timestamp: desc}, {_id: asc} ] }实际项目中我发现几个关键点排序字段组合必须能唯一确定文档时间戳ID最保险查询期间如果新增数据可能导致结果微调新增数据可能插入到已读页建议配合PITPoint In Time使用保证查询一致性3. 伪分页的巧妙实现方案当产品经理坚持要跳转到指定页码时search_after就无能为力了。这时可以结合伪分页策略——通过条件过滤模拟跳页效果。我在金融风控系统中就采用这种方案实现了10万数据的灵活分页。实现原理需要有一个自增且唯一的字段如订单ID计算目标页的起始ID范围用range查询限定数据范围假设每页200条数据要跳转到第30页GET /orders/_search { query: { bool: { must: [ {range: {order_id: {gt: 5800}}}, {term: {status: paid}} ] } }, size: 200, sort: [{order_id: asc}] }这种方案的局限性也很明显必须知道每页的边界值需要额外存储总页数计算不准确需动态调整排序方式受限最好用ID或时间排序4. 混合策略的最佳实践经过多个项目验证我总结出一套混合方案默认用search_after滚动关键页码用伪分页跳转。具体实现如下4.1 系统架构设计前端维护当前页的sort值数组后端缓存热门页码的边界值如每100页记录边界ID超过1万条时自动切换分页模式4.2 性能优化技巧为分页字段建立doc_valuesPUT /my_index/_mapping { properties: { create_time: { type: date, doc_values: true } } }使用PIT保持索引状态一致// 创建PIT POST /my_index/_pit?keep_alive5m // 使用PIT查询 GET /_search { pit: { id: 46ToAwMDaWR5BXV1aWQyKwZub2RlXzM..., keep_alive: 5m }, sort: [{create_time: desc}], size: 100 }合理设置分片数建议每个分片不超过20GB数据4.3 监控指标查询耗时百分位P99 500ms分页缓存命中率深度分页请求占比在最近的双十一大促中这套方案支撑了峰值QPS 1.2万的商品查询请求99%的分页查询在300ms内返回。当遇到必须跳转深页码的场景比如直接跳转到第500页通过预计算的边界值结合search_after相比纯fromsize方案性能提升了40倍。