一文说清Elasticsearch中的分页与深度分页问题

一文讲透 Elasticsearch 分页与深度分页:从原理到实战

你有没有遇到过这样的场景?

前端同学说:“用户点了第500页,怎么卡住了?”
运维报警:“ES节点CPU爆了,查一下是不是有人在翻几万条数据!”
面试官问:“Elasticsearch 为什么不能深度分页?from/size到底慢在哪?”

这些问题的背后,其实都指向同一个核心机制——Elasticsearch 的分页实现方式及其在分布式环境下的代价

今天我们就来彻底搞清楚:
-Elasticsearch 是怎么执行一次分页查询的?
-为什么越往后翻越慢?
-search_afterscroll真的能解决这个问题吗?它们之间又有什么区别?
-实际项目中到底该用哪种方案?

别急,我们一步步拆解。这不仅是一篇技术解析文,更是一份你可以直接拿去用的生产级分页选型指南


from/size 不是“跳过”,而是“先拉再切”

我们先来看最常见的分页写法:

{ "from": 9990, "size": 10, "query": { "match_all": {} } }

看起来很简单:跳过前9990条,取接下来的10条。就像 SQL 中的LIMIT 9990, 10

但问题来了——Elasticsearch 真的是“跳过”了吗?

答案是:不是

分布式系统里的“全局排序”有多贵?

Elasticsearch 是分布式的。你的索引可能被分成 5 个分片,分布在不同的节点上。

当你发起一个from=9990, size=10的请求时,协调节点(coordinating node)会做这几件事:

  1. 向每个分片发送子查询,要求返回本地排序后的 top 10000 条结果
  2. 每个分片自己执行查询、打分、排序,返回自己的前 10000 条;
  3. 协调节点把所有分片返回的结果合并起来,重新排序,形成全局有序列表;
  4. 最后截取[9990, 10000)这 10 条数据返回给客户端。

📌 关键点:每个分片都要处理from + size条记录,哪怕其中 9990 条最终都会被丢弃!

这意味着什么?

偏移量每个分片需返回总传输数据量(假设5分片)
from=01050
from=90100500
from=99901000050,000

看到没?随着from增大,资源消耗几乎是线性增长的。CPU、内存、网络带宽全都被浪费在“搬运无效中间结果”上。

这也是为什么 ES 默认设置了:

index.max_result_window: 10000

一旦from + size > 10000,就会报错:

Result window is too large, from + size must be less than or equal to 10000

这是保护机制——防止一个请求拖垮整个集群。

所以,下次面试官问你:“ES 为什么不支持深度分页?”
你可以这样答:

“因为from/size在分布式环境下需要各分片返回大量中间结果,在协调节点进行全局排序和截断。当偏移量很大时,这种‘拉取-合并-丢弃’模式会造成严重的性能浪费,甚至导致 OOM。”


search_after:用“游标”代替“跳过”

既然from/size太重,那有没有办法只拿“真正需要的数据”?

有,就是search_after

它不靠数字偏移,而是靠“上一页最后一个文档的位置”作为锚点,继续往下读。有点像数据库里的“键集分页”(Keyset Pagination),也像翻书时记住“上次看到哪一页”。

它是怎么工作的?

假设你要按时间倒序查看日志:

{ "size": 10, "sort": [ { "@timestamp": "desc" }, { "_id": "asc" } ], "query": { "range": { "@timestamp": { "lte": "now" } } } }

第一次请求没有search_after,直接返回最新的10条。

拿到结果后,取出最后一条文档的排序值,比如:

[1680000000000, "log_001"]

下一次请求带上这个值:

{ "size": 10, "sort": [ { "@timestamp": "desc" }, { "_id": "asc" } ], "query": { "range": { "@timestamp": { "lte": "now" } } }, "search_after": [1680000000000, "log_001"] }

ES 就知道:“哦,你要找比(1680000000000, log_001)更大的记录”,于是每个分片只需扫描后续数据,无需加载前面成千上万条。

性能对比惊人

方案查询延迟(from=9990)内存占用是否可扩展
from/size800ms+
search_after~50ms极低

差距不止十倍。

而且search_after不受max_result_window限制,理论上可以一直翻到一百万页。

