Elasticsearch从入门到精通- Elasticsearch 分片分配


Elasticsearch 提供无缝扩展体验的能力的核心在于其跨机器分配工作负载的能力。这是通过Elasticsearch的sharding创建索引时,您为该Elasticsearch 索引设置主分片和副本分片计数。Elasticsearch 将您的数据和请求分布在这些分片之间,以及跨数据节点的分片上。Elasticsearch 集群的容量和性能主要取决于 Elasticsearch 如何在节点上分配分片。如果您的所有流量都流向一两个节点,因为它们包含Elasticsearch 集群中的活动索引,那么这些节点将显示出较高的 CPU、RAM、磁盘和网络使用率。当这几个节点崩溃时,您的Elasticsearch 集群中可能有数十或数百个节点处于空闲状态。

在这篇文章中,我将深入探讨 Elasticsearch 的分片分配策略,并讨论集群中“热”节点的原因。有了这种理解,您就可以修复根本原因以实现更好的性能和更稳定的Elasticsearch 集群。

在继续讲解之前,先回顾一下以下基本概念~

集群(cluster) 由一个或多个节点组成, 并通过集群名称与其他Elasticsearch 集群进行区分 节点(node) 单个ElasticSearch实例. 通常一个节点运行在一个隔离的容器或虚拟机中 索引(index) 在ElasticSearch中, 索引是一组文档的集合 分片(shard)

Elasticsearch 中的数据会整理为索引。每个索引又由一个或多个分片组成。每个分片都是一个 Lucene 索引实例,您可以将其视作一个独立的搜索引擎,它能够对 Elasticsearch 集群中的数据子集进行索引并处理相关查询。

数据写到分片上之后,会定期发布到磁盘上不可更改的新 Lucene 段中,此时,数据便可用于查询了。这称为刷新。相关原理的详细介绍,请参见 Elasticsearch:权威指南。

随着段数越来越多,这些段会定期合并为更大的段。这一过程称为合并。由于所有段都是不可更改的,这意味着在索引期间所用磁盘空间通常会上下浮动,这是因为只有合并后的新段创建完毕之后,它们所替换的那些段才能删掉。合并是一项极其耗费资源的任务,尤其耗费磁盘 I/O。

分片是 Elasticsearch 在集群内分发数据的单位。Elasticsearch 在对数据进行再平衡(例如发生故障后)时移动分片的速度取决于分片的大小和数量,以及网络和磁盘性能。

由于段是不可更改的,所以更新文档时必须要求 Elasticsearch 首先找到既有文档,然后将其标为已删除,并添加更新后版本。删除文档时同样也要求先找到文档,再将其标为已删除。有鉴于此,已删除文档仍将继续占用磁盘空间和系统资源,直至将它们合并,而合并过程也会消耗大量系统资源。

通过 Elasticsearch,用户可以十分高效地从文件系统中直接删除整个索引,而无需单独删除所有记录。这是迄今为止从 Elasticsearch 中删除数据的最高效方法。


 

注意: 

1.避免Elasticsearch 分片过大,因为这样会对集群从故障中恢复造成不利影响。尽管并没有关于分片大小的固定限值,但是人们通常将 50GB 作为Elasticsearch 分片上限,而且这一限值在各种用例中都已得到验证。

2.但凡可能,尽量使用时序型索引来管理具有数据保留期要求的数据。根据保留期限对数据分组,将它们存储到索引中。通过时序型索引,用户还能随着时间推移轻松调整Elasticsearch 主分片和副本分片的数量,这是因为用户可针对要生成的下个Elasticsearch 索引进行这方面的更改。这样便能简化对不断变化的数据量和数据要求的适应过程。

副本(replica) ElasticSearch默认为一个索引创建5个主分片, 并分别为其创建一个副本分片. 也就是说每个索引都由5个主分片成本, 而每个主分片都相应的有一个copy. 对于分布式搜索引擎ElasticSearch来说, 分片及副本的分配将是高可用及快速搜索响应的设计核心.主分片与副本都能处理查询请求, 它们的唯一区别在于只有主分片才能处理索引请求.

谨慎分配你的分片

