Elasticsearch 索引分片的分配策略說明
在上一篇《索引生命周期管理ILM看完不懂你錘我 》(https://mp.weixin.qq.com/s/ajhFp-xBU1dJm8a1dDdRQQ)中,我們已經學會了索引級別的分片分配過濾屬性,也就是在配置文件中指定當前節點的屬性值node.attr.node_type: hot
,這個你還記得嗎,不記得的話可以回去在復習一下哦。
這一篇文章中,我們主要學習一下索引分片的分配策略,也就是分片時是根據什么規則進行分配的呢?
版本:Elasticsearch 8.1
一、索引級自定義屬性分片分配策略
我們有 5
個節點,node-1,node-2,node-3 增加屬性 node.attr.role: master
,node-4,node-5 增加屬性 node.attr.role: slave
。 elasticsearch.yml
文件中配置如下:
node-1,node-2,node-3
# node-1,node-2,node-3node.attr.role: master
node-4,node-5
# node-4,node-5node.attr.role: slave
定義索引zfc-doc-000013
,指定index.routing.allocation.include.role
為slave
,意思就是該索引分片只分配到node.attr.role
的值為slave
的節點上。
PUT zfc-doc-000013{ "settings": { "number_of_replicas": 0, "number_of_shards": 3, "index.routing.allocation.include.role":"slave" }}
當前集群為 5
個節點的集群,其中只有 node-4
、node-5
的 role
為 slave
,所以創建的索引 zfc-doc-000013
只會在這兩個節點 node-4
、node-5
中進行分配,而不會在 node-1
、node-2
、node-3
中進行分配。
二、節點離開時觸發分配
當集群中的節點由于未知的原因或者已知的原因離開集群時,主節點會做出以下反應:
- 如果當前離開的節點上有主分片,會將其它的副本分片提升為主分片以替換該節點上的主分片;
- 如果有足夠多的節點,分配副本分片來替代當前節點丟失的副本分片;
- 在剩余的節點之間進行重新平衡分片。
通過上述的操作可以讓我們盡可能的防止數據丟失,但是如果離開的節點很快就恢復那么這可能就是沒有必要的操作了。所以哪怕我們在節點級別和集群級別限制并發恢復,這種重新分配分片仍然會給系統帶來大量的額外負載。
想象一下以下場景:
- 節點
node-3
離開集群 - 主節點將節點
node-3
上的主分片的副本分片提升為主分片 - 主節點將新的副本分配給集群中的其它節點
- 每個新副本都會通過網絡創建主分片的完整副本
- 更多的分片被移動到不同的節點以重新分片分配達到平衡
- 幾分鐘之后節點
node-3
返回加入集群 - 主分片將分片分配給節點
node-3
以重新平衡集群
集群初始分配狀態
停掉節點 node-3
默認等待一分鐘
之后,系統自動進行分片的重新分配
節點恢復之后進行分片重新分配
由于節點離開造成的未分配分片可以通過修改參數 index.unassigned.node_left.delayed_timeout
動態設置延遲時間,默認 1m
。
可以在單個索引或者全部索引上進行設置該參數。
PUT zfc-doc-000013/_settings{ "settings": { "index.unassigned.node_left.delayed_timeout": "2m" }}
此設置不會影響副本分片升級為主分片,也不會影響之前未分配的副本的分配。需要注意的是,該設置在集群重啟之后會失效。
2.1、取消分片分配
如果延遲分配超時,主節點會將丟失的分片分配給另一個節點,該節點將開始恢復。 如果離開的節點重新加入集群,并且其分片仍然具有與主分片相同的 sync-id
,分片的重新分配將被取消,同步分片將恢復。 因此默認超時設置為一分鐘,即使分片重新分配已經開始,取消的成本也很低。
我們可以通過如下 API 查看集群健康狀態。
GET _cluster/health
2.2、節點永久離開
如果一個節點離開集群之后確定不會在返回,我們可以通過設置參數 index.unassigned.node_left.delayed_timeout
為 0
來讓 Elasticsearch 馬上分配未分配的分片。
PUT _all/_settings{ "settings": { "index.unassigned.node_left.delayed_timeout": "0" }}
三、索引恢復優先級
對于索引分片的重新分配,對于索引來說是有優先級的。
1、index.priority
最高的優先
2、其次是索引的創建時間
對于如下例子,我們可以自己測試一下:
PUT zfc-doc-index_1PUT zfc-doc-index_2PUT zfc-doc-index_3{ "settings": { "index.priority": 10 }}PUT zfc-doc-index_4{ "settings": { "index.priority": 5 }}
-
zfc-doc-index_3
第一個被恢復,因為它的優先級index.priority
最高。 -
zfc-doc-index_4
第二個被恢復,它的優先級僅次于索引zfc-doc-index_3
。 -
zfc-doc-index_2
第三個被恢復,它是最近創建的。 -
zfc-doc-index_1
最后被恢復。
四、節點總分片數限制
Elasticsearch 的集群在分配分片的時候,Elasticsearch 會盡可能的將單個索引的分片盡可能的分配在盡可能多的節點上,但是有時不是那么的均勻。我們可以通過以下設置修改允許每個節點上單個索引的分片總數的限制,index.routing.allocation.total_shards_per_node
分配給單個節點的最大分片數,默認沒有限制。
PUT zfc-doc-000013/_settings{ "settings":{ "index.routing.allocation.total_shards_per_node": 2 }}
也可以在不考慮索引的情況下限制節點可以擁有的分片數量 cluster.routing.allocation.total_shards_per_node
分配給每個節點的主分片和副本分片的最大數量,默認 -1
沒有限制。
PUT _cluster/settings?flat_settings=true{ "transient":{ "cluster.routing.allocation.total_shards_per_node":2 }}
Elasticsearch 會在分片的重新分配期間進行校驗該參數,有如下場景: 一個 Elasticsearch 集群,有三個節點,cluster.routing.allocation.total_shards_per_node
設置為100,并且具有如下的分片分布情況
- 節點1:100個分片
- 節點2:98個分片
- 節點3:1個分片
如果節點3發生故障,Elasticsearch 會將其分片重新分配到節點2,并不會分配到節點1,因為分配到節點1會超過設置的100
分片限制。
需要注意的是,如果我們設置了該參數,可能會導致部分分片無法進行分配。
下面我們用個例子來說明,首先看如下是我本地 Elasitcsearch 集群的部分索引
我們通過使用 API 動態修改參數之后,讓其重新進行分片分配會發生什么情況呢?
如下語句意思就是每個節點主分片加副本分片數量不能大于 2
,所以在下次發生分片的重新分配時肯定會無法進行分配。
PUT _cluster/settings?flat_settings=true{ "transient":{ "cluster.routing.allocation.total_shards_per_node":2 }}
停止 node-3
節點之后,分片無法分配。
在上面更改集群的設置時,我們可能已經注意到了,使用的是
transient
,還可以使用persistent
,他倆的區別就是transient
的配置會在集群重啟之后失效,persistent
會持久化保存。
不過這幾個配置的優先級如下:
1、transient
2、persistent
3、elasticsearch.yml
4、設置的默認值
五、索引級別數據層過濾
索引級別過濾與前面的自定義屬性分片分配類似,不過索引級別使用的是_tier_preference
來控制索引分配到哪個數據層。
這塊不是特別了解的可以參考文章開頭引用的索引生命周期那篇文章,該文章內通過例子展示了索引的生命周期。
我們還是用一個例子來說明 首先在elasticsearch.yml 定義角色 node-1,node-2中定義 node.roles: ["data_hot", "data_content"]
node-3,node-4中定義 node.roles: ["data_warm","data_content"]
node-5中定義 node.roles: ["data_cold","data_content"]
關于節點角色的定義,可以參考官網:https://www.elastic.co/guide/en/elasticsearch/reference/8.1/modules-node.html#master-node
集群按照上面的配置完成之后,啟動如下
通過 API 定義索引 zfc-doc-index_1
的分片分配策略在 data_warm
層
PUT zfc-doc-index_1{ "settings": { "number_of_replicas": 2, "number_of_shards": 3, "index.routing.allocation.include._tier_preference": "data_warm" }}
按照預期的設定,索引的分片應該是分布在 node-3
,node-4
中,結果如下:
可以看到,有分片是沒有進行分配的,所以這也是修改分片分配策略時需要特別注意的一點。
總結
通過上面的學習,我們知道了:
- 可以通過自定義屬性
node.attr.[]
控制分片的分配 - 可以通過索引級別的角色
index.routing.allocation.include._tier_preference
進行數據層的過濾分配 - 分片的分配過程中是可以通過
index.priority
指定優先級的 - 可以通過
index.routing.allocation.total_shards_per_node
控制每個節點上單個索引的分片數量 - 可以通過
cluster.routing.allocation.total_shards_per_node
控制每個節點上所有主分片加副本分片的總數量