文章二十五:ElasticSearch 分页查询

from+size分页:

from:起使数据的位置(不是页数,是位置)

size:单页返回的数据量大小

复制代码
GET page_study_index/_search
{
  "track_total_hits": true, #这个属性慎用
  "from": 0,
  
  "size":1
}

from size的窗口限制是10000,size+from<=10000,下面是错误信息:

type": "illegal_argument_exception",

"reason": "Result window is too large, from + size must be less than or equal to: [10000] but was [10001]. See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level setting."

通过上面的错误信息我们可以发现通过修改setting中的设置可以将这个窗口变大:

复制代码
PUT page_study_index/_settings
{
  "max_result_window":20000
}
PUT page_study_index/_settings
{
  "index": {
    "max_result_window": 20000
  }
}

之后就可以查询20000内置的数据了。但是这个数值要谨慎开启,因为数值的过大影响es的效率。

size数值的最佳设置:

每一个source数据的大小在1KB以内,每次查询的数据在2MB以内

复制代码
GET index_test/_search
{
  "size": 20,           // 每次取20条
  "query": { "match_all": {} },
  "sort": [ { "timestamp": "desc" } ],  // 按时间倒序
  "search_after": [ 1780268648000 ]     // 核心:从这个时间戳之后开始查
}

根据上一页最后一条数据的排序值,来定位下一页开始的位置。 它是深分页最优方案,专门解决:

  • from + size 深分页(如 page=1000,size=20)性能极差
  • max_result_window 限制(默认只能查前 10000 条)
  • 上亿数据分页

必须遵守的 3 条规则

  1. 必须带 sort 排序(search_after 依赖排序值定位)
  2. search_after 的值 = 上一页最后一条数据的排序字段值
  3. 不支持跳页(不能直接点第 100 页,只能一页一页往后翻)

Point in time查询

PIT(Point in Time) ,中文译为时间点视图 ,是 Elasticsearch 7.10+ 版本推出的轻量级数据快照机制

简单理解:PIT 会锁定索引在某一时刻的静态数据视图 ,后续所有基于该 PIT 的查询、分页操作,都会基于这一固定快照执行,完全不受索引实时新增、修改、删除数据的影响

PIT 仅保存数据视图的元数据,不复制原始数据,占用资源极低,是 ES 官方推荐的上亿数据深分页、全量数据导出、滚动查询的企业级方案。

PIT解决的核心问题:

在未使用 PIT 时,常规 search_after 分页存在严重的数据一致性问题,场景如下:

  1. 数据偏移、重复、漏数据 :分页查询过程中,若索引有新数据写入、旧数据更新/删除,ES 数据物理排序会发生变化,导致下一页search_after 定位错乱,出现重复查询或数据丢失。

  2. 无法保证分页一致性 :普通search_after 每次查询都会读取最新索引视图,多次分页查询的数据源视图不统一。

  3. 规避深分页性能限制 :突破 max_result_window 10000 条的默认分页上限,支持海量数据无上限滚动分页。

PIT 核心价值 :固定查询视图,让多次分页查询基于同一时间点的静态数据,彻底解决分页数据错乱问题,同时保障海量数据分页性能。

PIT 核心特性

  • 轻量无数据拷贝:仅记录索引分片、版本、时间点元数据,不复制文档数据,内存开销极小。

  • 视图隔离:PIT 创建后,索引后续的增删改操作,不会影响当前 PIT 的查询结果。

  • 必须配合 search_after :PIT 不支持页码分页,仅用于滚动深分页,需与 search_after 组合使用。

  • 生命周期可控 :通过 keep_alive 设置存活时间,超时自动销毁,也可手动提前删除。

  • 查询参数限制:使用 PIT 查询时,不可指定 index、routing、preference 参数,所有数据源信息均继承自 PIT 快照。

完整实操流程

PIT 分页固定四步流程:创建 PIT 视图 → 首次分页查询 → 滚动分页查询 → 销毁 PIT

1. 创建 PIT 时间点视图

锁定目标索引当前数据状态,生成唯一的 PIT ID,指定视图存活时间。

复制代码
POST page_study_index/_pit?keep_alive=5m

返回结果示例

{ "id": "46ToAwMDcGFnZV9zdHVkeV9pbmRleAAEWjFhY...(唯一PIT-ID)" }