但它也有前提条件

  1. 必须指定明确的排序规则
  2. 排序字段组合必须能唯一标识顺序,否则可能出现漏读或重复。

举个例子:

如果你只按@timestamp desc排序,而很多文档时间戳相同,那么 ES 无法确定“下一个”是谁。这时候就必须补充一个唯一字段,比如_id或业务主键:

"sort": [ { "@timestamp": "desc" }, { "_id": "asc" } ]

这样才能保证每次都能精准定位“下一个”。

Java 实现示例

// 第一次请求 SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.rangeQuery("@timestamp").lte("now")); builder.sort("@timestamp", SortOrder.DESC); builder.sort("_id", SortOrder.ASC); builder.size(10); SearchRequest request = new SearchRequest("logs"); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); List<Object[]> sortValuesList = new ArrayList<>(); for (SearchHit hit : response.getHits()) { System.out.println(hit.getSourceAsString()); sortValuesList.add(hit.getSortValues()); } // 如果还有数据,构造下一页 if (!sortValuesList.isEmpty()) { Object[] lastSortValues = sortValuesList.get(sortValuesList.size() - 1); SearchSourceBuilder nextBuilder = new SearchSourceBuilder(); nextBuilder.query(QueryBuilders.rangeQuery("@timestamp").lte("now")); nextBuilder.sort("@timestamp", SortOrder.DESC); nextBuilder.sort("_id", SortOrder.ASC); nextBuilder.size(10); nextBuilder.searchAfter(lastSortValues); // 设置游标 SearchRequest nextRequest = new SearchRequest("logs"); nextRequest.source(nextBuilder); SearchResponse nextPage = client.search(nextRequest, RequestOptions.DEFAULT); // 处理 nextPage ... }

这就是典型的“连续拉取”逻辑。注意:它是无状态的,翻页依赖客户端维护上一次的排序值。


scroll API:为批量任务而生的“数据快照”

如果说search_after是为“高效顺滑地浏览数据”设计的,那scroll就是为“一次性搬完所有数据”服务的。

它适用于数据导出、迁移、报表生成等离线任务。

核心思想:保存搜索上下文

当你发起第一个scroll请求时:

POST /logs/_search?scroll=1m { "size": 100, "query": { "match_all": {} }, "sort": ["_doc"] }

ES 会:
- 创建一个“搜索上下文”(search context);
- 基于当前 Lucene 版本创建一个数据快照;
- 返回第一批数据和一个scroll_id

之后你拿着scroll_id不断请求:

POST /_search/scroll { "scroll": "1m", "scroll_id": "DnF1ZXJ5VGhlbkZldGNo..." }

ES 就会根据上下文继续返回下一批,直到数据全部读完。

优势很明显

  • 数据一致性强:你在遍历过程中看到的始终是初始时刻的数据视图,不会因新写入而“抖动”;
  • 适合大数据量导出:可稳定处理百万级文档;
  • 推荐使用_doc排序:这是 Lucene 内部顺序,不需要额外排序,速度最快。

但它也有硬伤

  1. 占用服务器资源:每个 scroll 上下文会锁定缓存、文件句柄,消耗堆内存;
  2. 不能长期持有:默认 1 分钟超时,需及时拉取;
  3. 不支持随机访问:只能顺序读;
  4. 不适合高并发交互场景:大量活跃的 scroll 会导致集群负载升高。

更关键的是:自 Elasticsearch 7.10 起,官方已明确建议用 PIT(Point in Time)替代传统 scroll


scroll 正在被淘汰?PIT 才是未来

没错,scroll虽然还在用,但已经是“老古董”了。

它的替代者叫Point in Time(PIT),结合search_after使用,既能保持快照一致性,又能享受游标的高性能。

PIT 的工作流程

  1. 先打开一个 PIT,获取一个pit_id
POST /logs/_pit?keep_alive=1m
  1. 在查询中使用 PIT,并配合search_after分页:
{ "size": 10, "sort": [ { "@timestamp": "desc" }, { "_id": "asc" } ], "query": { "range": { "@timestamp": { "gte": "now-1h" } } }, "pit": { "id": "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxKgZub2RlXzEAAAAAAAAAAAEBYQACAWh1aWRqaBZub2RlXzIAAAAAAAAAAAI", "keep_alive": "1m" }, "search_after": [1680000000000, "log_001"] }
  1. 每次返回新的pit信息,用于下一轮请求;
  2. 完成后显式关闭 PIT 释放资源。

