前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于重建索引 API 使用和故障排查的 3 个最佳实践

关于重建索引 API 使用和故障排查的 3 个最佳实践

原创
作者头像
周银辉
发布2024-05-09 16:24:53
1340
发布2024-05-09 16:24:53

重建索引API功能:

  • 在集群之间传输数据 
  • 重新定义、更改和/或更新映射
  • 通过采集管道进行处理和编制索引
  • 通过清除已删除的文档回收存储空间
  • 通过查询筛选器将大型索引拆分成较小的索引组

常见问题处理

症状:Kibana 开发工具中显示“backend closed connection”(后端已关闭连接)

问题

您的客户端将在 N 秒后关闭非活动套接字;以 Kibana 为例,如果重建索引操作无法在 120 秒内(v7.13 中默认的 server.socketTimeout 值)完成,您将看到“backend closed connection”(后端已关闭连接)消息。

解决方案 #1 - 获取在集群上运行的任务列表

其实这并不是问题,即使您在 Kibana 中看到这条消息,Elasticsearch 也会在后台运行重建索引 API。

您可以使用 _task API 跟踪重建索引 API 的执行情况,并查看所有指标:

代码语言:javascript
复制
GET _tasks?actions=*reindex&wait_for_completion=false&detailed

这个 API 将向您显示当前在 Elasticsearch 集群中运行的所有重建索引 API,如果您在此列表中没有看到您的重建索引 API,即表明它已完成。

解决方案 #2 - 将重建索引结果存储在 _tasks 上

如果已知重建索引操作需要的时间超过 120 秒(120 秒是 Kibana 开发工具的超时时间),可以使用查询参数 wait_for_completion= false 来存储重建索引 API 的结果,这样您就能使用 _task API 来获取重建索引 API 结束时的状态(也可以从“.tasks”索引获取文档,如 wait_for_completion=false 的文档中所述)。

代码语言:javascript
复制
POST _reindex?wait_for_completion=false
{
  "conflicts": "proceed",
  "source": {
    "index": "<source-index-name>"
  },
  "dest": {
    "index": "<dest-index-name>"
  }
}

当您使用“wait_for_completion=false”执行重建索引时,响应将类似于以下内容:

代码语言:javascript
复制
{
  "task" : "a9Aa_I_ZSl-4bjR5vZLnSA:247906"
}

您需要保留这里提供的任务,搜索重建索引的结果时会用到(您将看到已创建的文档数、冲突甚至是错误;完成后,您将看到所花费的时间、批次数等等):

代码语言:javascript
复制
GET _tasks/a9Aa_I_ZSl-4bjR5vZLnSA:247906

症状:_task API 列表中没有您的重建索引 API。

如果使用上文提到的 API 无法找到重建索引 API 操作,可能这又是另一个问题,下面我们一个一个地解决。

问题

如果重建索引 API 不在列表中,即表明操作已完成,因为没有更多的文档需要重建索引,或者是因为出现了错误。

我们将使用 _cat count API 来查看存储在两个索引中的文档数量,如果两个数值不同,则表明您的重建索引 API 执行已失败。

代码语言:javascript
复制
GET _cat/count/<source-index-name>?h=count
GET _cat/count/<dest-index-name>?h=count

您需要将 / 替换为您在重建索引 API 中使用的索引名称。

解决方案 #1 - 这是一个冲突问题

最常见的错误之一是存在冲突,默认情况下,如果有冲突,重建索引 API 将中止。

现在,我们有两个选择:

  1. 将“conflicts”设置为“proceed”,这样重建索引 API 将忽略无法索引的文档,转而索引其他文档。
  2. 或者,我们也可以选择修复冲突,这样就可以为所有文档重建索引。

第一个选择中的冲突设置如下所示:

代码语言:javascript
复制
POST _reindex
{
  "conflicts": "proceed",
  "source": {
    "index": "<source-index-name>"
  },
  "dest": {
    "index": "<dest-index-name>"
  }
}

或者,在第二个选择中,我们将搜索并修复产生冲突的错误:

  • 避免这一问题的最佳实践是在目标索引上定义映射或模板。这些错误中 99% 是源索引和目标索引之间的字段类型不匹配。
  • 如果在定义了映射或模板后,问题仍然存在,则表明某些文档可能无法建立索引,并且默认情况下不会记录错误。我们需要启用记录器,以便在 Elasticsearch 日志中查看错误。
代码语言:javascript
复制
PUT /_cluster/settings
{
  "transient": {
    "logger.org.elasticsearch.action.bulk.TransportShardBulkAction":"DEBUG"
  }
}
  • 启用记录器后,我们需要再执行一次重建索引 API,如果可能的话,将“conflicts”设置为“proceed”,因为很多时候源索引和目标索引之间会有多个字段发生冲突。
  • 现在,重建索引 API 正在运行,我们将在日志中使用 grep 命令查找/搜索“failed to execute bulk item”或“MapperParsingException”
