PIT 是干什么的
PIT(point in time)为一次搜索会话固定索引视图:创建后拿到一个 id,后续 search 请求在请求体里带上它,就相当于一直在同一份数据快照上检索。它解决了索引持续写入时普通 search_after 结果漂移的问题,也是官方推荐的深分页方式。代价是 PIT 持有期间相关段无法合并,用完必须显式关闭。
基本信息
| 创建 | POST /<index>/_pit?keep_alive=1m |
|---|---|
| 使用 | search 请求体写 "pit": { "id": "...", "keep_alive": "1m" },可不指定索引 |
| 收尾 | DELETE /_pit,请求体传 id |
| 分页搭配 | 与 search_after 搭配使用,不能同时带 from |
| 适用版本 | Elasticsearch 7.10 起提供 PIT API |
完整流程
完整流程
# 1 创建 PIT,保留 1 分钟
POST /my-index/_pit?keep_alive=1m
# 2 用 PIT 检索:不能再用 from,排序里加 _shard_doc 兜底成唯一序
GET /_search
{
"size": 10,
"query": { "match": { "title": "elastic" } },
"pit": { "id": "<pit_id>", "keep_alive": "1m" },
"sort": [ { "@timestamp": "desc" }, { "_shard_doc": "asc" } ],
"search_after": [ 1699999999000, 42 ]
}
# 3 收尾:关闭 PIT,释放段引用
DELETE /_pit
{ "id": "<pit_id>" }典型使用场景
- 深分页:翻到几千页之后,用 PIT + search_after 替代 from+size
- 一致性遍历:导出期间索引仍在写入,需要固定视图
- 报表批量取数:保证同一批次的数据口径一致
- 游标式翻页:前端滚动加载,用 _shard_doc 保证全序
- 跨索引会话:一次遍历同时覆盖多个索引并固定视图
参数与注意
| 参数 | 作用 | 注意 |
|---|---|---|
| keep_alive | PIT 与上下文保留时长 | 每次带 pit 的请求可续期 |
| id | PIT 标识 | 响应会返回新 id,翻页要用最新的 |
| _shard_doc | 分片内文档序号 | 与其他排序字段组合成唯一全序 |
| from | 起始偏移 | 使用 pit 时不允许出现 |
keep_alive 过期后再发请求会报上下文丢失(context missing),长任务要把保留时长设得比单页耗时宽裕,并在每页请求里自动续期。
PIT 持有的段无法参与合并,时间窗越宽、开启时间越长,对段合并与磁盘回收的推迟越明显;遍历结束或异常退出后都要确认没有残留 PIT。
为什么深分页需要它
from 加 size 的深分页要求协调节点在每个分片上各取前若干条再归并,翻得越深开销越大;search_after 用上一页最后一条的排序值当游标,把开销压到接近常数。但只有游标还不够:索引在翻页期间持续写入,排序值会变化,可能出现重复或漏掉的文档。PIT 把索引视图固定下来,两者配合才能既省资源又保证遍历结果自洽。
PIT 的价值在写入活跃的场景:纯只读索引上 search_after 配固定排序已经足够稳定,不必额外承担 PIT 保留段带来的合并推迟成本。