排序与分页是什么
搜索默认按 _score 相关度排序,也可以指定字段排序并控制 order、missing 与数组字段的 mode。分页用 from 加 size,受 index.max_result_window 限制(默认最多取到一万条)。深分页要改用 search_after 配合稳定排序键,否则每次都要跳过前面所有文档,代价随页数线性上升。
基本信息
| 排序写法 | "sort": [ { "date": "desc" }, "_score" ] |
|---|---|
| 字段排序限制 | text 字段不能直接排序,要用 keyword 子字段 |
| 分页上限 | from + size 不超过 index.max_result_window,默认 10000 |
| 深分页方案 | search_after + 唯一稳定的排序键 |
| 总数统计 | track_total_hits 控制是否精确统计命中总数 |
| 适用版本 | Elasticsearch 7.x / 8.x |
典型使用场景
- 商品列表按价格、销量或上架时间排序
- 日志按时间倒序查看最新事件
- 列表页常规分页,页数不深的场景用 from 加 size
- 导出大数据量结果集,用 search_after 逐批拉取
- 需要精确总数时开启 track_total_hits
- 多级排序:主键相同再按时间或 ID 兜底排序
示例
示例
GET /my-index/_search
{
"from": 20,
"size": 10,
"sort": [
{ "publish_date": { "order": "desc", "missing": "_last" } },
{ "_score": "desc" }
],
"track_total_hits": true
}
GET /my-index/_search
{
"size": 10,
"sort": [ { "publish_date": "desc" }, { "_id": "asc" } ],
"search_after": [ 1750000000000, "doc-9" ] # 深分页续取
}排序参数读法
| 参数 | 作用 | 注意点 |
|---|---|---|
| order | asc / desc | 多字段分别指定 |
| missing | _last / _first | 缺失值位置 |
| mode | min/max/avg/sum/median | 数组字段取哪个元素 |
| unmapped_type | 未映射字段类型 | 避免查询报错 |
| track_total_hits | 是否精确统计总数 | 默认只统计到一万 |
| search_after | 从上次结果之后取 | 排序键必须唯一稳定 |
排序与分页实践
- 排序字段要建索引,否则只能现场计算或加载列存
- 排序键加 _id 兜底,避免相同值时顺序不确定导致漏数据
- 深分页一律改用 search_after,不要靠不断加大 from
- 导出用滚动查询或 search_after,并控制每批大小
- 对 text 字段排序前先确认有 keyword 子字段
- 排序方向要与字段的 doc_values 用法一致,避免额外内存排序
track_total_hits 默认只统计到 10000 就返回 gte,需要精确总数时显式设为 true;只关心是否有结果时保持默认即可省开销。
from 加 size 超过 max_result_window 会直接报错,调大窗口只是绕过限制,真正的解决方式是改用 search_after。
数组字段排序默认取最小值(min 模式),需要别的语义必须显式设置 mode;单值字段不需要关注该参数。
search_after 依赖的排序键组合必须唯一且稳定,通常用「时间加 _id」,只按时间排序在时间相同时可能漏掉或重复文档。
深分页方案对比
- from 加 size:最简单,页数一深性能迅速下降
- scroll:适合一次性导出全量,会占用快照资源
- search_after:适合用户侧深分页,需要稳定唯一排序键
- 游标查询:适合实时性要求不高的滚动遍历
- 导出任务优先 search_after 或 scroll,并控制每批大小
- 调大 max_result_window 只是放开限制,解决不了性能问题
排序常见问题
- 排序字段是 text 会直接报错,需要 keyword 子字段
- 缺失值位置用 missing 指定,默认位置与预期不一定一致
- 数组字段排序要明确 mode,否则默认取最小值
- 多字段排序时后面的字段只在前面字段相同时才起作用
- 把 _score 与字段混排要注意两者量纲不同
- 排序键不唯一会导致分页结果重复或遗漏
分页总数返回 relation 为 gte 说明未精确统计;需要精确总数时把 track_total_hits 设为 true,代价是要遍历更多文档。
排序依赖字段的正排数据(doc_values),它默认开启;关闭了 doc_values 的字段无法排序,只能重新映射或改用运行时字段计算排序值。
分页参数速记
- from 默认 0、size 默认 10,取全量时要显式放大但仍受窗口限制
- 排序字段与查询条件字段尽量复用同一个索引,减少内存排序
- 导出场景不需要精确总数,把 track_total_hits 关掉可省一次统计
- search_after 的排序键要能唯一定位文档,否则会重复或漏取