打破規模壁壘:我們如何在 Intercom 優化 Elasticsearch 的使用

已發表: 2022-09-22

Elasticsearch 是 Intercom 不可或缺的一部分。

它支持核心對講功能,如收件箱、收件箱視圖、API、文章、用戶列表、報告、Resolution Bot 和我們的內部日誌系統。 我們的 Elasticsearch 集群包含超過 350TB 的客戶數據,存儲超過 3000 億個文檔,峰值每秒處理超過 6 萬個請求。

隨著 Intercom 的 Elasticsearch 使用量增加,我們需要確保我們的系統擴展以支持我們的持續增長。 隨著我們最近推出的下一代收件箱,Elasticsearch 的可靠性比以往任何時候都更加重要。

我們決定解決我們的 Elasticsearch 設置存在的一個問題,該問題會帶來可用性風險並威脅到未來的停機時間:我們 Elasticsearch 集群中節點之間的流量/​​工作分佈不均。

低效率的早期跡象:負載不平衡

Elasticsearch 允許您通過增加存儲數據的節點(數據節點)的數量來進行水平擴展。 我們開始注意到這些數據節點之間的負載不平衡:由於磁盤或 CPU 使用率較高,其中一些節點比其他節點承受更大的壓力(或“更熱”)。

圖 1 CPU 使用不平衡
(圖 1)CPU 使用不平衡:兩個熱節點的 CPU 使用率比平均值高約 20%。

Elasticsearch 的內置分片放置邏輯基於粗略估計每個節點中的可用磁盤空間和每個節點索引的分片數量的計算來做出決策。 分片的資源利用率不計入此計算。 結果,一些節點可能會收到更多資源匱乏的分片並變得“熱”。 每個搜索請求都由多個數據節點處理。 在高峰流量期間超過其限制的熱節點可能會導致整個集群的性能下降。

熱節點的一個常見原因是分片放置邏輯將大分片(基於磁盤利用率)分配給集群,從而降低了平衡分配的可能性。 通常,一個節點可能會比其他節點分配更多的大分片,從而使其磁盤利用率更高。 大分片的存在也阻礙了我們增量擴展集群的能力,因為添加數據節點並不能保證所有熱節點的負載減少(圖 2)。

圖 4 添加節點

(圖2)增加一個數據節點並沒有減少Host A的負載。增加一個節點可以減少Host A的負載,但是集群仍然會出現負載分佈不均的情況。

相比之下,隨著集群的擴展,擁有更小的分片有助於減少所有數據節點上的負載——包括“熱”節點(圖 3)。

圖 3 許多較小的分片

(圖 3)擁有許多較小的分片有助於減少所有數據節點上的負載。

注意:問題不僅限於具有大型分片的集群。 如果我們將“大小”替換為“CPU 利用率”或“搜索流量”,我們會觀察到類似的行為,但比較大小更容易可視化。

除了影響集群穩定性外,負載不平衡還會影響我們經濟高效地擴展的能力。 我們總是需要增加比需要更多的容量,以將較熱的節點保持在危險水平以下。 解決這個問題意味著通過更有效地利用我們的基礎設施來獲得更好的可用性和顯著的成本節約。

我們對問題的深刻理解幫助我們意識到,如果我們有以下條件,負載可以分佈得更均勻:

  • 相對於數據節點數量的更多分片。 這將確保大多數節點收到相同數量的分片。
  • 相對於數據節點大小的更小的分片。 如果為某些節點提供了一些額外的分片,則不會導致這些節點的負載有任何有意義的增加。

紙杯蛋糕解決方案:更少的大節點

分片數量與數據節點數量的比率,以及分片大小與數據節點大小的比率,可以通過擁有更多的小分片來調整。 但是可以通過移動到更少但更大的數據節點來更輕鬆地對其進行調整。

我們決定從一個紙杯蛋糕開始來驗證這個假設。 我們將一些集群遷移到更大、更強大且節點更少的實例——保持相同的總容量。 例如,我們將一個集群從 40 個 4xlarge 實例移動到 10 個 16xlarge 實例,通過更均勻地分佈分片來減少負載不平衡。

圖 4 磁盤和 CPU 之間更好的負載分佈

(圖 4)通過移動到更少的更大節點,更好地分配磁盤和 CPU 之間的負載。

較少的較大節點緩解驗證了我們的假設,即調整數據節點的數量和大小可以改善負載分佈。 我們本可以停在那裡,但這種方法有一些缺點:

  • 我們知道,隨著分片隨著時間的推移變大,或者如果將更多節點添加到集群中以解決流量增加,負載不平衡會再次出現。
  • 更大的節點使增量擴展更加昂貴。 即使我們只需要一點額外的容量,現在添加一個節點也會花費更多。

挑戰:跨越壓縮普通對象指針 (OOP) 閾值

遷移到更少的更大節點並不像更改實例大小那麼簡單。 我們面臨的一個瓶頸是在遷移時保留可用的總堆大小(一個節點上的堆大小 x 節點總數)。

正如 Elastic 建議的那樣,我們一直將數據節點中的堆大小限制在 ~30.5 GB,以確保我們保持在臨界值以下,以便 JVM 可以使用壓縮的 OOP。 如果我們在遷移到更少、更大的節點後將堆大小限制為 ~30.5 GB,我們將減少整體堆容量,因為我們將使用更少的節點。

“我們遷移到的實例非常龐大,我們希望將大部分 RAM 分配給堆,以便我們有空間用於指針,並有足夠的空間用於文件系統緩存”

