Elasticsearch 索引中具有相同 _uid 的重复文档

Duplicate documents in Elasticsearch index with the same _uid

我们在我们的一个 Elasticsearch 索引中发现了一些重复的文档,但我们无法找出原因。每个受影响的文档都有两个副本,它们具有完全相同的 _id_type_uid 字段。

一个GET请求到/index-name/document-type/document-id只是returns一份,但是用这样的查询来搜索文档returns两个结果,这很令人惊讶:

POST /index-name/document-type/_search
{
  "filter": {
    "term": {
      "_id": "document-id"
    }
  }
}

_uid 字段上聚合也识别重复文档:

POST /index-name/_search
{
  "size": 0,
  "aggs": {
    "duplicates": {
      "terms": {
        "field": "_uid",
        "min_doc_count": 2
      }
    }
  }
}

重复项都在不同的分片上。例如,一个文档可能在主分片 0 上有一个副本,在主分片 1 上有一个副本。我们已经通过 运行 在每个分片上依次使用 preference parameter 的聚合查询验证了这一点:它在单个分片中找不到任何重复项。

我们最好的猜测是路由出了点问题,但我们不明白副本是如何路由到不同的分片的。根据 routing documentation,默认路由基于文档 ID,并且应该一致地将文档路由到同一个分片。

我们没有使用会覆盖默认路由的自定义路由参数。我们通过确保重复文档没有 _routing 字段来仔细检查。

我们也没有定义任何也会影响路由的 parent/child 关系。 (例如,参见 this question in the Elasticsearch forum,它与我们的问题具有相同的症状。我们认为原因不同,因为我们没有设置任何文档父项)。

我们通过重新索引到新索引来解决眼前的问题,该索引压缩了重复的文档。我们仍有旧索引可供调试。

我们还没有找到重现该问题的方法。新索引正确地为文档编制索引,我们已经尝试重新运行 通宵处理作业,该作业也会更新文档,但它没有创建更多重复项。

集群有3个节点,3个主分片和1个副本(即3个副本分片)。 minimum_master_nodes 设置为 2,这应该可以防止 split-brain 问题。我们是 运行 Elasticsearch 2.4(我们知道它很旧 - 我们计划很快升级)。

有谁知道可能导致这些重复项的原因是什么?您对调试方法有什么建议吗?

我们找到了答案!问题是索引意外地切换了它用于路由的哈希算法,这导致一些更新的文档存储在不同的分片上到它们的原始版本。

/index-name/_settings 的 GET 请求揭示了这一点:

"version": {
  "created": "1070599",
  "upgraded": "2040699"
},
"legacy": {
  "routing": {
    "use_type": "false",
    "hash": {
      "type": "org.elasticsearch.cluster.routing.DjbHashFunction"
    }
  }
}

“1070599”是指Elasticsearch 1.7,“2040699”是ES 2.4。

看起来索引试图将自己从 1.7 升级到 2.4,尽管事实上它已经是 运行 2.4。这是此处描述的问题:https://github.com/elastic/elasticsearch/issues/18459#issuecomment-220313383

我们认为这是触发变化的原因:

  1. 当我们将索引从 ES 1.7 升级到 2.4 时,我们决定不就地升级 Elasticsearch,因为那样会导致停机。相反,我们创建了一个单独的 ES 2.4 集群。

    我们使用复制所有索引设置和数据的工具将数据加载到新集群中,包括 version 设置 you should not set in ES 2.4

  2. 在处理最近的一个问题时,我们碰巧关闭并重新打开了索引。这通常会保留所有数据,但由于 version 设置不正确,导致 Elasticsearch 认为正在处理升级。

  3. ES 自动设置了 legacy.routing.hash.type 设置因为错误的升级。这意味着在这一点之后索引的任何数据都使用旧的 DjbHashFunction 而不是最初用于路由数据的默认值 Murmur3HashFunction

这意味着将数据重新索引到新索引中是解决问题的正确做法。新索引具有正确的版本设置并且没有遗留哈希函数设置:

"version": {
  "created": "2040699"
}