当在ElasticSearch集群中配置好你的索引后, 你要明白在集群运行中你无法调整分片设置. 既便以后你发现需要调整分片数量, 你也只能新建创建并对数据进行重新索引(reindex)(虽然reindex会比较耗时, 但至少能保证你不会停机). 主分片的配置与硬盘分区很类似, 在对一块空的硬盘空间进行分区时, 会要求用户先进行数据备份, 然后配置新的分区, 最后把数据写到新的Elasticsearch 分区上.

分片倾斜可能导致集群故障

在最佳Elasticsearch 分片分布中,每台机器的资源利用率是一致的:每个分片具有相同的存储空间,每个请求都由每个分片提供服务,每个请求都平等地使用 CPU、RAM、磁盘和网络资源。当您垂直或水平扩展时,额外的节点同样有助于执行Elasticsearch 集群的工作,从而增加其容量。最佳情况就这么多。在实践中,你在一个Elasticsearch 集群中运行多个索引,数据分布不均匀,请求在不同节点上以不同的速率处理。在之前的一篇文章中,解释了Elasticsearch 存储使用是如何出现偏差的。当分片分布出现偏差时,CPU、网络和磁盘带宽的使用也会出现偏差。

例如,假设您有一个包含三个索引的Elasticsearch 集群,每个索引有四个主分片,部署在六个节点上,如下图所示。方形索引的分片都落在两个节点上,而圆形和圆角矩形索引混合在四个节点上。如果方形索引接收的流量是其他两个索引的十倍,那么这些节点将需要十倍于其他四个节点的 CPU、磁盘、网络和 RAM(可能)。您要么需要根据方形索引的要求进行过度扩展,要么如果您已经针对其他索引进行了扩展,那么您的Elasticsearch 集群就会崩溃。

正确的分配策略应该做出尊重系统要求的智能决策。这是一个难题,Elasticsearch 很好地解决了这个问题。让我们深入了解 Elasticsearch 的算法。

ShardsAllocator 找出放置分片的位置

ShardsAllocator是 Elasticsearch 中的一个接口,其实现负责分片放置。当分片因任何原因未分配时,ShardsAllocator决定将它们放置在Elasticsearch 集群中的哪些节点上。

ShardsAllocator在以下条件下确定分片位置:

  • 索引创建——当您向集群添加索引(或从快照恢复索引)时,ShardsAllocator决定将其分片放置在何处。当您增加索引的副本计数时,它会决定新副本副本的位置。
  • 节点故障——如果一个节点从集群中退出,ShardsAllocator找出该节点上的分片的放置位置。
  • 集群调整大小 - 如果从集群中添加或删除节点,则ShardsAllocator决定如何重新平衡集群。
  • 磁盘高水位线——当节点上的磁盘使用率达到高水位线(默认为 90% 满)时,Elasticsearch 会参与ShardsAllocator将分片移出该节点。
  • 手动分片路由 - 当您手动路由分片时,ShardsAllocator还会移动其他分片以确保集群保持平衡。
  • 路由相关设置更新——当您更改影响分片路由的集群或索引设置时,例如分配感知、排除或包含节点(通过 ip 或节点属性),或过滤索引以包含/排除特定节点。

分片放置策略可以分解为两个较小的子问题:对哪个Elasticsearch 分片进行操作,以及将其放置在哪个目标节点。默认的 Elasticsearch 实现BalancedShardsAllocator将其职责划分为三个主要的代码路径:分配未分配的分片、移动分片和重新平衡分片。这些中的每一个都在内部解决了原始子问题并决定了分片的操作:是将其分配到特定节点上,将其从一个节点移动到另一个节点,还是保持原样。

reroute当存在可能影响分片放置的集群状态更改时,将调用在 Elasticsearch 中调用的整体放置操作。

节点选择

Elasticsearch 通过处理一系列 Allocation Decider 来获取符合条件的节点列表。节点资格可能因分片和节点上的当前分配而异。并非所有节点都有资格接受特定分片。例如,Elasticsearch 不会将副本分片放在与主分片相同的节点上。或者,如果节点的磁盘已满,Elasticsearch 无法在其上放置另一个分片。

Elasticsearch 遵循一种贪心的分片放置方法:它做出局部最优决策,希望达到全局最优。节点托管分片的资格被抽象为权重函数,然后将每个分片分配给当前最有资格接受它的节点。将此权重函数视为一个数学函数,给定一些参数,它返回节点上分片的权重。分片最符合条件的节点是具有最小权重的节点。

分配未分配