代码语言:javascript
复制
failed to execute bulk item (index) index {[my-dest-index-00001][_doc][11], source[{
  "test-field": "ABC"
}

代码语言:javascript
复制
 "org.elasticsearch.index.mapper.MapperParsingException: failed to parse field [test-field] of type [long] in document with id '11'. Preview of field's value: 'ABC'",
                "at org.elasticsearch.index.mapper.FieldMapper.parse(FieldMapper.java:216) ~[elasticsearch-7.13.4.jar:7.13.4]",

通过这个堆栈跟踪,我们已经有足够的信息来理解冲突是什么。在我的重建索引 API 中,目标索引有一个名为 [test-field] 的字段,类型为 [long],重建索引 API 尝试将该字段设置为字符串“ABC”(您可以用自己的内容字段替换“ABC”)。

在 Elasticsearch 中,字段数据类型是可以定义的,您可以在索引创建期间或使用模板设置这些类型。索引创建完成后,类型便不能更改,您需要先删除目标索引,然后使用之前提供的选项来设置新的固定映射。

  • 修复了错误之后,请记得将记录器转换为没那么冗长的模式:
代码语言:javascript
复制
PUT /_cluster/settings
{
  "transient": {
    "logger.org.elasticsearch.action.bulk.TransportShardBulkAction":NULL
  }
}

解决方案 #2 - 没有冲突错误,但重建索引仍然失败

如果在重建索引执行期间,您在 Elasticsearch 日志中发现以下跟踪:

代码语言:javascript
复制
'search_phase_execution_exception', 'No search context found for id [....]')

可能有很多种原因:

  1. 集群存在一些不稳定问题,并且在重建索引执行过程中,一些数据节点进行了重启或不可用。 如果是这个原因,在运行重建索引之前,请确保集群是稳定的,且所有数据节点都运行良好。
  2. 如果您是远程执行重建索引操作,并且已知节点之间的网络不可靠:
    • 建议选择快照 API(如本文结尾处所述)。
  3. 我们可以尝试对重建索引 API 执行手动切片,该操作可以将请求过程分割成较小的部分(当我们在同一集群中使用重建索引 API 时,可以使用这个选项)。
  4. 如果您的 Elasticsearch 集群存在过度分片、资源利用率高或垃圾收集问题,可能会在滚动搜索查询过程中出现超时。默认的滚动超时值为 5 分钟,因此,您可以尝试将重建索引 API 上的滚动设置为一个更高的值。
代码语言:javascript
复制
POST _reindex?scroll=2h
{
 "source": {
"index": "<source-index-name>"
  },
  "dest": {
"index": "<dest-index-name>"
  }
}

症状:Elasticsearch 日志中显示“节点未连接”

我们始终建议在集群稳定且状态为绿色的情况下运行重建索引 API,集群需要足够的容量才能运行搜索和索引操作。

问题

代码语言:javascript
复制
NodeNotConnectedException[[node-name][inet[ip:9300]] Node not connected]; ]

如果您的日志中出现这类错误,表明您的集群存在稳定性和连接问题,而不仅仅是重建索引 API 出现了问题。

但不妨设想一下,已知存在连接问题,但是需要运行重建索引 API,该怎么办。

解决方案

修复连接问题。

但是,假设我们知道有连接问题,可是需要运行重建索引 API,我们可以减少失败的可能性,不过这不是修复操作,并不是在所有情况下都有效。

  • 将源索引或目标索引(主索引或副本)的分片移出存在连接问题的节点。使用分配筛选 API 移动分片。
  • 您也可以移除目标索引上的副本(仅针对目标索引),这将加快重建索引 API 的执行速度,毕竟重建索引的运行速度越快,出现故障的可能性就越小。
代码语言:javascript
复制
PUT /<dest-index-name>/_settings
{
  "index" : {
    "number_of_replicas" : 0
  }
}

如果这两种操作都无法让您成功执行重建索引 API,表明您需要先修复稳定性问题。 

症状:日志中没有错误,但两个索引的文档计数不一致

有时,重建索引 API 已经完成,但是源索引与目标索引中的文档计数不一致。

问题

如果我们尝试在一个目标中从多个源重建索引(即在一个目标中合并多个索引),问题可能源自您为这些文档分配的 _id。

假设我们有 2 个源索引:

  • 索引 A,_id:1,信息:“Hello A”
  • 索引 B,_id:1,信息:“Hello B”

两个索引在 C 中合并后:

  • 索引 C,_id:1,信息:“Hello B”