为什么 PIT 更好?

对比项scrollPIT + search_after
数据一致性✅ 快照✅ 快照
性能一般高(无需维护上下文)
资源占用高(context 占内存)
可扩展性
是否推荐❌(逐渐废弃)✅(官方推荐)

简单说:PIT 把“快照能力”和“高效分页”完美结合,还不占资源

新项目强烈建议直接上PIT + search_after组合拳。


到底该怎么选?一张表说清适用场景

方案支持跳页性能稳定性数据一致性适用场景推荐指数
from/size❌(随偏移恶化)⚠️ 实时变化前台分页展示(<1万条)、简单列表⭐⭐☆
search_after❌(仅顺序)✅(稳定)⚠️ 实时变化深度分页、日志流、消息中心、实时查询⭐⭐⭐⭐
scroll✅(批次稳定)✅(快照)数据导出、迁移、旧系统兼容⭐⭐
PIT + search_after✅✅✅(快照)大数据量一致性遍历(新项目首选)⭐⭐⭐⭐⭐

实战建议:别让分页拖垮你的系统

1. 前端分页尽量加过滤条件

与其让用户无脑翻到第1000页,不如引导他们通过关键词、时间范围、分类筛选缩小结果集。

用户真需要看一万条以前的商品吗?大概率不需要。

2. 深度分页坚决不用from/size

如果业务允许,强制限制最大页码(如最多显示100页),超出提示“请优化搜索条件”。

或者干脆改成分页模式:只允许“上一页/下一页”,背后用search_after实现。

3. 导出任务优先考虑 PIT

不要用scroll写脚本跑百万数据!用 PIT 更安全、更轻量。

4. 监控活跃的 search context 数量

设置告警规则,监控nodes.stats.indices.search.open_contexts,防止scroll泛滥导致内存溢出。

5. 排序字段一定要唯一

search_after时,务必组合时间戳 + ID 或其他唯一字段,避免因排序模糊导致数据跳跃。


面试题也能轻松应对

现在回过头来看那些高频 es 面试题:

Q1:Elasticsearch 为什么不适合深度分页?

因为from/size在分布式环境下需要每个分片返回from + size条数据,协调节点合并后再排序截断。当偏移量很大时,会产生大量无效计算和数据传输,性能急剧下降。因此默认限制max_result_window=10000

Q2:如何优化深度分页?

改用search_after,基于上一页最后一个文档的排序值作为游标,避免全局排序和中间结果加载,性能几乎不受偏移影响。

Q3:search_afterscroll有什么区别?

  • search_after是无状态的,适合实时交互式分页;
  • scroll有状态,维护搜索上下文,提供数据快照,适合批量导出;
  • scroll资源消耗高,已被 PIT + search_after 取代。

写在最后

分页看似简单,但在分布式系统中却藏着很深的设计权衡。

from/size简单直观,但代价高昂;
search_after高效稳定,但放弃随机跳转;
scroll曾经强大,如今正被 PIT 取代。

作为开发者,我们要做的不是死记硬背语法,而是理解每种方案背后的成本模型适用边界

当你下次面对“第500页加载慢”的问题时,希望你能从容地说:

“我们换个分页方式吧,用search_after,保证流畅到底。”

这才是真正的技术底气。

如果你正在做搜索、日志、监控类系统,欢迎收藏本文,也可以转发给团队一起讨论:你们现在的分页方案,真的合适吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1137041.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大数据BI工具排行榜:2023年最受欢迎的10款工具盘点

大数据BI工具排行榜&#xff1a;2023年最受欢迎的10款工具盘点关键词&#xff1a;大数据BI工具、商业智能、数据可视化、自助式BI、企业级BI、AI驱动分析、数据治理摘要&#xff1a; 本文深度解析2023年全球最受欢迎的10款大数据BI工具&#xff0c;从技术架构、核心功能、适用场…

