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 集群。