两个文档的 _id 相同,因此,最后索引的文档将覆盖前一个索引的文档。

解决方案

您可以选择不同的采集管道,也可以在重建索引 API 中使用 Painless。在这篇博文中,我们将使用脚本选项,在请求正文中使用“Painless”。

操作起来非常简单,只需使用原始 _id 并添加源索引名称:

代码语言:javascript
复制
POST _reindex
{
  "source": {
    "index": "<source-index-name>"
  },
  "dest": {
    "index": "<dest-index-name>"
  },
  "script": {
    "source": "ctx._id = ctx._id + '-' + ctx._index;"
  }
}

还是前面的示例:

  • 索引 A,_id:1,信息:“Hello A”
  • 索引 B,_id:1,信息:“Hello B”

两个索引在 C 中合并后:

  • 索引 C,_id:1-A,信息:“Hello A”
  • 索引 C,_id:1-B,信息:“Hello B”

最佳实践

并发切片与size设置

Reindex支持切片滚动,以并行重建进程。这种并行化可以提高效率,并提供一种方便的方式将请求分解为更小的部分。默认reindex 读取源索引的 batch_size 为1000,这个size的使用限制于系统资源的分配,可以调整到10000增加速度.并发数主要通过url后的slices参数控制,下面的例子里 size设置为1w,并发数为5 .

代码语言:javascript
复制
POST _reindex?slices=5&refresh
{
  "conflicts": "proceed",
  "source": {
    "index": "my-index-000001",
    "size": 10000,
    "query":{
       "range":{
         "FIELD": {
            "gte": 10,
            "lte": 20
          }
       }
     }
  },
  "dest": {
    "index": "my-new-index-000001"
  }
}

怎么选择切片的数量

  1. 使用自动切片,将切片设置为auto, ES 会自行设置一个合理的数字。
  2. 设置slice数量与索引中的分片数量相等时,查询性能是最有效的。通常情况下,将slice的数量设置为高于shard的数量并不会提高效率,反而会增加开销。

在生产使用中,请通过测试设置合理的slice数量提高reindex的效率。

实际测试中,一个1.5tb的24分片索引(集群配置32c64g,24节点,索引1副本,目标索引未设置副本),使用48 slice 需要2小时完成,24 slice 则需要3小时。

reindex 减索引字段

在 source 的 query 中限定 includes 的字段,这样可以在 reindex 时去除原索引不需要的字段

代码语言:javascript
复制
POST _reindex?slices=5&refresh
{
  "conflicts": "proceed",
  "source": {
    "index": "my-index-000001",
    "size": 10000,
    "query":{
       "range":{
         "FIELD": {
            "gte": 10,
            "lte": 20
          }
       }
     },
     "_source":{
     "includes":["_id","aaa","bbb"],
     "excludes":["xxx","yyy","zzz"]
     }
  },
  "dest": {
    "index": "my-new-index-000001",
    "version_type":"external"
  }
}

5、关于写入的版本操作

此处引用携程wood大叔在社区上的一个例子:

version_type 的internal, external都是从dest索引的视角看过去的,internal表示使用dest内部版本, external表示使用source的版本。

假设source索引有3条文档:

id: 1 text: "a" version: 4

id: 2 text: "b" version: 3

id: 3 text: "c" version: 2

dest索引有2条文档:

id: 1 text: "e" version: 1

id: 2 text: "f" version: 5

如果将source reindex到dest,文档1和2会产生冲突。

使用不同的version type做reindex,dest里的文档内容和版本变化的结果如下:

version type = internal

id: 1 text: "a" version: 2 # 覆盖 & 内部版本号+1

id: 2 text: "b" version: 6 # 覆盖 & 内部版本号+1

id: 3 text: "c" version: 1 # 新文档,创建,版本号为1

version type = external:

id: 1 text: "a" version: 4 # 外部版本号> 内部,因此覆盖文档 & 保留外部版本号4

id: 2 text: "f" version: 5 # 外部版本号小于内部,版本冲突,保留文档和内部版本号5

id: 3 text: "c" version: 2 # 新文档,创建,并保留外部版本号2

op_type = create:

id: 1 text: "e" version: 1 # dest已有文档忽略不处理

id: 2 text: "f" version: 5 # dest已有文档忽略不处理

id: 3 text: "c" version: 1 # dest没有此文档,新创建(create),版本号为1

6、reindex 生产操作流程

前提条件:有时间戳字段,保证数据查询的范围有效。使用时间戳字段来分批执行reindex,这样来减少因reindex导致的数据停写时间。如没有时间戳字段,则整个reindex需要在源索引停止写入后操作

主要操作流程:

1、新建新索引,设置好新的字段mapping和setting;

2、根据时间字段进行reindex,将大部分数据写入新索引;