琴声润晚年!红松小课见证盲人夫妻的幸福答卷

55岁的王金珠提起学钢琴的事&#xff0c;眼睛里带着笑意&#xff1a;“结婚纪念日&#xff0c;刘鹏偷偷给我报了红松小课的钢琴课&#xff0c;他说我这个‘琴行老板’该好好学琴了。”她口中的“琴行”不是卖钢琴的店铺&#xff0c;而是和丈夫刘鹏在哈尔滨经营的一家盲人按摩店…

提示系统代码覆盖率分析瓶颈:架构师的6个突破策略

系统代码覆盖率分析瓶颈&#xff1a;架构师的6个突破策略——深入剖析大型项目优化之道 副标题&#xff1a;从测试效率到高质量交付&#xff0c;打造可伸缩的覆盖分析架构 第一部分&#xff1a;引言与基础 (Introduction & Foundation) 1. 引人注目的标题 系统代码覆盖率…

动圈 vs 动铁耳机频率响应差异:深度剖析结构影响

动圈 vs 动铁耳机频率响应差异&#xff1a;从结构到听感的深度拆解你有没有过这样的体验&#xff1f;同一首歌&#xff0c;换一副耳机&#xff0c;仿佛换了支乐队在演奏。低频不再是“轰”地一下扑来&#xff0c;而是清晰可辨的鼓点节奏&#xff1b;人声不再浑浊挤在一起&#…

基于Java+SpringBoot+SSM传统文化交流交易平台(源码+LW+调试文档+讲解等)/传统文化传播平台/文化交流平台/文化交易平台/传统文化活动平台/传统文化展示平台/文化交流交易网站

博主介绍 &#x1f497;博主介绍&#xff1a;✌全栈领域优质创作者&#xff0c;专注于Java、小程序、Python技术领域和计算机毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb; 精彩专栏 推荐订阅&#x1f447;&#x1f3fb; 2025-2026年最新1000个热门Java毕业设计选题…

伊顿变压器启用明诺V75驾驶式洗地机,赋能智慧工厂清洁升级

作为全球智能动力管理领域的领军企业&#xff0c;伊顿变压器始终以高标准打造生产环境&#xff0c;保障电力设备制造的安全性与精密性。近日&#xff0c;伊顿变压器&#xff08;江苏&#xff09;有限公司正式启用明诺V75驾驶式洗地机&#xff0c;为其68000平方米的现代化生产车…

漏电探测仪:发现绝缘老化、线路破损等隐患

漏电探测仪作为一种专业的电气安全检测工具&#xff0c;在发现绝缘老化、线路破损等隐患方面发挥着至关重要的作用。以下是对漏电探测仪如何发现这些隐患的详细解释&#xff1a;一、漏电探测仪的工作原理漏电探测仪通常基于电磁感应原理或电场感应原理工作。它能够检测到电路中…

PCB铺铜初学指南:掌握地平面设计要点

PCB铺铜实战心法&#xff1a;从地平面设计到信号完整性的底层逻辑你有没有遇到过这样的情况&#xff1f;电路原理图明明没问题&#xff0c;元器件选型也经过反复验证&#xff0c;可板子一上电就干扰严重&#xff0c;ADC采样数据跳得像心电图&#xff0c;高速通信动不动就丢包。…

一文说清USB-Serial Controller D驱动下载常见问题

一文说清USB-Serial Controller D驱动下载常见问题 你有没有遇到过这样的情况&#xff1a; 手里的开发板插上电脑&#xff0c;设备管理器里却只显示“未知设备”或“USB-Serial Controller D”&#xff0c;找不到COM口&#xff1f; 串口助手打不开端口&#xff0c;烧录程序失…

新手必看:工业电子项目前的Vivado安全卸载方法

工业电子项目启动前&#xff0c;如何彻底卸载Vivado&#xff1f;新手避坑全指南 你有没有遇到过这种情况&#xff1a; 刚装好新版Vivado&#xff0c;结果一打开就报错“许可证无效”&#xff1b; 或者JTAG连不上开发板&#xff0c;反复重装驱动也没用&#xff1b; 甚至安装…

工业电机驱动器中I2C配置接口的操作指南

