網(wǎng)站制作網(wǎng)站建設(shè)競價開戶
作者:來自 Elastic?Sachin Frayne
探索 Elasticsearch 中的熱點(diǎn)以及如何使用 AutoOps 解決它。
Elasticsearch 集群中出現(xiàn)熱點(diǎn)的方式有很多種。有些我們可以控制,比如吵鬧的鄰居,有些我們控制得較差,比如 Elasticsearch 中的分片分配算法。好消息是,新的 desire_balance cluster.routing.allocation.type 算法(參見 shards-rebalancing-heuristics)在確定集群中的哪些節(jié)點(diǎn)應(yīng)該獲得新分片方面要好得多。如果存在不平衡,它會為我們找出最佳平衡。壞消息是,較舊的 Elasticsearch 集群仍在使用平衡(balanced)分配算法,該算法的計算能力較有限,在選擇節(jié)點(diǎn)時容易出錯,從而導(dǎo)致集群不平衡或出現(xiàn)熱點(diǎn)。
在這篇博客中,我們將探討這種舊算法,它應(yīng)該如何工作以及何時不起作用,以及我們可以做些什么來解決這個問題。然后,我們將介紹新算法以及它如何解決這個問題,最后,我們將研究如何使用 AutoOps 來針對客戶用例突出顯示這個問題。然而,我們不會深入探討熱點(diǎn)的所有原因,也不會深入探討所有具體的解決方案,因?yàn)樗鼈兲嗔恕?/p>
什么是 AutoOps?
平衡分配
在 Elasticsearch 8.5 及更早版本中,我們使用以下方法來確定在哪個節(jié)點(diǎn)放置分片,此方法主要?dú)w結(jié)為選擇分片數(shù)量最少的節(jié)點(diǎn):https://github.com/elastic/elasticsearch/blob/8.5/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java#L242
float weight(Balancer balancer, ModelNode node, String index) {final float weightShard = node.numShards() - balancer.avgShardsPerNode();final float weightIndex = node.numShards(index) - balancer.avgShardsPerNode(index);return theta0 * weightShard + theta1 * weightIndex;
}
- node.numShards():分配給集群中特定節(jié)點(diǎn)的分片數(shù)量
- balancer.avgShardsPerNode():集群中所有節(jié)點(diǎn)的分片平均值
- node.numShards(index):分配給集群中特定節(jié)點(diǎn)的特定索引的分片數(shù)量
- balancer.avgShardsPerNode(index):集群中所有節(jié)點(diǎn)的特定索引的分片平均值
- theta0:(cluster.routing.allocation.balance.shard) 分片總數(shù)的權(quán)重因子,默認(rèn)為 0.45f,增加該值會增加均衡每個節(jié)點(diǎn)分片數(shù)量的趨勢(請參閱 ?Shard balancing heuristics settings)
- theta1:(cluster.routing.allocation.balance.index) 每個索引分片總數(shù)的權(quán)重因子,默認(rèn)為 0.55f,增加該值會增加均衡每個索引分片數(shù)量的趨勢每個節(jié)點(diǎn)(請參閱?Shard balancing heuristics settings)
該算法在整個集群中的目標(biāo)值是以這樣的方式選擇一個節(jié)點(diǎn),使得集群中所有節(jié)點(diǎn)的權(quán)重回到 0 或最接近 0。
示例
讓我們探討這樣一種情況:我們有 2 個節(jié)點(diǎn),其中 1 個索引由 3 個主分片組成,并且假設(shè)我們在節(jié)點(diǎn) 1 上有 1 個分片,在節(jié)點(diǎn) 2 上有 2 個分片。當(dāng)我們向具有 1 個分片的集群添加新索引時會發(fā)生什么?
由于新索引在集群中的其他任何地方都沒有分片,因此 weightIndex 項(xiàng)減少到 0,我們可以在下一個計算中看到,將分片添加到節(jié)點(diǎn) 1 將使余額回到 0,因此我們選擇節(jié)點(diǎn) 1。
現(xiàn)在讓我們添加另一個包含 2 個分片的索引,由于現(xiàn)在已達(dá)到平衡,因此第一個分片將隨機(jī)分配到其中一個節(jié)點(diǎn)。假設(shè)節(jié)點(diǎn) 1 被選為第一個分片,則第二個分片將分配到節(jié)點(diǎn) 2。
新的平衡最終將是:
如果集群中的所有索引/分片在采集、搜索和存儲要求方面都執(zhí)行大致相同的工作量,則此算法將很好地發(fā)揮作用。實(shí)際上,大多數(shù) Elasticsearch 用例并不這么簡單,并且分片之間的負(fù)載并不總是相同的,請想象以下場景。