3、如果步骤2耗时漫长,比如3小时,则根据时间进行第二轮数据reindex;

4、停止数据写入,进行最后一轮的reindex。为了减少停服时间,query的时间窗口控制在半小时内,数据量控制在整体数据集的10分之一以下。

5、比对新旧索引数据量,正确后进行索引别名切换,无索引别名则应用程序切换至新索引。

7、对于大索引reindex的异步处理方案

方案:利用镜像把数据复制出来在非生产环境处理

具体操作:

1.设置临时镜像路径,提前一天把索引先镜像copy出来

代码语言:javascript
复制
PUT _snapshot/ops-es-snapshot-ol
备份索引

2.在单独的非生产集群reindex,注意新索引不需要带别名

3.第二天晚上把结果索引镜像复制回生产环境

4.利用时间戳把新旧索引的数据差补上。注意:delete操作不可回放

5.数据校验无误后,需要别名切换的进行别名切换,再应用切换

6.删除生产的临时镜像路径

8、磁盘空间不足问题

增加副本导致索引数据成倍增长,磁盘空间需要评估能否满足需求,最好提前扩容。

如果创建副本期间磁盘空间告警:

1. 空间完全不足以容纳新增的副本,快速动态调整副本数量设置为0

PUT /shimo_v4/_settings { "number_of_replicas": 0 }

2. 空间勉强容纳新增副本,需要调高磁盘水位线,防止触发水位限制导致限流,数据写入失败

cluster.routing.allocation.disk.watermark.low cluster.routing.allocation.disk.watermark.high

9、reindex期间双写问题

问题:因业务不能停止写入,服务滚动重启期间存在新旧索引双写的问题,部分数据可能双边更新,应用切换完成后,不能简单覆盖。

解决方法:多次reindex操作(全量+增量+补数据),version_type统一使用external模式。以source版本为准,dest不存在的doc直接插入,version版本比dest大的覆盖更新,version版本比dest小的版本冲突,同时需设置conflicts="proceed",确保冲突不会中断reindex操作。

优势:

  1. reindex对生产的资源使用减少,影响时间也更小
  2. reindex的时间窗口和资源配置更加灵活,成功率也更高
  3. 大索引下(500g以上)镜像备份的时间远远小于reindex的耗时,这样能有效减少生产变更的耗时

建议:

鉴于 reindex 在 1TB 以上数据量糟糕的表现(时间长,速度衰迭严重,任务完成无保障),大索引的重刷还是使用logstash 分段任务来处理更合适。

结论

当您需要更改某些字段的格式时,重建索引 API 是一个不错的选择。下面我们将列出一些关键方面,确保重建索引 API 尽可能顺利地运行:

  1. 为目标索引创建并定义映射(或模板)。
  2. 调整目标索引,使重建索引 API 尽可能快地索引文档。我们有一个文档页面,其中提供了用于调整和加快索引速度的所有选项。 https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html
  3. 如果源索引的大小为中型或大型,请定义“wait_for_completion=false”设置,以便重建索引 API 结果存储 在 _tasks API 上。
  4. 将索引分成更小的组,您可以使用查询(范围、术语等)定义不同的组,或者使用切片功能将请求分成较小的部分。
  5. 运行重建索引 API 时,稳定性是关键因素,参与重建索引 API 的索引需要处于绿色状态(最糟糕的情况是黄色状态),然后确保我们的数据节点中没有很长的 GarbageCollections,并且 CPU 和磁盘使用率为正常值。

从 v7.11 开始,我们发布了一项新功能,让您无需为数据重建索引,这项功能称为“运行时字段”。使用这个 API 可以修复错误,而无需为数据重建索引,因为您可以在索引映射搜索请求中定义运行时字段。您可以通过这两种方式在采集数据后灵活地更改文档的模式,并生成只作为搜索查询的一部分存在的字段。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 重建索引API功能:
  • 常见问题处理
    • 症状:Kibana 开发工具中显示“backend closed connection”(后端已关闭连接)
      • 问题
      • 解决方案 #1 - 获取在集群上运行的任务列表
      • 解决方案 #2 - 将重建索引结果存储在 _tasks 上
    • 症状:_task API 列表中没有您的重建索引 API。
      • 问题
      • 解决方案 #1 - 这是一个冲突问题
      • 解决方案 #2 - 没有冲突错误,但重建索引仍然失败
    • 症状:Elasticsearch 日志中显示“节点未连接”
      • 问题
      • 解决方案
    • 症状:日志中没有错误,但两个索引的文档计数不一致
      • 问题
      • 解决方案
      • 并发切片与size设置
      • reindex 减索引字段
  • 最佳实践
    • 结论
    相关产品与服务
    Elasticsearch Service
    腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档