Elasticsearch 分片分配
Elasticsearch 提供无缝扩展体验的能力的核心在于其跨机器分配工作负载的能力。这是通过Elasticsearch的sharding
. 创建索引时,您为该Elasticsearch 索引设置主分片和副本分片计数。Elasticsearch 将您的数据和请求分布在这些分片之间,以及跨数据节点的分片上。Elasticsearch 集群的容量和性能主要取决于 Elasticsearch 如何在节点上分配分片。如果您的所有流量都流向一两个节点,因为它们包含Elasticsearch 集群中的活动索引,那么这些节点将显示出较高的 CPU、RAM、磁盘和网络使用率。当这几个节点崩溃时,您的Elasticsearch 集群中可能有数十或数百个节点处于空闲状态。
在这篇文章中,我将深入探讨 Elasticsearch 的分片分配策略,并讨论集群中“热”节点的原因。有了这种理解,您就可以修复根本原因以实现更好的性能和更稳定的Elasticsearch 集群。
在继续讲解之前,先回顾一下以下基本概念~
Elasticsearch 中的数据会整理为索引。每个索引又由一个或多个分片组成。每个分片都是一个 Lucene 索引实例,您可以将其视作一个独立的搜索引擎,它能够对 Elasticsearch 集群中的数据子集进行索引并处理相关查询。
数据写到分片上之后,会定期发布到磁盘上不可更改的新 Lucene 段中,此时,数据便可用于查询了。这称为刷新。相关原理的详细介绍,请参见 Elasticsearch:权威指南。
随着段数越来越多,这些段会定期合并为更大的段。这一过程称为合并。由于所有段都是不可更改的,这意味着在索引期间所用磁盘空间通常会上下浮动,这是因为只有合并后的新段创建完毕之后,它们所替换的那些段才能删掉。合并是一项极其耗费资源的任务,尤其耗费磁盘 I/O。
分片是 Elasticsearch 在集群内分发数据的单位。Elasticsearch 在对数据进行再平衡(例如发生故障后)时移动分片的速度取决于分片的大小和数量,以及网络和磁盘性能。
由于段是不可更改的,所以更新文档时必须要求 Elasticsearch 首先找到既有文档,然后将其标为已删除,并添加更新后版本。删除文档时同样也要求先找到文档,再将其标为已删除。有鉴于此,已删除文档仍将继续占用磁盘空间和系统资源,直至将它们合并,而合并过程也会消耗大量系统资源。
通过 Elasticsearch,用户可以十分高效地从文件系统中直接删除整个索引,而无需单独删除所有记录。这是迄今为止从 Elasticsearch 中删除数据的最高效方法。
注意:
1.避免Elasticsearch 分片过大,因为这样会对集群从故障中恢复造成不利影响。尽管并没有关于分片大小的固定限值,但是人们通常将 50GB 作为Elasticsearch 分片上限,而且这一限值在各种用例中都已得到验证。
2.但凡可能,尽量使用时序型索引来管理具有数据保留期要求的数据。根据保留期限对数据分组,将它们存储到索引中。通过时序型索引,用户还能随着时间推移轻松调整Elasticsearch 主分片和副本分片的数量,这是因为用户可针对要生成的下个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 集群。