- 索引 1,小型搜索用例,包含幾千個文檔,分片數(shù)量不正確;
- 索引 2,索引非常大,但未被主動寫入且偶爾搜索;
- 索引 3,輕量級索引和搜索;
- 索引 4,重度攝取應(yīng)用程序日志。
假設(shè)我們有 3 個節(jié)點(diǎn)和 4 個索引,它們只有主分片,并且故意處于不平衡狀態(tài)。為了直觀地了解正在發(fā)生的事情,我根據(jù)分片的繁忙程度以及繁忙的含義(寫入、讀取、CPU、RAM 或存儲)夸大了分片的大小。即使節(jié)點(diǎn) 3 已經(jīng)擁有最繁忙的索引,新的分片也會路由到該節(jié)點(diǎn)。索引生命周期管理 (ILM) 不會為我們解決這種情況,當(dāng)索引滾動時,新的分片將放置在節(jié)點(diǎn) 3 上。我們可以手動緩解這個問題,強(qiáng)制 Elasticsearch 使用集群重新(cluster reroute)路由均勻分布分片,但這無法擴(kuò)展,因?yàn)槲覀兊姆植际较到y(tǒng)應(yīng)該處理這個問題。盡管如此,如果沒有任何重新平衡或其他干預(yù)措施,這種情況將繼續(xù)存在,并可能變得更糟。此外,雖然這個例子是假的,但這種分布在具有混合用例(即搜索、日志記錄、安全)的舊 Elasticsearch 集群中是不可避免的,尤其是當(dāng)一個或多個用例是重度攝取時,確定何時會發(fā)生這種情況并不是一件容易的事。
雖然預(yù)測這個問題的時間范圍很復(fù)雜,但在某些情況下行之有效的一個好的解決方案是保持所有索引的分片密度相同,這是通過在所有索引的分片達(dá)到預(yù)定大小(以 GB 為單位)時滾動所有索引來實(shí)現(xiàn)的(請參閱分片大小 -? size your shards)。這并不適用于所有用例,正如我們將在下面 AutoOps 捕獲的集群中看到的那樣。
所期望的平衡分配
為了解決這個問題和其他一些問題,一種可以同時考慮寫入負(fù)載和磁盤使用情況的新算法最初在 8.6 中發(fā)布,并在 8.7 和 8.8 版本中進(jìn)行了一些微小但有意義的更改:https://github.com/elastic/elasticsearch/blob/8.8/server/src/main/java/org/elasticsearch/cluster/routing/allocation/allocator/BalancedShardsAllocator.java#L305
float weight(Balancer balancer, ModelNode node, String index) {final float weightShard = node.numShards() - balancer.avgShardsPerNode();final float weightIndex = node.numShards(index) - balancer.avgShardsPerNode(index);final float ingestLoad = (float) (node.writeLoad() - balancer.avgWriteLoadPerNode());final float diskUsage = (float) (node.diskUsageInBytes() - balancer.avgDiskUsageInBytesPerNode());return theta0 * weightShard + theta1 * weightIndex + theta2 * ingestLoad + theta3 * diskUsage;
}
- node.writeLoad():特定節(jié)點(diǎn)的寫入或索引負(fù)載
- balancer.avgWriteLoadPerNode():整個集群的平均寫入負(fù)載
- node.diskUsageInBytes():特定節(jié)點(diǎn)的磁盤使用情況
- balancer.avgDiskUsageInBytesPerNode():整個集群的平均磁盤使用情況
- theta2:(cluster.routing.allocation.balance.write_load)寫入負(fù)載的權(quán)重因子,默認(rèn)為 10.0f,增加該值會增加均衡每個節(jié)點(diǎn)的寫入負(fù)載的趨勢(請參閱?Shard balancing heuristics settings)
- theta3:(cluster.routing.allocation.balance.disk_usage)磁盤使用情況的權(quán)重因子,默認(rèn)為 2e-11f,增加該值會增加均衡每個節(jié)點(diǎn)的磁盤使用情況的趨勢(請參閱?Shard balancing heuristics settings)
我不會在本博客中詳細(xì)介紹此算法所做的計算,但是 Elasticsearch 用于決定分片應(yīng)位于何處的數(shù)據(jù)可通過 API 獲取:獲取所需平衡(Get desired balance)。在調(diào)整分片大小時,遵循我們的指導(dǎo)仍然是最佳實(shí)踐,并且仍然有充分的理由將用例分離到專用的 Elasticsearch 集群中。然而,此算法在平衡 Elasticsearch 方面要好得多,以至于它為我們的客戶解決了以下平衡問題。(如果你遇到本博客中描述的問題,我建議你升級到 8.8)。
最后要注意的是,此算法沒有考慮搜索負(fù)載,這很難衡量,甚至更難預(yù)測。6.1 中引入的自適應(yīng)副本選擇(Adaptive replica selection)對解決搜索負(fù)載大有幫助。在未來的博客中,我們將深入探討搜索性能的主題,特別是如何使用 AutoOps 在搜索性能問題發(fā)生之前發(fā)現(xiàn)它們。
在 AutoOps 中檢測熱點(diǎn)
上述情況不僅難以預(yù)測,而且一旦發(fā)生也難以檢測,我們需要對 Elasticsearch 有深入的內(nèi)部了解,并且我們的集群需要滿足非常具體的條件才能處于這種狀態(tài)。
現(xiàn)在,使用 AutoOps 檢測這個問題就輕而易舉了。讓我們看一個真實(shí)的例子;
在這個設(shè)置中,Elasticsearch 前面有一個排隊(duì)機(jī)制,用于處理數(shù)據(jù)峰值,但是用例是近實(shí)時日志 - 持續(xù)的滯后是不可接受的。我們遇到了持續(xù)滯后的情況,必須進(jìn)行故障排除。從集群視圖開始,我們獲取了一些有用的信息,在下圖中我們了解到有 3 個主節(jié)點(diǎn)、8 個數(shù)據(jù)節(jié)點(diǎn)(以及 3 個與案例無關(guān)的其他節(jié)點(diǎn))。我們還了解到集群是紅色的(這可能是網(wǎng)絡(luò)或性能問題),版本是 8.5.1,有 6355 個分片;最后這兩個將在以后變得重要。