参数说明:keep_alive 表示 PIT 视图的存活时长,分页耗时久可设置 10m、30m,避免视图过期失效。

2. 第一页查询(无 search_after)

首次查询无需 search_after,携带 PIT-ID、固定排序规则,获取首页数据,并记录最后一条数据的排序值。

复制代码
GET _search
{
  "pit": {
    "id": "上面返回的PIT-ID",
    "keep_alive": "5m" // 续期视图时长
  },
  "size": 20,
  "sort": [
    { "timestamp": "desc" }
  ]
}

💡 最佳实践:海量数据分页可使用"sort": ["_shard_doc"],这是 ES 内置唯一排序字段,性能最优、无重复值,避免排序冲突。

3. 滚动分页查询(第二页及以后)

提取上一页最后一条数据的 sort 排序值 ,填入search_after,基于固定 PIT 视图向后滚动查询。

复制代码
GET _search
{
  "pit": {
    "id": "-6zrAwEQcGFnZV9zdHVkeV9pbmRleBZlNy0ycllYVFFpUy00XzRsOFR3VUV3ABZuZlYzVjBoQlJRU1J6dUh5THJrMEZRAAAAAAAAAZHoFnBKNGpYOWgyU3JtMmRNNkdXRnFhQVEAARZlNy0ycllYVFFpUy00XzRsOFR3VUV3AAA=",
    "keep_alive": "5m"
  },
  "sort": [
    {
      "number": {
        "order": "desc"
      }
    }
  ],
  "size":2,
  "search_after": [
    "99998"
    ]
}

循环执行此步骤,直到查询结果为空,代表数据分页完毕。

4. 手动销毁 PIT(必做)

PIT 超时前会一直占用集群资源,分页结束后必须手动删除,释放资源。

# 删除指定 PIT DELETE _pit { "id": "你的PIT-ID" }

Scroll 滚动查询

一、Scroll 查询核心概念

Scroll 是 Elasticsearch旧版经典深分页/全量遍历方案,是 ES7.10 之前主流的海量数据分页方式,现已被官方逐步废弃。

普通 search 查询是「实时快照、单次查询」,而 Scroll 查询的核心逻辑是:第一次查询时生成一个持久化索引快照上下文 ,后续每一次滚动查询,都基于这一份固定快照持续向后取数,不再读取最新索引数据。

核心定位:用于批量、全量、大批量数据导出、数据同步场景,不用于前端普通分页

二、Scroll 核心原理

  1. 首次 scroll 查询 :ES 遍历匹配数据,生成一个快照上下文 ,记录当前索引所有分片的数据状态、文档排序、遍历指针,同时返回唯一scroll_id

  2. 后续滚动查询 :客户端携带 scroll_id 持续请求,ES 基于快照上下文,从上次遍历位置继续向后取数,不会重新检索索引、不会感知新数据变更

  3. 快照生命周期 :通过scroll=时间 控制快照存活时间,超时自动销毁上下文,释放资源。

简单理解:第一次查询拍照定格数据,后续所有翻页都是翻看这张照片

三、Scroll 完整实操流程(标准旧版写法)

1. 初始化 Scroll(首次查询,生成快照)

首次查询必须指定 scroll 存活时间,用于维持快照上下文,同时设置 size 指定每页数据量。

GET test_index/_search?scroll=5m { "size": 100, "query": { "match_all": {} } }

返回结果包含关键参数:_scroll_id,后续所有滚动请求依赖该 ID。

2. 滚动分页查询(循环取数)

无需指定索引、查询条件、排序,只需要携带 scroll_id 和续期时间,持续获取下一批数据。

GET _search/scroll { "scroll": "5m", "scroll_id": "上一步返回的_scroll_id" }

循环执行此请求,直到返回的 hits 为空,代表全量数据遍历完毕。

3. 手动清除 Scroll 上下文(重要)

Scroll 快照会常驻内存,超时前不会自动释放,业务结束必须手动清理,防止集群内存泄露。

DELETE _search/scroll { "scroll_id": "对应的_scroll_id" }