我們找不到很多關於跨越這個門檻的影響的建議。 我們要遷移到的實例非常龐大,我們希望將大部分 RAM 分配給堆,以便我們有空間用於指針,並有足夠的空間用於文件系統緩存。 我們通過將生產流量複製到測試集群來嘗試一些閾值,並將 RAM 的 33% 到 42% 作為 RAM 超過 200 GB 的機器的堆大小。

堆大小的變化對各種集群的影響不同。 雖然一些集群在“JVM % heap in use”或“Young GC Collection Time”等指標上沒有變化,但總體趨勢是增加。 無論如何,總的來說這是一次積極的體驗,我們的集群在這種配置下已經運行了 9 個多月——沒有任何問題。

長期修復:許多較小的碎片

一個長期的解決方案是,相對於數據節點的數量和大小,轉向擁有更多數量的小分片。 我們可以通過兩種方式獲得更小的分片:

  • 遷移索引以獲得更多主分片:這會將索引中的數據分佈在更多分片中。
  • 將索引分解為更小的索引(分區):這會將索引中的數據分佈在更多索引中。

重要的是要注意我們不想創建一百萬個小分片,或者有數百個分區。 每個索引和分片都需要一些內存和 CPU 資源。

“我們專注於更容易地在我們的系統中試驗和修復次優配置,而不是專注於‘完美’配置”

在大多數情況下,一小組大分片比許多小分片使用更少的資源。 但是還有其他選擇——實驗應該可以幫助您獲得更適合您的用例的配置。

為了使我們的系統更具彈性,我們專注於更容易地在我們的系統中進行實驗和修復次優配置,而不是固定在“完美”配置上。

分區索引

增加主分片的數量有時會影響聚合數據的查詢的性能,我們在遷移負責 Intercom 報告產品的集群時遇到了這種情況。 相反,將一個索引劃分為多個索引可以將負載分配到更多分片上,而不會降低查詢性能。

對講不需要為多個客戶共同定位數據,因此我們選擇根據客戶的唯一 ID 進行分區。 這通過簡化分區邏輯和減少所需設置幫助我們更快地交付價值。

“為了以對我們工程師現有習慣和方法影響最小的方式對數據進行分區,我們首先投入了大量時間來了解我們的工程師如何使用 Elasticsearch”

為了以對工程師現有習慣和方法影響最小的方式對數據進行分區,我們首先投入了大量時間來了解我們的工程師如何使用 Elasticsearch。 我們將可觀察性系統深度集成到 Elasticsearch 客戶端庫中,並掃描我們的代碼庫以了解我們團隊與 Elasticsearch API 交互的所有不同方式。

我們的故障恢復模式是重試請求,因此我們在發出非冪等請求的地方進行了必要的更改。 我們最終添加了一些 linter 來阻止使用諸如 `update/delete_by_query` 之類的 API,因為它們使發出非冪等請求變得容易。

我們構建了兩種功能,它們協同工作以提供完整的功能:

  • 一種將請求從一個索引路由到另一個索引的方法。 這個其他索引可以是一個分區,或者只是一個非分區索引。
  • 一種將數據雙重寫入多個索引的方法。 這使我們能夠使分區與正在遷移的索引保持同步。

“我們優化了我們的流程,在不影響速度的情況下最大限度地減少任何事故的爆炸半徑”

總而言之,將索引遷移到分區的過程如下所示:

  1. 我們創建新分區並打開雙寫,以便我們的分區與原始索引保持同步。
  2. 我們觸發所有數據的回填。 這些回填請求將被雙重寫入新分區。
  3. 回填完成後,我們驗證舊索引和新索引是否具有相同的數據。 如果一切正常,我們使用功能標誌開始為少數客戶使用分區並監控結果。
  4. 一旦我們有信心,我們會將所有客戶移動到分區,同時對舊索引和分區進行雙重寫入。
  5. 當我們確定遷移成功後,停止雙寫並刪除舊索引。

這些看似簡單的步驟包含了很多複雜性。 我們優化了我們的流程,以最大限度地減少任何事件的爆炸半徑,同時不影響速度。

收穫利益

這項工作幫助我們改善了 Elasticsearch 集群中的負載平衡。 更重要的是,我們現在可以通過將索引遷移到主分片較少的分區來改善負載分佈,從而實現兩全其美:每個索引的分片越來越少。

應用這些知識,我們能夠釋放重要的性能提升和節省。

  • 我們將兩個集群的成本分別降低了 40% 和 25%,並且在其他集群上也顯著節省了成本。
  • 我們將某個集群的平均 CPU 利用率降低了 25%,並將請求延遲中值提高了 100%。 我們通過將高流量索引遷移到與原始分區相比每個分區具有更少主分片的分區來實現這一點。
  • 遷移索引的通用能力還使我們能夠更改索引的架構,從而允許產品工程師為我們的客戶構建更好的體驗,或者使用更新的 Lucene 版本重新索引數據,從而解鎖我們升級到 Elasticsearch 8 的能力。

圖 5 負載不平衡和 CPU 利用率的改善

(圖 5)通過將高流量索引遷移到每個分區的主分片較少的分區,負載不平衡改善了 50%,CPU 利用率提高了 25%。

圖 6 中值請求延遲

(圖 6)通過將高流量索引遷移到每個分區的主分片較少的分區,平均請求延遲平均提高了 100%。

下一步是什麼?

引入 Elasticsearch 來支持新產品和功能應該很簡單。 我們的願景是讓我們的工程師與 Elasticsearch 交互變得像現代 Web 框架使其與關係數據庫交互一樣簡單。 團隊應該很容易創建索引、從索引讀取或寫入、更改其架構等等——而不必擔心如何處理請求。

您對我們的工程團隊在 Intercom 的工作方式感興趣嗎? 在此處了解更多信息並查看我們的空缺職位。

職業 CTA - 工程(水平)