文章目录
- 前言
- [常见的 Elasticsearch 分页设计方案](#常见的 Elasticsearch 分页设计方案)
-
- [1. `from + size` 分页 (浅分页)](#1.
from + size分页 (浅分页)) - [2. `scroll` 分页 (游标分页/深分页)](#2.
scroll分页 (游标分页/深分页)) - [3. `search_after` 分页 (搜索后分页)](#3.
search_after分页 (搜索后分页)) - [4. 基于 `PIT` (Point In Time) 的 `search_after` 分页](#4. 基于
PIT(Point In Time) 的search_after分页)
- [1. `from + size` 分页 (浅分页)](#1.
- 方案总结与对比
- 核心建议
- 资料获取

前言
博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。
涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。
博主所有博客文件目录索引:博客目录索引(持续更新)
CSDN搜索:长路
视频平台:b站-Coder长路
常见的 Elasticsearch 分页设计方案
1. from + size 分页 (浅分页)
这是最直观、最常见的分页方式,类似于 MySQL 的 LIMIT offset, size。
- 工作原理 :
from指定了跳过的初始文档数量,size指定了返回的最大文档数量。 - 例子:查询第 6-10 条数据(即第 2 页,每页 5 条)。
json
GET /my_index/_search
{
"query": {
"match_all": {}
},
"from": 5, // 跳过前5条
"size": 5 // 返回5条
}
2. scroll 分页 (游标分页/深分页)
专为一次性处理大量数据(如数据导出、全量重建索引)而设计,不适合实时用户请求。
- 工作原理 :首次查询创建一个快照和游标,后续请求使用这个游标来获取剩余结果。搜索上下文会在指定时间(如
1m)内保持活跃。 - 例子:导出所有符合条件的数据。
a. 初始化 scroll 请求
json
GET /my_index/_search?scroll=1m // 保持搜索上下文1分钟
{
"query": {
"match": {
"title": "elasticsearch"
}
},
"size": 100 // 每次滚动返回100条
}
响应中会包含一个 _scroll_id。
b. 后续获取请求
json
POST /_search/scroll
{
"scroll": "1m",
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAC...(很长的字符串)"
}
3. search_after 分页 (搜索后分页)
ES 5.0 引入,是进行深分页的最佳实践,适用于需要实时、有序地翻看大量数据的场景。
- 工作原理 :使用上一页结果中的排序值作为"锚点",来获取下一页的结果。它避免了
from + size深度分页的性能问题。 - 要求 :必须指定一个或多个唯一的、不重复的排序字段(通常包括
_id)。 - 例子:按时间戳和 ID 进行深度分页。
a. 获取第一页
json
GET /my_index/_search
{
"query": {
"match_all": {}
},
"size": 10,
"sort": [
{"create_time": "desc"}, // 主排序字段
{"_id": "asc"} // 确保排序唯一性
]
}
b. 获取第二页及以后
使用第一页最后一条记录的排序值。
json
GET /my_index/_search
{
"query": {
"match_all": {}
},
"size": 10,
"sort": [
{"create_time": "desc"},
{"_id": "asc"}
],
"search_after": [
"2023-10-27T08:00:00.000Z", // 上一页最后一条记录的 create_time
"abc123" // 上一页最后一条记录的 _id
]
}
4. 基于 PIT (Point In Time) 的 search_after 分页
search_after 的增强版,解决了在分页过程中索引可能发生变化(如数据删除、更新)导致的结果不一致问题。
- 工作原理 :先创建一个"时间点"(PIT),这个时间点会冻结索引的视图,保证在整个分页过程中看到的数据是一致的。然后结合
search_after进行分页。 - 例子:保证在长时间翻页过程中数据的一致性。
a. 创建 PIT
json
POST /my_index/_pit?keep_alive=1m
返回一个 id。
b. 使用 PIT 和 search_after 进行查询
json
GET /_search
{
"pit": {
"id": "46ToAwMDaWR5BXV1aWQyKwZub2RlXzMAAAAAAAAAACoBYwADaWR4BXV1aWQxAgZub2RlXzEAAAAAAAAAAAEBYQADaWR5BXV1aWQyKgZub2RlXzIAAAAAAAAAAAwBYgACBXV1aWQyAAAFdXVpZDEAAQltYXRjaF9hbGw_gAAAAA==",
"keep_alive": "1m"
},
"sort": [
{"@timestamp": "asc"},
{"_id": "asc"}
],
"size": 100,
"search_after": [
1698390400000,
"o1W6_osB8-dyz-MuPqQ3"
]
}
方案总结与对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
from + size |
浅分页:用户界面的前几十或几百页数据。例如,网站的商品列表、新闻列表的前几页。 | 1. 使用简单,易于理解。 2. 支持随机跳页。 | 1. 深度分页性能极差 。from 值越大,协调节点需要排序和汇总的结果越多,消耗的内存和CPU也越多。 2. 默认 index.max_result_window 为 10000,限制了其使用深度。 |
scroll |
离线处理:需要从索引中导出全部数据或批量处理所有符合条件的数据。例如,数据迁移、日志分析、全量备份。 | 1. 适合处理海量数据。 2. 高效,因为后续请求不需要重复查询和排序。 | 1. 不适用于实时用户请求。 2. 搜索上下文保持需要开销,占用资源。 3. 数据非实时,是基于第一次查询时的快照。 |
search_after |
深度分页的实时查询:用户需要有序地、实时地翻阅大量数据。例如,微博/新闻流的无限下拉、后台系统的审计日志查看。 | 1. 解决深度分页性能问题,性能恒定。 2. 查询结果是实时的。 3. 不占用大量资源。 | 1. 不支持随机跳页 ,只能一页一页顺序翻。 2. 需要至少一个唯一性排序字段(通常加上 _id)。 3. 页面结构相对固定(依赖于上一页的结果)。 |
PIT + search_after |
对一致性要求高的深度分页:在分页过程中,不允许因数据变更导致结果重复或丢失。例如,金融交易记录的翻页查询、严格的数据核对任务。 | 1. 具备 search_after 的所有优点。 2. 提供跨页的一致性视图,避免因数据更新导致的分页混乱。 |
1. 相比纯 search_after 稍复杂,需要管理 PIT ID。 2. 同样不支持随机跳页。 3. PIT 会占用一定的服务器资源。 |
核心建议
- Top N 或浅层分页 :毫不犹豫地使用
from + size。 - 数据导出或离线处理 :使用
scroll。 - 深度分页(最常见) :使用
search_after。这是现代 ES 应用中处理深度分页的标准答案。 - 深度分页 + 强一致性要求 :使用
PIT + search_after。
记住一个黄金法则:永远不要在生产环境中使用 from + size 来获取超过 10000 条以后的数据。
资料获取
大家点赞、收藏、关注、评论啦~
精彩专栏推荐订阅:在下方专栏👇🏻
- 长路-文章目录汇总(算法、后端Java、前端、运维技术导航):博主所有博客导航索引汇总
- 开源项目Studio-Vue---校园工作室管理系统(含前后台,SpringBoot+Vue):博主个人独立项目,包含详细部署上线视频,已开源
- 学习与生活-专栏:可以了解博主的学习历程
- 算法专栏:算法收录
更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