📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 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 的正确性依赖于一个关键前提:查询结果在遍历过程中不会发生变更。这就需要 scrollpoint-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. 如何排查和解决这个异常 #

建议按以下顺序快速定位问题:

  1. 确认报错请求中是否包含 slice 字段,检查查询 DSL 或 SDK 代码中是否调用了 slice 相关方法。
  2. 检查是否设置了 scroll 参数(URL 参数或请求体中的 scroll 持续时间)。
  3. 检查是否使用了 point_in_timepit,确认请求体中包含有效的 PIT ID 和保持打开时间。
  4. 确认客户端 SDK 版本与 Elasticsearch 版本是否匹配,某些 SDK 旧版本示例会省略 scroll 或 PIT。
  5. 如果是升级后出现的问题,重点检查升级前后查询模板的差异。

排查时需要注意的问题 #

  • slice 不能与普通搜索、search_afterfrom/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 + scrollslice + 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,以获得更好的性能和更长的支持周期。

相关错误 #

附:日志上下文 #

下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:

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());
}