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

适用版本: 7.x-8.9

1. 错误异常的基本描述 #

Failed to get id [id] with includes/excludes set 表示 Elasticsearch 已经取到了文档的 _source,但在按照 includes/excludes 规则做字段过滤时失败。相比普通的 Failed to get id [id],这个异常更明确地指向 _source 过滤阶段,而不是 stored fields 初始定位阶段。

从日志片段可见,异常发生在 XContentFieldFilter.newFieldFilter(...).apply(source, null)。这说明问题出在 _source 的过滤与重写过程,而不是文档定位过程。

常见现象 #

  • 只有带 _source_includes_source_excludes 或等价参数的 Get 请求失败,返回 500 内部服务器错误。
  • 不带 source 过滤时读取正常(如 GET /index/_doc/id 不加参数),带过滤条件时却失败。
  • 使用 _source 参数指定返回的字段时触发异常,例如 GET /index/_doc/id?_source_includes=field1,field2
  • 日志中常伴随 _source 解析、内容转换或 I/O 相关异常。
  • 如果是批量查询(_mget),只有部分指定了 source 过滤的文档会失败。

典型报错与异常栈 #

常见日志形态通常类似下面这样:

ElasticsearchException: Failed to get id [abc123] with includes/excludes set
Caused by: java.io.IOException: read past EOF
	at org.elasticsearch.common.xcontent.XContentFieldFilter...

或者 _source 解析异常:

ElasticsearchException: Failed to get id [abc123] with includes/excludes set
Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected character
	at com.fasterxml.jackson.core.JsonParser...

或者内容截断:

ElasticsearchException: Failed to get id [abc123] with includes/excludes set
Caused by: java.io.EOFException: null
	at org.elasticsearch.common.xcontent.XContentHelper...

2. 为什么会发生这个错误 #

Failed to get id [id] with includes/excludes set 的根因是"在 Get 请求中,_source 内容过滤过程失败"。Elasticsearch 需要先获取完整的 _source,然后根据 includes/excludes 规则进行字段过滤;如果 _source 内容本身有问题,或者过滤过程出现异常,就会抛出此异常。

常见原因通常包括:

  • _source 内容损坏:文档的 _source JSON 内容损坏、截断或不是可正常解析的 XContent 格式。
  • _source 格式异常:写入时数据格式有问题(如非 UTF-8 编码、非法 JSON 字符),导致解析失败。
  • includes/excludes 规则复杂叠加:过滤规则与实际文档结构复杂叠加(如嵌套对象、数组),触发过滤阶段异常。
  • 底层 I/O 问题:节点在处理 _source 时遭遇底层 I/O 问题,如磁盘读取失败、文件句柄异常。
  • 大文档问题_source 特别大的文档在过滤时可能触发内存或缓冲区问题。
  • 字段路径不存在:includes/excludes 中指定的字段路径在文档中不存在,且过滤逻辑对此处理不当。
  • 历史数据问题:某些异常文档内容在无过滤时还能原样返回,但一旦需要解析过滤就会暴露问题。

3. 如何排查和解决这个异常和解决这个异常 #

建议按"先复现问题、再检查 _source、后优化过滤规则"的顺序处理:

  1. 复现问题:分别测试"无 includes/excludes"和"有 includes/excludes"的 Get 请求,确认问题触发条件。

    # 不带过滤,查看是否能正常获取
    curl -X GET "localhost:9200/my_index/_doc/my_id?pretty"
       
    # 带过滤,查看是否触发异常
    curl -X GET "localhost:9200/my_index/_doc/my_id?_source_includes=field1,field2&pretty"
    
  2. 检查 _source 内容:抓取目标文档原始 _source,确认其内容是否完整和合法。

    # 获取原始 _source
    curl -X GET "localhost:9200/my_index/_doc/my_id?_source=true" > source.json
       
    # 验证 JSON 格式
    cat source.json | jq .  # 如果 jq 解析失败,说明 JSON 有问题
    
  3. 简化过滤规则:逐步缩小触发异常的字段路径,定位是哪个字段过滤导致的问题。

    # 逐个字段测试
    curl -X GET "localhost:9200/my_index/_doc/my_id?_source_includes=field1"
    curl -X GET "localhost:9200/my_index/_doc/my_id?_source_includes=field2"
    
  4. 检查节点日志:结合节点日志确认是 _source 解析异常还是底层 I/O 异常。

    # 查看相关错误日志
    grep -r "Failed to get id.*includes/excludes" /var/log/elasticsearch/
    grep -r "XContentFieldFilter" /var/log/elasticsearch/
    
  5. 检查文档大小:确认文档是否过大,导致过滤时出现问题。

    # 查看文档大小(通过 _size 字段,需要 mapper-size 插件)
    curl -X GET "localhost:9200/my_index/_doc/my_id?stored_fields=_size"
    
  6. 检查历史数据:如果问题只发生在少量旧文档上,重点检查历史写入链路和数据修复记录。

