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

适用版本: 6.8-7.13

1. 错误说明 #

unknown array 是 Elasticsearch 在解析快照仓库(Snapshot Repository)元数据文件时抛出的 ElasticsearchParseException 异常。当 Elasticsearch 读取快照元数据(如 index-*.stsnap-*.dat 文件)时,期望遇到特定名称的数组字段(主要是 files),但实际上遇到了未知名称的数组字段,于是抛出此异常并中断解析流程。

常见现象 #

  • 执行 GET _snapshot/<repo>/<snapshot> 或恢复快照时,API 返回 500 Internal Server Error
  • 集群启动时加载快照仓库失败,影响快照相关操作。
  • 在 Elasticsearch 日志中可以看到类似如下的报错信息:
ElasticsearchParseException: unknown array [unexpected_array]
    at org.elasticsearch.snapshots.blobstore.BlobStoreIndexShardSnapshot.fromXContent(...)
  • Kibana 或其他管理工具在访问快照信息时显示加载失败。

典型报错 #

org.elasticsearch.ElasticsearchParseException: unknown array [some_field]
    at org.elasticsearch.snapshots.blobstore.BlobStoreIndexShardSnapshot.fromXContent(BlobStoreIndexShardSnapshot.java:XXX)
Caused by: java.lang.IllegalArgumentException: unknown array [some_field]

2. 原因分析 #

unknown array 异常的根源在于快照仓库中的元数据文件内容不符合当前 Elasticsearch 版本的解析预期。具体原因包括:

  • 仓库数据损坏:底层存储(如 S3、HDFS、NFS)出现故障,导致元数据文件写入不完整或被截断,数组字段名被篡改。
  • 版本不兼容:快照由较新版本的 Elasticsearch 创建,而当前集群版本较旧,新版本引入的数组字段名在旧版本中无法识别;或者反之,旧版本快照在新版本中元数据格式已变更。
  • 手动修改元数据文件:人为直接编辑了仓库中的 index-*.stsnap-*.dat 等二进制或 JSON 格式元数据文件,引入了非预期的数组字段。
  • 跨仓库复制或迁移错误:将快照文件从一个仓库目录手动复制到另一个仓库,未保持目录结构和元数据一致性,导致字段不匹配。
  • 存储层 Bug 或并发写入冲突:在极少数情况下,多个集群同时写入同一个共享仓库,导致元数据文件内容混乱。

3. 解决方案 #

方案一:确认版本兼容性并切换至对应版本 #

在创建快照的相同版本(或官方兼容版本)的 Elasticsearch 集群上执行恢复操作。

# 查看当前集群版本
curl -X GET "localhost:9200"

# 查看快照信息(在兼容版本上执行)
curl -X GET "localhost:9200/_snapshot/my_repo/my_snapshot"

查阅 Elasticsearch 官方快照兼容性文档,确认版本间的兼容性矩阵。

方案二:验证并修复仓库文件完整性 #

直接检查仓库中对应的元数据文件内容,确认是否存在异常字段。

# 如果使用本地仓库,直接查看元数据文件
cat /path/to/repo/indices/<index_uuid>/index-*.st | python -m json.tool

# 如果使用 S3 仓库,使用 aws cli 下载后检查
aws s3 cp s3://my-bucket/indices/<index_uuid>/index-0001.st ./index-0001.st
cat index-0001.st | python -m json.tool

如果发现未知数组字段(如 unexpected_array),需要判断该字段是否为必需内容。若非必需,可尝试在备份后移除该字段再重新上传。

方案三:从健康备份重新恢复仓库 #

如果仓库存在损坏的元数据文件,且无法简单修复,建议从仓库的完整备份中恢复元数据文件。

# 如果有仓库备份,覆盖损坏的文件
cp /backup/repo/indices/<index_uuid>/index-0001.st /path/to/repo/indices/<index_uuid>/index-0001.st

# 重新注册仓库并验证
curl -X POST "localhost:9200/_snapshot/my_repo/_verify"

方案四:重新创建快照 #

如果源集群仍然可用,在兼容版本上重新创建快照,并将新快照写入一个新的仓库路径,避免与旧仓库文件混用。

# 创建新快照
curl -X PUT "localhost:9200/_snapshot/my_new_repo/my_snapshot_2024?wait_for_completion=true" -H 'Content-Type: application/json' -d'
{
  "indices": "my_index",
  "ignore_unavailable": true
}
'

方案五:清理异常快照元数据 #

如果某个快照的元数据已损坏且无法恢复,可以尝试手动删除该快照对应的元数据文件(需先停止所有快照相关操作)。

# 删除指定快照(通过 API,会清理仓库中的对应文件)
curl -X DELETE "localhost:9200/_snapshot/my_repo/broken_snapshot"

4. 预防措施 #

  • 严格版本管理:快照仓库应绑定到特定版本的 Elasticsearch 集群,避免跨大版本混用同一个仓库。升级集群前,先完成快照迁移或重新创建快照。
  • 禁止手动修改仓库文件:快照仓库中的文件由 Elasticsearch 内部管理,严禁人工编辑或脚本直接修改。
  • 使用专用仓库:不同集群、不同用途(备份 vs 迁移)应使用不同的快照仓库路径或存储桶,避免文件交叉污染。
  • 定期验证仓库完整性:通过 _snapshot/<repo>/_verify API 和定期恢复测试(恢复到临时集群)来验证仓库可用性。
  • 做好仓库备份:对存储快照数据的 S3 桶、NFS 目录等配置定期备份策略,防止元数据文件丢失或损坏。
  • 监控仓库存储层:监控底层存储的可用性、延迟和错误率,及时发现存储层故障。

5. 小结 #

unknown array 异常本质上是快照仓库元数据格式与当前 Elasticsearch 解析器预期不一致的信号。排查时应优先确认快照来源版本与当前集群版本的兼容性,其次检查仓库文件的完整性。修复方案以恢复兼容版本操作为首选,无法恢复时再从备份重建或重新创建快照。通过规范仓库使用、禁止手动修改元数据文件、定期验证仓库健康度,可以有效避免此类问题的再次发生。

相关错误 #

附:日志上下文 #

XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser);
String currentFieldName = parser.currentName();
token = parser.nextToken();
if (token == XContentParser.Token.START_ARRAY) {
    if (ParseFields.FILES.match(currentFieldName, parser.getDeprecationHandler()) == false) {
        throw new ElasticsearchParseException("unknown array [{}]", currentFieldName);
    }
    while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
        FileInfo fileInfo = FileInfo.fromXContent(parser);
        files.put(fileInfo.name(), fileInfo);
    }
}