工业电机驱动器中I2C配置接口的实战解析&#xff1a;从原理到代码调试在工业自动化系统中&#xff0c;电机驱动器早已不是简单的“通电就转”设备。现代伺服、步进或BLDC驱动芯片集成了复杂的控制逻辑和保护机制&#xff0c;而如何高效地与这些“智能执行单元”通信&#xff0c…

SSM校园排球联赛管理系统y513u(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面

系统程序文件列表系统项目功能&#xff1a;学生,排球联赛,比赛报名,比赛成绩,比赛分组SSM校园排球联赛管理系统开题报告一、课题研究背景与意义&#xff08;一&#xff09;研究背景校园排球联赛作为高校体育文化建设的重要组成部分&#xff0c;深受学生喜爱。但当前联赛管理多依…

HID设备操作指南:报告描述符编写技巧与验证方法

深入HID报告描述符&#xff1a;从零构建可即插即用的USB输入设备你有没有遇到过这样的情况&#xff1f;精心设计的嵌入式HID设备&#xff08;比如自定义键盘、游戏手柄或工业控制面板&#xff09;已经能正常发送数据&#xff0c;但主机就是“视而不见”——按键不响应、坐标错乱…

前端向架构突围系列 - 框架设计(三):用开闭原则拯救你的组件库

写在前面 兄弟们&#xff0c;回想一下&#xff0c;你有没有接过这种需求&#xff1a; 产品经理跑来说&#xff1a;“咱们那个通用的表格组件&#xff0c;现在需要在某一列加个自定义的渲染逻辑&#xff0c;以前是纯文本&#xff0c;现在要变成个带图标的按钮&#xff0c;还能点…

如何在 Linux 中使用 file 命令识别文件类型

在 Linux 系统中&#xff0c;file 命令是一款强大的工具&#xff0c;用于确定文件类型&#xff0c;例如普通文件、压缩归档文件、符号链接以及其他特殊文件类型。与仅依赖文件扩展名的方法不同&#xff0c;file 命令通过引用“magic file database”数据库来识别文件类型。该数…

WebM转MP4在线转换工具

WebM转MP4在线转换工具 - 88box视频格式转换助手 工具核心信息 工具名称&#xff1a;88box视频格式转换工具访问地址&#xff1a;https://88box.top/video-tools/transcode核心功能&#xff1a;支持WebM与MP4格式双向转换&#xff0c;兼容多场景视频格式适配需求 工具详细介…

SSM校园人才市场391d8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面

系统程序文件列表系统项目功能&#xff1a;学生,企业,招聘信息,岗位类别,投递简历,参加面试,面试结果,学生评价SSM校园人才市场开题报告一、课题研究背景与意义&#xff08;一&#xff09;研究背景当前高校毕业生就业压力逐年增大&#xff0c;校园招聘作为毕业生求职的核心渠道…

图解说明RS232串口调试工具在自动化产线中的连接方式

RS232串口调试工具如何接&#xff1f;一张图讲清自动化产线中的“通信听诊器”用法在现代自动化车间里&#xff0c;PLC、伺服驱动器、条码扫描仪、温控表这些设备高速协同运转。一旦通信出问题&#xff0c;整条产线可能就得停摆。这时候&#xff0c;工程师往往会掏出一个不起眼…

2026“芯片战局”白热化,AMD/微美全息加固“护城河”竞逐AI算力制高点

1月6日至9日&#xff0c;全球规模最大&#xff0c;影响力最广泛的国际消费电子展&#xff08;CES&#xff09;在美国拉斯维加斯开幕。这个全球最重要的消费电子展会已经有近60年历史&#xff0c;从1978年开始固定在美国。AI芯片巨头集体站台在刚开锣的2026 CES上&#xff0c;芯…

HBuilderX运行项目不启动浏览器?一文说清常见故障点

HBuilderX运行项目不启动浏览器&#xff1f;别急&#xff0c;这5个坑我替你踩过了你有没有过这样的经历&#xff1a;兴冲冲打开HBuilderX&#xff0c;写完一段代码&#xff0c;信心满满地点击“运行到浏览器”&#xff0c;结果——什么都没发生。没有弹出Chrome&#xff0c;没有…