排查时需要注意的问题 #

  • 如果 _source 本身损坏,不带过滤时可能也能返回数据(只是内容有问题),但过滤时需要解析完整内容,所以会暴露问题。
  • includes/excludes 支持通配符和路径表达式,复杂规则可能匹配到意外的字段,需要仔细检查。
  • 对于大文档,过滤操作可能消耗较多内存,需要确保节点有足够资源。

4. 如何解决这个错误 #

常用修复思路 #

  • 修复或删除损坏文档:对于 _source 损坏的文档,可以尝试重新索引或删除。

    # 删除损坏的文档
    curl -X DELETE "localhost:9200/my_index/_doc/my_id"
      
    # 重新索引(如果有原始数据)
    curl -X PUT "localhost:9200/my_index/_doc/my_id" -H 'Content-Type: application/json' -d'
    {
      "field1": "value1",
      "field2": "value2"
    }
    '
    
  • 简化过滤规则:修正 includes/excludes 表达式,避免对异常结构做深层过滤。

    # 使用更简单的过滤规则
    # 原来可能过于复杂:?_source_includes=obj1.*.field,obj2.nested[*].value
    # 改为更简单的:?_source_includes=field1,field2
    curl -X GET "localhost:9200/my_index/_doc/my_id?_source_includes=field1,field2"
    
  • 重建索引:如果多个文档都有问题,考虑重建整个索引。

    # 重建索引(跳过无法解析的文档)
    curl -X POST "localhost:9200/_reindex?pretty" -H 'Content-Type: application/json' -d'
    {
      "source": {
        "index": "old_index"
      },
      "dest": {
        "index": "new_index"
      },
      "conflicts": "proceed"  # 跳过冲突文档
    }
    '
    
  • 修复底层 I/O 问题:若底层 I/O 不稳定,先恢复节点与存储健康状态(参考 failed-to-get-id-id 的处理方法)。

  • 禁用 source 过滤:作为临时解决方案,可以暂时不使用 source 过滤,或者改用 stored_fields 参数。

    # 使用 stored_fields 代替 _source 过滤(需要字段设置为 store=true)
    curl -X GET "localhost:9200/my_index/_doc/my_id?stored_fields=field1,field2"
    

后续注意事项与推荐建议 #

  • 在应用层对写入 Elasticsearch 的数据进行校验,确保 _source 内容是合法的 JSON 格式。
  • 对于重要索引,考虑在 mapping 中设置 _sourceenabledfalse(如果不需要原始 _source),或者只存储必要的字段。
  • 建立对大文档的监控,在文档大小异常增长时及时预警。
  • _source 过滤操作配置适当的超时和内存限制,避免大文档导致节点内存压力。

借助 INFINI 产品提升排障效率 #

  • INFINI Console 适合查看索引的文档样本、字段分布和 _source 内容,帮助快速判断是特定文档问题还是系统性问题,并提供可视化的文档查看和编辑功能。
  • INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测和流量治理,可以记录所有 Get 请求的详细日志,包括 _source 过滤参数,帮助定位是请求参数问题还是数据问题,同时提供请求缓存功能减少重复的过滤操作。
  • 建议将文档读取失败、source 过滤异常等指标统一接入监控面板,结合 INFINI Console 的告警功能,在文档质量问题频发时及时通知。

5. 小结 #

Failed to get id [id] with includes/excludes set 说明文档已经找到了,但 _source 过滤失败。排查时要把重点放在原始 _source 的健康状况和 includes/excludes 规则本身,而不是只看 ID 是否存在。大多数情况下,这个问题可以通过检查 _source 内容完整性、简化过滤规则和修复损坏文档来解决。

只要把数据校验、文档监控和过滤规则管理固定下来,大多数 _source 过滤类异常都可以被快速定位和恢复,也更容易通过 INFINI Console 和 INFINI Gateway 实现持续防护。

相关错误 #

参考文档 #

附:日志上下文 #

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

} else if (fetchSourceContext.includes().length > 0 || fetchSourceContext.excludes().length > 0) {
    try {
        source = XContentFieldFilter.newFieldFilter(fetchSourceContext.includes(); fetchSourceContext.excludes())
            .apply(source; null);
    } catch (IOException e) {
        throw new ElasticsearchException("Failed to get id [" + id + "] with includes/excludes set"; e);
    }
}
}  return new GetResult(