《Elastic Stack 实战手册》——三、产品能力——3.5 进阶篇——3.5.5.Shard allocation (1) https://developer.aliyun.com/article/1228686
集群维度分片分配
从集群层面来说,分片分配控制的是集群内各索引的分片,集合在各节点的分布,并且在分片分配过程中,添加一些硬性限制以控制集群负载在合理范围内。
以一个实际操作中可能会遇到的情况来说,公司准备新上一批业务,需要用到大规模的数据检索功能,首先需要处理搭建集群的任务,在开始搭建集群之前,让我们先看看实际的业务场景。
系统用于收集应用打点日志,提供一周内数据供查询。
分片平衡的启发式参数
日志类型数据存储,带有非常明显的时效性,一般情况下当日数据的读写频繁,非当日数据几乎不会有写操作,离当前时间越久的数据读操作也越少。
通过上述判断,可以认为索引的分片,越是平均分布于集群内各节点越好,因为可以充分利用全部节点的算力,来分摊当日数据的高频读写负载。
为了达到这个目的,我们可以通过 Elasticseach 提供的部分启发式参数,让 master 在决策分片如何分配时,更多的向我们期望的方向考虑:
l cluster.routing.allocation.balance.shard 节点中分片总数对权重的影响因子,默认为 0.45,该值越大则各节点的分片数越趋向于相等。
l cluster.routing.allocation.balance.index 节点中来自不同索引的分片数对权重的影响因子,默认为 0.55,该值越大,则各索引的分片更倾向于均匀分配到各节点。
l cluster.routing.allocation.balance.threshold 默认为 1.0,该值越大,则集群对不平衡状态的容忍程度越高。
我们可以适当放大 cluster.routing.allocation.balance.index 的权重来使得集群在分配分片时,更倾向于将一个索引的不同分片,均匀分布到各个节点。不过需要注意的是,这只是一个启发式参数,更多的是"建议",而不是"命令",要完全达到我们的期望,还需要借助索引维度的分配调控手段。
分片迁移的流量控制
回到本节起始提到的日志业务,系统上线运行一段时间后,随着索引量的不断增加,我们需要适时的清理掉过期数据,清理过程中自然的会删除过期数据所在的索引,释放存储空间供新的索引使用。
在集群删除索引时,因为集群内分片总数发生了变化,自然的分片在各节点的分配状态也随之发生变化,可能会出现分片的"不平衡"状态。这时默认情况下集群,会自动触发分片的重平衡操作,将分片在各节点间适当的迁移,以使得分片在集群重新达到"平衡"状态。
在日志类数据情况下,单个分片包含的数据量可能会较大,达到若干 GB。这样在分片发生迁移时,节点必然会触发大量的 IO 操作,为了避免大量的 IO 操作对节点造成冲击,使得集群服务发生抖动,我们可以通过分片迁移的流量控制参数进行干预:
l cluster.routing.allocation.node_concurrent_incoming_recoveries
l 用于控制可同时在一个节点上进行初始化或恢复的最大分片数,默认为2,设置过大可能导致节点负载过高 (同时写入大量数据),调整时需要考虑节点的硬件配置。
l cluster.routing.allocation.node_concurrent_outgoing_recoveries
l 用于控制该节点可同时为其他节点分片恢复或迁移提供数据源的最大分片数,默认为2,调整同上。
注1: 从节点的角度,分片会出现两种流向:流入 和 流出,其中,流入是指来自某个索引的分片新落入到该节点,流出是指该在其他节点的分片以该节点所属的分片为数据源进行副本恢复或者数据迁移。
节点的水位线
随着打点应用的接入越来越多,单日的日志索引量上涨迅速,节点的磁盘水位吃紧,我们希望新的分片在分配时,能考虑到节点存储容量的状态,避免将新分片分配到磁盘容量快满的节点。
这个情况下如果要自行通过节点属性来调控,至少需要:
l 自动监测磁盘水位,并为节点打上 low/medium/high 的属性,而且更改属性还需要重启节点。
l 为新分配的索引设置 index.routing.allocation.require.* 属性,来让索引避开高水位节点。
l 在节点的磁盘水位属性变更时,自动为集群内的索引更新 allocation 配置来避免自动平衡。
看上去就很麻烦,那么有没有简便方法呢?
答案是使用内置的水位 cluster.routing.allocation.disk.watermark.* 属性。
水位限制分为高低两种,其中:
l cluster.routing.allocation.disk.watermark.low
l 低水位,默认为磁盘容量的 85%,Elasticsearch 会避免将分片分布至磁盘容量超过低水位的节点。但是新创建索引的主分片 (primary shards),仍然可以分配到超过低水位的节点。
l cluster.routing.allocation.disk.watermark.high
l 高水位,默认为磁盘容量的 90%,Elasticsearch 会将磁盘容量超过高水位节点上的分片迁移至其他节点。
l cluster.routing.allocation.disk.watermark.flood_stage
l 警戒水位,默认为磁盘容量的 95%,当节点磁盘容量超过警戒水位时,该节点所属分片所在的索引,都会被执行写禁止操作,即索引变为只读状态。
l 比如 A 索引的 shard 1 分布在 N1 节点,shard 2 分布在 N2 节点,如果 N1 节点磁盘容量超过警戒水位,索引 A 即被执行写禁止操作,成为只读索引。但是容量绝对值和百分比不能混用,比如指定了磁盘低水位为 500mb,则高水位相应的也必须使用绝对值表示。
索引的增、删、改都会对所在节点的磁盘水位产生影响,为了动态的感知磁盘水位,相应的就有了水位采集参数:
l cluster.info.update.interval
l 磁盘水位采集频率,即每隔多久去检查一次磁盘用量,默认为 30 秒。
l cluster.routing.allocation.disk.include_relocations
l 是否将正在迁移到当前节点的分片磁盘用量 (将占用的磁盘空间) ,计入当前节点的磁盘用量,默认打开。
热点问题
虽然根据实际数据更替情况,合理配置了节点的高低水位,但是随着时间推移,我们发现集群发生了热点数据倾斜问题,由于冷数据占用了大量的存储空间,导致热点数据 (当日新创建的索引), 被迫分配到空间用量相对较少的几个节点,使得集群的负载不均。
针对日志服务等索引频繁创建、删除的场景,数据带有明显的时效性,可以考虑集群分组,对冷热数据使用不同的分配标记 (allocation attributes) ,来隔离冷热数据 (或者使用 data
tierallocation) ,目的是避免访问较少的冷数据,占用磁盘容量,导致集群将新创建的索引分配到少数几个“看起来比较合适”的节点,导致热点问题出现。
Elasticsearch 新版本中已经将类似的功能集成为 data_tier 插件,详见下一小节。
针对索引创建、删除不频繁的场景,比如电商后端常见的商品搜索、订单搜索等,一方面可以考虑将集群节点分组,或者部署多个集群,将不同业务进行资源隔离。
另一方面,创建索引时需要考虑到,未来数据量的增长情况,以设置合理的分片数量,将分片尽量均匀分配到每个节点,以更合理的利用节点硬件资源。
一般来说,商品、订单等业务数据长尾效应比较明显,针对热点的店铺、类目等引起的数据倾斜问题,可以将热点数据单独拆出一个索引,配合前端的引擎代理将请求路由到对应的索引。
《Elastic Stack 实战手册》——三、产品能力——3.5 进阶篇——3.5.5.Shard allocation (3) https://developer.aliyun.com/article/1228683