重新路由调用执行的第一个操作是allocateUnassigned每次创建索引时,都会取消分配其Elasticsearch 分片(主分片和副本分片)。当一个节点离开集群时,该节点上的分片就会丢失。对于丢失的主分片,它们幸存的副本(如果有)被提升为主分片(这是由不同的模块完成的),并且相应的副本被渲染unassigned所有这些都分配给此操作中的节点。

对于allocateUnassigned()BalancedShardsAllocator遍历所有未分配的分片,找到有资格接受分片的节点子集(分配决策者),并从中选择权重最小的节点。

Elasticsearch 有一个固定的顺序选择未分配的分片进行分配。它首先选择主分片,为一个索引分配所有分片,然后再转移到下一个索引的主分片。要选择索引,它使用基于索引优先级、创建数据和索引名称的比较器(请参阅PriorityComparator)。这可确保 Elasticsearch为尽可能多的索引分配所有主索引,而不是创建多个部分分配的索引。一旦 Elasticsearch 分配了所有主索引,它就会移动到每个索引的第一个副本。然后,它移动到每个索引的第二个副本,依此类推。

移动碎片

考虑缩小Elasticsearch 集群时的场景。为应对工作负载的季节性变化,您刚刚度过了一个高流量季节,现在又恢复到中等工作负载。您想通过删除一些节点来调整Elasticsearch 集群的大小。如果您删除保存数据的节点太快,您可能会删除保存主节点及其副本的节点,从而永久丢失该数据。更好的方法是排除节点子集,等待所有Elasticsearch 分片移出,然后终止它们。

或者,考虑一个节点的磁盘已满并且必须移出一些分片以释放空间的情况。在这种情况下,必须将分片移出节点。这是由moveShards()操作处理的,在完成后立即触发allocateUnassigned()

对于“移动分片”,Elasticsearch 会遍历集群中的每个分片,并检查它是否可以保留在当前节点上。如果不是,它从合格节点的子集中(由决策者过滤中选择权重最小的节点作为该分片的。然后触发从当前节点到目标节点的分片重定位。 target node

移动操作仅适用于STARTED 分片;跳过任何其他状态的分片。要从Elasticsearch 集群所有节点均匀移动分片,请moveShards使用nodeInterleavedShardIterator此迭代器首先跨节点进行广度,从每个节点中选择一个分片,然后是下一个分片,依此类推。因此,所有节点上的所有分片都被评估为移动,而不是优先考虑一个。

重新平衡分片

当您达到工作负载限制时,您可能会决定添加更多节点来扩展集群。Elasticsearch 应该自动检测这些节点并重新定位分片以实现更好的分布。添加或删除节点可能并不总是需要移动分片——如果节点的分片很少(比如说只有一个),而额外的节点只是作为一种主动扩展措施而添加的呢?

Elasticsearch 使用分片分配器中的权重函数抽象来概括这个决定。给定节点上的当前分配,权重函数提供节点上分片的权重。与具有较低权重值的节点相比,具有高权重值的节点不太适合放置分片。比较不同节点上的分片权重,Elasticsearch 决定重定位是否可以改善整体权重分布。

对于重新平衡决策,Elasticsearch 计算每个节点上每个索引的权重,以及索引的最小和最大可能权重之间的差异。(这可以在索引级别完成,因为索引中的每个分片在 Elasticsearch 中都被平等对待。)然后按照最不平衡索引的顺序首先处理索引。

Elasticsearch 分片移动是一项繁重的操作。在实际重定位之前,Elasticsearch 对重新平衡前后的分片权重进行建模;只有当操作导致权重分布更平衡时,才会重新定位分片。

最后,再平衡是一个优化问题。超过阈值,移动分片的成本开始超过平衡权重的好处。在 Elasticsearch 中,这个阈值目前是一个固定值,可以通过动态设置进行配置cluster.routing.allocation.balance.threshold当计算出的索引权重增量(其跨节点的最小权重和最大权重之间的差异)小于此阈值时,该Elasticsearch 索引被认为是平衡的。

结论

在这篇文章中,我们介绍了在 Elasticsearch 中支持分片放置和平衡决策的算法。每次重新路由调用都会经历分配未分配的分片、移动必须从当前节点撤出的分片以及尽可能重新平衡分片的过程。它们共同维持着一个稳定平衡的Elasticsearch 集群。