Elasticsearch 近期问题汇总

Elasticsearch 近期问题汇总

协调节点长时间 GC 导致查询延迟增长

很多朋友会使用 nginx 作为 elasticsearch的网关,需要注意一个问题, nginx 探活是 TCP 层面的,而非 http 层面。当其下挂的协调节点长时间 GC,甚至脱离集群,nginx不会将其摘除。导致一些无辜的查询被发到 GC 的节点,查询延迟激增。

解决方式:增加 http 层面的探活,扩容协调节点或增加其 JVM 内存。

在集群缩容后,需要考虑重新调整 total_shard_per_node

total_shard_per_node 的设置只能集群节点数量不变的情况下是正确的。当硬件故障等原因集群缩容时,需要考虑重新设置该值,否则可能会影响分片分配过程。例如 exlude 节点发现无法排除,分配不执行迁移。

7.7.0 动态模板导致大量 put mapping 问题的优化

动态模板会导致大量 put mapping,写入触发的 put mapping 通常大于 master 处理速度。该 pr 对数据节点发送 put mapping 的请求进行了限制,特定数量的请求未完成前,不会发起下一个。默认 indices.mapping.max_in_flight_updates=10

Block too many concurrent mapping updates #51038 (issue: #50670)

7.6 之前的版本 shard-started RPC 造成 Master pending_taks堆积问题

故障表现

在分片数量较多的集群,当调整分片副本数等操作时,集群无法响应 put mapping 等操作,导致业务数据无法写入

基本原理

调整分片副本数,reopen 索引等操作时,shard-started RPC。当 master 将分片指定给某个节点,节点正常打开该分片后,会通知 master “我已经正常打开”,master 就不会再考虑把他分给别人。在集群分片数量较多的集群(5w问题明显),该 RPC需要较长的处理时间(数十秒),他有次高优先级,当并行的 rebalance,reopen 操作较多时, 导致 shard-started RPC几乎完全占据了 master 的优先队列,导致其他较低优先级的任务没有机会执行,例如 put mapping

解决方案

有两个 pr 解决此问题。

  1. Defer reroute when starting shards(7.4)

应用此 pr 之前,Master 对 shard-started 的处理中会执行 reroute,这其实没有必要,因此将对 shard-started 处理过程中的 reroute 调用去掉,改到 shard-started 处理完成后,发起一个低优先级的 reroute。
https://github.com/elastic/elasticsearch/pull/44433
效果:shard-started task 的处理时间从原来的30-40秒降低到1秒内。reroute作为独立的正常优先级执行,比 put mapping 低一个优先级(shard-started 本身处理应该很快,只是 reroute 占据了大部分时间)

  1. 提升 reroute 的处理速度(7.6)

每次reroute 函数会计算所有涉及 shard routing 变更的逻辑,其中 BalancedShardsAllocator负责将分片数量在节点直接保持均匀。在此过程中需要考虑 DiskThresholdDecider,他会考虑 relocating 到节点的数据量,因此计算所有 INIT 和 RELOCATING 的分片,在此过程消耗了太多时间
下面的 pr 优化了此处理过程:
https://github.com/elastic/elasticsearch/pull/47817
效果:reroute task处理时间从原来的10-20秒降低到 1秒内(不计publish 时间)

整体效果:单个分片的 shard started 处理时间从 30 秒降低到 3 秒左右,大量 shard started 处理期间可以正在执行 put mapping,延迟为 十几秒。
例外情况:如果正常优先级的 reroute 执行时间超过 30 秒,put mapping 仍然可能会有失败的情况,但不是完全没有机会执行。reroute 的执行时间取决于同时 init 的分片数量。

磁盘空间到达 flood 水位线,客户端写入报错 read-only

故障现象

客户端写入 es 时报错失败:

es 有如下日志

故障原因

磁盘剩余空间达到 watermark.flood_stage,索引写入被 block。需要做 2 个操恢复。

解决方式

elasticsearch 会自己执行分片迁移到正常水平,你也可以自己迁移或删除数据。当节点的磁盘可用空间恢复到正常值之后,7.4及之后的版本会将相关索引自己恢复可写。

https://github.com/elastic/elasticsearch/pull/42559

但是在较旧的版本,即使磁盘空间恢复到正常水平,相关索引仍然时只读状态,你需要手工介入,将其设置为可写:

对 _all 索引设置:

一般不合理的水位线设置才会出现这种情况。如果你的集群有不同规格的机器混部,建议水位线设置绝对值,而非百分比,百分比在各个节点上的空间是不一样的,会有不一样的结果。设置磁盘水位:

7.6 版本 update 后 refresh 慢,性能问题导致稳定性问题

故障现象

主分片自我恢复非常慢,或者 refresh 慢。refresh 期间的堆栈:

move 分片,或者重启节点,关闭索引等,应用集群状态时也需要 refresh,但是一直拿不到锁,从而导致节点无法处理集群状态:

故障原因

引入 softdelete 后导致的 update 性能问题,该问题触发条件是执行大量更新文档的操作,然后执行 refresh。该问题影响 7.7.0之前所有开启了 softdelete的版本。临时解决方式可以加大 refresh 频率。

解决方式

这是 Lucene 的问题:
https://github.com/elastic/elasticsearch/issues/52146
https://issues.apache.org/jira/browse/LUCENE-9228

这两个 issues 所说的”对字段更新为相同值“,在 Elasticsearch 的场景中指的就是一个 es 里的 update 操作,即:put /index/_doc/1,在 lucene 中,更新为同一个值的意思是将 _ id 字段总是更新为 1;而 lucene 执行这个更新的主要消耗,在于将 __soft_deletes 字段设置为 1

假设 update 10w 次
最内层 applyDocValuesUpdates 函数被调用的次数为 shard 中 searcable=true && doc.count>0的分段个数+1,加 1 是因为最后一次为当前要刷下去的段,因此实际上 内层的applyDocValuesUpdates会被调用 2 次,第一次为已经 refresh 下去的,已存在的 segment,第二次为当前 refresh,还没落盘的 segment
因此对于上述每个 segment,执行 applyDocValuesUpdates,该函数的目的是记录哪些 doc 的值要被更新为指定值,保存在 docIdConsumer(dvUpdates)

applyDocValuesUpdates内部有多个循环嵌套,问题版本会产生 10w*10w 次迭代,而 770 只迭代 10w 次。

由两个主要循环组成,下面的循环用于遍历所有的更新操作,对于 762版本,会迭代 10w 次,而优化后的 770版本 只循环 1 次:

下列循环迭代 10w 次,遍历倒排链:

770 的优化方式是,在 FieldUpdatesBuffer.BufferedUpdateIterator#nextTerm 进行过滤,如果 刚刚处理的lastTerm和这次迭代到的相同,则跳过,避免后面再去调用 10w 次对倒排链的循环:

(转载请注明作者和出处 easyice.cn ,请勿用于任何商业用途)

1 Star2 Stars3 Stars4 Stars5 Stars (欢迎评分)
Loading...

发表评论

邮箱地址不会被公开。 必填项已用*标注