四、Scroll 核心特性

  • 基于固定快照 :遍历期间索引新增、修改、删除的数据完全不可见,数据一致性强,无漏数、无重复。

  • 无深度分页限制 :不受 max_result_window 10000 条限制,支持全量数据遍历。

  • 无需业务排序字段:Scroll 基于文档内部顺序遍历,不需要依赖高离散排序字段,规避 search_after 字段选型难题。

  • 资源占用高:会长时间持有分片快照上下文,占用节点内存、文件句柄,高并发场景压力极大。

  • 不支持实时数据:一旦生成快照,无法读取最新业务数据,只适合离线批量任务。

  • 不支持跳页:只能向后滚动遍历,无法实现前端页码跳转分页。

五、Scroll 核心优缺点

优点

  • 天然保证全量数据一致性,遍历全程数据无错乱、无丢失、无重复。

  • 使用简单,无需关心排序字段离散度问题。

  • 完美支持超大索引全量导出、数据同步、离线计算场景。

缺点(致命)

  • 集群资源开销极大:长期持有分片快照上下文,占用内存,高并发批量导出极易打满 CPU、引发 GC。

  • 数据严重滞后:无法感知遍历过程中的数据新增和变更,不适合实时业务。

  • 容易资源泄露:代码异常、程序中断时,未手动清理 scroll_id,会导致内存长期占用。

  • 官方已废弃:ES7.10+ 版本开始逐步弃用,不再优化维护,被 PIT 方案全面替代。

六、Scroll 淘汰核心原因(面试重点)

很多人只知道 Scroll 不用了,但不知道为什么被 PIT 替代,核心三点:

  1. 资源模型笨重 :Scroll 的快照是重量级上下文,绑定分片资源,长时间占用集群资源,并发场景扩展性极差。

  2. 功能固化不灵活:一次初始化固定查询条件和快照,无法动态调整查询参数,无法续期灵活控制。

  3. PIT 全方位碾压:PIT 是轻量级时间点视图,资源开销几乎可忽略,同时保留快照一致性能力,还支持动态续期、灵活搭配 search_after,性能和稳定性全面超越 Scroll。

七、Scroll 适用 & 禁用场景

适合场景(仅老旧项目)

  • 低并发、低频的离线全量数据导出

  • 旧版 ES(7.10 以下)海量数据同步任务

绝对不适用场景

  • 前端列表分页、实时查询业务

  • 高并发、高频批量任务

  • 新版 ES 集群新项目开发

八、Scroll、普通 search_after、PIT+search_after 核心对比

分页方案 数据一致性 资源开销 排序依赖 适用场景 状态
Scroll 极高(快照固定) 大(重量级上下文) 无依赖 旧版离线全量遍历 已废弃
普通 search_after 差(实时视图易错乱) 极小 强依赖高离散唯一字段 中小数据滚动分页 日常常用
PIT+search_after 极高(时间点快照) 极小(轻量元数据) 可依赖内置字段兜底 上亿数据、高并发、严谨业务 官方最优推荐

九、在节点中查看当前获取的scroll快照

GET _nodes/stats/indices/search?pretty 查看这个命令中的

找到字段:

  • open_contexts:当前节点存活 Scroll 快照数量
  • scroll_total:历史总共创建过多少 scroll
  • scroll_time_in_millis:scroll 累计耗时

这几个字段就可以了。

相关推荐
科研前沿1 小时前
SpaceOS™空间计算底座与五大自研引擎,实现多项关键技术突破
大数据·运维·人工智能·算法·重构
今天长肉了吗1 小时前
风控指标平台实战:大数据量下如何设计分批处理
开发语言·数据库·python
2301_782040451 小时前
JavaScript中丢失的this:回调函数中指向改变的对策
jvm·数据库·python
2301_818008441 小时前
MySQL从库出现数据同步异常中断_重新获取binlog坐标同步
jvm·数据库·python
四维迁跃1 小时前
MySQL如何优雅处理数据库连接池耗尽_HikariCP与连接数调优
jvm·数据库·python
ch.ju2 小时前
Java programming(The third edition) Chapter Two——Null return value
java·开发语言
X56612 小时前
Go语言如何做Helm Chart_Go语言Helm打包部署教程【收藏】
jvm·数据库·python
szccyw02 小时前
如何阻止 HTML 页面在 JavaScript 执行完成前渲染
jvm·数据库·python
1.14(java)2 小时前
Spring事务和事务传播机制
java·数据库·spring