适用版本: 7.15-8.9
1. 错误异常的基本描述 #
当 Elasticsearch 请求中使用了 slice 参数,但既没有设置 scroll,也没有使用 point-in-time(PIT),集群会拒绝该请求并返回以下错误:[slice] can only be used with [scroll] or [point-in-time] requests。这是服务端在查询解析阶段抛出的 SearchException,用于防止 slice 在不具备稳定结果视图的上下文中被误用。
常见现象 #
- 调用搜索 API 时直接返回
400 Bad Request,响应体中包含上述错误信息。 - 使用官方客户端(Java、Python、Go 等)时,SDK 会抛出异常或返回带错误信息的响应对象。
- 在 Kibana Dev Tools 或任意 HTTP 客户端中执行包含
slice的查询时,无论数据量大小,都会立即报错。 - 升级到 7.15+ 版本后,原本正常运行的代码会突然大面积报错。
典型报错与异常栈 #
{
"error": {
"root_cause": [{"type": "search_exception", "reason": "[slice] can only be used with [scroll] or [point-in-time] requests"}],
"type": "search_exception",
"reason": "[slice] can only be used with [scroll] or [point-in-time] requests",
"status": 400
}
}
服务端日志异常栈通常类似:
org.elasticsearch.search.SearchException: [slice] can only be used with [scroll] or [point-in-time] requests
at org.elasticsearch.search.SearchService.parseSource(SearchService.java:...)
2. 为什么会发生这个错误 #
slice 是 Elasticsearch 提供的**并行遍历(parallel scrolling)**机制,用于将一个大查询切分成多个子查询,由多个客户端或线程并发消费,从而加速全量数据导出或离线处理。
slice 的正确性依赖于一个关键前提:查询结果在遍历过程中不会发生变更。这就需要 scroll 或 point-in-time(PIT)提供稳定、一致的结果视图。
从 Elasticsearch 7.15 开始,源码中明确增加了对 slice 使用场景的校验:
if (source.slice() != null) {
if (source.pointInTimeBuilder() == null && context.scrollContext() == null) {
throw new SearchException(shardTarget, "[slice] can only be used with [scroll] or [point-in-time] requests");
}
context.sliceBuilder(source.slice());
}
常见触发原因包括:
- 将
slice当成普通分页参数(from/size)直接使用,未提供scroll或 PIT 上下文。 - 从旧版本升级后,原本可以运行的代码因版本行为变化而开始报错。
- 使用
slice搭配search_after或普通分页,未意识到slice与这些机制互斥。 - 代码中设置了
slice,但遗漏了scroll参数或 PIT 的构造。 - 封装好的搜索模板中
slice被固定写死,但调用方并不总是传入遍历上下文。
3. 如何排查和解决这个异常 #
建议按以下顺序快速定位问题:
- 确认报错请求中是否包含
slice字段,检查查询 DSL 或 SDK 代码中是否调用了slice相关方法。 - 检查是否设置了
scroll参数(URL 参数或请求体中的scroll持续时间)。 - 检查是否使用了
point_in_time(pit),确认请求体中包含有效的 PIT ID 和保持打开时间。 - 确认客户端 SDK 版本与 Elasticsearch 版本是否匹配,某些 SDK 旧版本示例会省略
scroll或 PIT。 - 如果是升级后出现的问题,重点检查升级前后查询模板的差异。
排查时需要注意的问题 #
slice不能与普通搜索、search_after或from/size分页混用。- PIT 需要在请求体中通过
pit.id引用,而不是 URL 参数。 slice.max建议设置为索引主分片数,过大或过小只影响性能,不会触发此错误。
4. 如何解决这个错误 #
根据实际业务场景,选择以下三种方案之一:
方案一:使用 scroll + slice(传统方式) #
适合需要向后兼容 7.x 老版本,或已有 scroll 代码的场景。
POST /my-index/_search?scroll=5m
{ "slice": { "id": 0, "max": 4 }, "query": { "match_all": {} }, "size": 1000 }
用多个线程分别用 id: 0/1/2/3 并发执行上述查询,各自独立消费 scroll 上下文,完成后再分别清除 scroll。
方案二:使用 PIT + slice(推荐的新方式) #
Elasticsearch 7.10+ 引入了 PIT,8.x 中 scroll 已被标记为废弃。推荐使用 PIT 替代 scroll。
第一步:创建 PIT POST /my-index/_pit?keep_alive=5m
第二步:使用 PIT + slice 并行遍历
POST /_search
{ "pit": { "id": "得到的pit_id", "keep_alive": "5m" }, "slice": { "id": 0, "max": 4 }, "query": { "match_all": {} }, "size": 1000, "sort": [{ "_shard_doc": "asc" }] }
方案三:普通搜索移除 slice #
如果业务场景只是普通分页查询或列表展示,不需要并行遍历,直接删除 slice 参数即可:POST /my-index/_search { "query": { "match_all": {} }, "from": 0, "size": 20 }
5. 预防建议 #
- 升级前做兼容性检查:升级 Elasticsearch 大版本时,全面排查
slice的使用场景,确保均已搭配scroll或 PIT。 - 统一遍历工具类并增加测试覆盖:将 scroll 遍历、PIT 遍历封装为独立工具类,并在自动化测试中覆盖
slice + scroll、slice + PIT及裸slice调用三种场景。 - 优先迁移到 PIT:scroll 已在 Elasticsearch 8.x 中被标记为废弃,新项目应直接使用 PIT + slice。
- 合理设置 slice 数量:
slice.max建议设置为索引主分片数,过多切片不会提升性能,反而增加协调节点负担。
6. 小结 #
[slice] can only be used with [scroll] or [point-in-time] requests 的本质含义是:slice 依赖稳定的结果视图才能正确工作。修复时只需确保 slice 总是出现在 scroll 或 PIT 上下文中;如果不需要并行遍历,则直接移除 slice。建议逐步将 scroll 方案迁移到 PIT,以获得更好的性能和更长的支持周期。
相关错误 #
- slice-cannot-be-used-outside-of-a-scroll-context-how-to-solve-this-elasticsearch-exception
- search-after-cannot-be-used-in-a-scroll-context-how-to-solve-this-elasticsearch-exception
附:日志上下文 #
下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:
if (source.slice() != null) {
if (source.pointInTimeBuilder() == null && context.scrollContext() == null) {
throw new SearchException(shardTarget, "[slice] can only be used with [scroll] or [point-in-time] requests");
}
context.sliceBuilder(source.slice());
}