這個集群中發(fā)生了很多事情,它經(jīng)常變成紅色,這些都與離開集群的節(jié)點(diǎn)有關(guān)。節(jié)點(diǎn)離開集群的時間大約在我們觀察到索引拒絕的時間,并且拒絕發(fā)生在索引隊(duì)列過于頻繁地填滿后不久,黃色越深,時間塊中的高索引事件越多。

轉(zhuǎn)到節(jié)點(diǎn)視圖并關(guān)注最后一個節(jié)點(diǎn)斷開連接的時間范圍,我們可以看到另一個節(jié)點(diǎn)(節(jié)點(diǎn) 9)的索引率比其他節(jié)點(diǎn)高得多,其次是節(jié)點(diǎn) 4,該節(jié)點(diǎn)在本月早些時候曾出現(xiàn)過一些斷開連接的情況。你還會注意到,在同一時間范圍內(nèi)索引率下降幅度相當(dāng)大,這實(shí)際上也與此特定集群中計算資源和存儲之間的間歇性延遲有關(guān)。

默認(rèn)情況下,AutoOps 只會報告斷開連接時間超過 300 秒的節(jié)點(diǎn),但我們知道包括節(jié)點(diǎn) 9 在內(nèi)的其他節(jié)點(diǎn)經(jīng)常離開集群,如下圖所示,節(jié)點(diǎn)上的分片數(shù)量增長太快,無法移動分片,因此在節(jié)點(diǎn)斷開連接/重新啟動后,它們必須重新初始化。有了這些信息,我們可以放心地得出結(jié)論,集群正在經(jīng)歷性能問題,但不僅僅是熱點(diǎn)性能問題。由于 Elasticsearch 以集群的形式工作,它只能以最慢的節(jié)點(diǎn)的速度運(yùn)行,而且由于節(jié)點(diǎn) 9 被要求比其他節(jié)點(diǎn)做更多的工作,它無法跟上,其他節(jié)點(diǎn)總是在等待它,偶爾也會斷開連接。

此時我們不需要更多信息,但為了進(jìn)一步說明 AutoOps 的強(qiáng)大功能,下面是另一張圖像,該圖像顯示了節(jié)點(diǎn) 9 比其他節(jié)點(diǎn)執(zhí)行了多少工作,特別是它寫入磁盤的數(shù)據(jù)量。

我們決定將所有分片從節(jié)點(diǎn) 9 移出,方法是將它們隨機(jī)發(fā)送到集群中的其他節(jié)點(diǎn);這是通過以下命令實(shí)現(xiàn)的。此后,整個集群的索引性能得到改善,延遲消失。
PUT /_cluster/settings
{"transient": {"cluster.routing.allocation.exclude._name": "****-data-9"}
}
現(xiàn)在我們已經(jīng)觀察、確認(rèn)并解決了該問題,我們需要找到一個長期的解決方案,這又讓我們回到了博客開頭的技術(shù)分析。我們遵循最佳實(shí)踐,分片以預(yù)定的大小滾動,甚至限制每個節(jié)點(diǎn)特定索引的分片數(shù)量。我們遇到了算法無法處理的邊緣情況,即索引繁重且頻繁滾動的索引。
我們考慮過是否可以手動重新平衡集群,但對于由 6355 個分片組成的約 2000 個索引,這并非易事,更不用說,在這種級別的索引下,我們將與 ILM 競爭重新平衡。這正是新算法的設(shè)計目的,因此我們的最終建議是升級集群。
最后的想法
本博客總結(jié)了一組相當(dāng)具體但復(fù)雜的情況,這些情況可能會導(dǎo)致 Elasticsearch 性能出現(xiàn)問題。你今天甚至可能會在集群中看到其中一些問題,但可能永遠(yuǎn)不會像這個用戶那樣嚴(yán)重地影響集群。這個案例強(qiáng)調(diào)了跟上 Elasticsearch 最新版本的重要性,以便始終利用最新的創(chuàng)新來更好地管理數(shù)據(jù),它有助于展示 AutoOps 在發(fā)現(xiàn)/診斷問題并提醒我們注意問題方面的強(qiáng)大功能,以免它們成為全面生產(chǎn)事件。
考慮遷移到至少 8.8 版 https://www.elastic.co/guide/en/elasticsearch/reference/8.8/migrating-8.8.html
Elasticsearch 包含許多新功能,可幫助你為你的用例構(gòu)建最佳搜索解決方案。深入了解我們的示例筆記本以了解更多信息,開始免費(fèi)云試用,或立即在你的本地機(jī)器上試用 Elastic。
原文:Hotspots in Elasticsearch and how to resolve them with AutoOps - Search Labs