400+ 節點的 Elasticsearch 叢集運維

vivo網際網路技術發表於2019-04-25

本文首發於InfoQ  https://

作者:Anton Hägerstrand

翻譯:楊振濤 

目錄:

  1. 資料量

  2. 版本

  3. 節點配置

  4. 索引結構

  5. 效能

Meltwater每天要處理數百萬量級的帖子資料,因此需要一種能處理該量級資料的儲存和檢索技術。

從0.11.X 版本開始我們就已經是Elasticsearch的忠實使用者了。在經歷了一些波折之後,最終我們認為做出了正確的技術選型。

Elasticsearch 用於支援我們的主要媒體監控應用,客戶透過該應用可以檢索和分析媒體資料,比如新聞文章、(公開的)Facebook帖子、Instagram帖子、部落格和微博。我們透過使用一個混合API來收集這些內容,並爬取和稍作加工,使得它們可被 Elasticsearch 檢索到。

本文將分享我們所學到的經驗、如何調優 Elasticsearch,以及要繞過的一些陷阱。

如果想了解更多關於我們在Elasticsearch方面的點滴,可參考之前博文中的  numad issues  和  batch percolator

1.資料量


每天都有數量相當龐大的新聞和微博產生;在高峰期需要索引大約300多萬社論文章,和近1億條社交帖子資料。其中社論資料長期儲存以供檢索(可回溯到2009年),社交帖子資料儲存近15個月的。當前的主分片資料使用了大約200 TB的磁碟空間,副本資料大約600 TB。

我們的業務每分鐘有3千次請求。所有的請求透過一個叫做 “search-service” 的服務,該服務會依次完成所有與 Elasticsearch 叢集的互動。大部分檢索規則比較複雜,包括在皮膚和新聞流中。比如,一個客戶可能對 Tesla 和 Elon Musk 感興趣,但希望排除所有關於 SpaceX 或 PayPal 的資訊。使用者可以使用一種與 Lucene 查詢語法類似的靈活語法,如下:

Tesla AND "Elon Musk" NOT (SpaceX OR PayPal)

我們最長的此類查詢有60多頁。重點是:除了每分鐘3千次請求以外,沒有一個查詢是像在 Google 裡查詢 “Barack Obama” 這麼簡單的;這簡直就是可怕的野獸,但ES節點必須努力找出一個匹配的文件集。

2.版本


我們執行的是一個基於 Elasticsearch 1.7.6 的定製版本。該版本與1.7.6 主幹版本的唯一區別是,我們向後移植(backport)了  roaring bitsets/bitmaps  作為快取。該功能是從 Lucene 5 移植到 Lucene 4 的,對應移植到了 ES 1.X 版本。Elasticsearch 1.X 中使用預設的 bitset 作為快取,對於稀疏結果來說開銷非常大,不過在 Elasticsearch 2.X 中已經做了最佳化。

為何不使用較新版本的 Elasticsearch 呢?主要原因是升級困難。在主版本間滾動升級只適用於從ES 5到6(從ES 2到5應該也支援滾動升級,但沒有試過)。因此,我們只能透過重啟整個叢集來升級。當機對我們來說幾乎不可接受,但或許可以應對一次重啟所帶來的大約30-60分鐘當機時間;而真正令人擔心的,是一旦發生故障並沒有真正的回滾過程。

截止目前我們選擇了不升級叢集。當然我們希望可以升級,但目前有更為緊迫的任務。實際上該如何實施升級尚未有定論,很可能選擇建立另一個新的叢集,而不是升級現有的。

3.節點配置


我們自2017年6月開始在AWS上執行主叢集,使用i3.2xlarge例項作為資料節點。之前我們在COLO(Co-located Data Center)裡執行叢集,但後續遷移到了AWS雲,以便在新機器當機時能贏得時間,使得我們在擴容和縮容時更加彈性。

我們在不同的可用區執行3個候選 master 節點,並設定 discovery.zen.minimum_master_nodes 為2。這是避免腦裂問題  split-brain problem  非常通用的策略。

我們的資料集在儲存方面,要求80%容量和3個以上的副本,這使得我們執行了430個資料節點。起初打算使用不同層級的資料,在較慢的磁碟上儲存較舊的資料,但是由於我們只有相關的較低量級舊於15個月的資料(只有編輯資料,因為我們丟棄了舊的社交資料),然而這並未奏效。每個月的硬體開銷遠大於執行在COLO中,但是雲服務支援擴容叢集到2倍,而幾乎不用花費多少時間。

你可能會問,為何選擇自己管理維護ES叢集。其實我們考慮過託管方案,但最後還是選擇自己安裝,理由是:  AWS Elasticsearch Service

暴露給使用者的可控性太差了, Elastic Cloud  的成本比直接在EC2上執行叢集要高2-3倍。

為了在某個可用區當機時保護我們自身,節點分散於eu-west-1的所有3個可用區。我們使用  AWS plugin  來完成該項配置。它提供了一個叫做aws_availability_zone 的節點屬性,我們把 cluster.routing.allocation.awareness.attributes 設定為 aws_availability_zone。這保證了ES的副本儘可能地儲存在不同的可用區,而查詢儘可能被路由到相同可用區的節點。

這些例項執行的是 Amazon Linux,臨時掛載為 ext4,有約64GB的記憶體。我們分配了26GB用於ES節點的堆記憶體,剩下的用於磁碟快取。為何是26GB?因為  JVM 是在一個黑魔法之上構建的  。

我們同時使用  Terraform  自動擴容組來提供例項,並使用  Puppet  完成一切安裝配置。

4.索引結構


因為我們的資料和查詢都是基於時間序列的,所以使用了  time-based indexing  ,類似於ELK (elasticsearch, logstash, kibana)  stack 。同時也讓不同型別的資料儲存在不同的索引庫中,以便諸如社論文件和社交文件類資料最終位於不同的每日索引庫中。這樣可以在需要的時候只丟棄社交索引,並增加一些查詢最佳化。每個日索引執行在兩個分片中的一個。

該項設定產生了大量的分片(接近40k)。有了這麼多的分片和節點,叢集操作有時變得更特殊。比如,刪除索引似乎成為叢集master的能力瓶頸,它需要把叢集狀態資訊推送給所有節點。我們的叢集狀態資料約100 MB,但透過TCP壓縮可減少到3 MB(可以透過 curl localhost:9200/_cluster/state/_all 檢視你自己叢集的狀態資料)。Master 節點仍然需要在每次變更時推送1.3 GB資料(430 節點 x 3 MB 狀態大小)。除了這1.3 GB資料外,還有約860 MB必須在可用區(比如 最基本的透過公共網際網路)之間傳輸。這會比較耗時,尤其是在刪除數百個索引時。我們希望新版本的 Elasticsearch 能最佳化這一點,首先從  ES 2.0支援僅傳送叢集狀態的差分資料  這一特性開始。

5.效能


如前所述,我們的ES叢集為了滿足客戶的檢索需求,需要處理一些非常複雜的查詢。

為應對查詢負載,過去幾年我們在效能方面做了大量的工作。我們必須嘗試公平分享ES叢集的效能測試,從下列引文就可以看出。

不幸的是,當叢集當機的時候,不到三分之一的查詢能成功完成。我們相信測試本身導致了叢集當機。 

—— 摘錄自使用真實查詢在新ES叢集平臺上的第一次效能測試

為了控制查詢執行過程,我們開發了一個外掛,實現了一系列自定義查詢型別。透過使用這些查詢型別來提供Elasticsearch官方版本不支援的功能和效能最佳化。比如,我們實現了 phrases 中的 wildcard 查詢,支援在 SpanNear 查詢中執行;另一個最佳化是支援“*”代替 match-all-query ;還有其他一系列特性。

Elasticsearch 和 Lucene 的效能高度依賴於具體的查詢和資料,沒有銀彈。即便如此,仍可給出一些從基礎到進階的參考:

限制你的檢索範圍,僅涉及相關資料。 比如,對於每日索引庫,只按相關日期範圍檢索。對於檢索範圍中間的索引,避免使用範圍查詢/過濾器。

使用wildcards時忽略字首wildcards  - 除非你能對term建立倒排索引。雙端wildcards難以最佳化。

關注資源消耗的相關跡象  資料節點的CPU佔用持續飆高嗎?IQ等待走高嗎?看看GC統計。這些可以從profilers工具或者透過 JMX 代理獲得。如果 ParNewGC 消耗了超過15%的時間,去檢查下記憶體日誌。如果有任何的 SerialGC 停頓,你可能真的遇到問題了。不太瞭解這些內容?

沒關係,這個系列博文很好地介紹了 JVM效能  。記住, ES和G1垃圾回收器一起並非最佳  。

如果遇到垃圾回收問題,請不要嘗試調整GC設定。 這一點經常發生,因為預設設定已經很合理了。相反,應該聚焦在減少記憶體分配上。具體怎麼做?參考下文。

如果遇到記憶體問題,但沒有時間解決,可考慮查詢Azul Zing。 這是一個很貴的產品,但僅僅使用它們的JVM就可以提升2倍的吞吐量。不過最終我們並沒有使用它,因為我們無法證明物有所值。

考慮使用快取,包括 Elasticsearch 外快取和 Lucene 級別的快取。 在 Elasticsearch 1.X 中可以透過使用 filter 來控制快取。之後的版本中看起來更難一些,但貌似可以實現自己用於快取的查詢型別。我們在未來升級到2.X的時候可能會做類似的工作。

檢視是否有熱點資料 (比如某個節點承擔了所有的負載)。可以嘗試均衡負載,使用分片分配過濾策略  shard allocation filtering  ,或者嘗試透過叢集重新路由  cluster rerouting  來自行遷移分片。我們已經使用線性最佳化自動重新路由,但使用簡單的自動化策略也大有幫助。

搭建測試環境 (我更喜歡筆記本) 可從線上環境載入一部分代表性的資料 (建議至少有一個分片)。使用線上的查詢回放加壓(較難)。使用本地設定來測試請求的資源消耗。

綜合以上各點,在  Elasticsearch 程式上啟用一個 profiler。這是本列表中最重要的一條。

我們同時透過 Java Mission Control  和  VisualVM  使用飛行記錄器。在效能問題上嘗試投機(包括付費顧問/技術支援)的人是在浪費他們(以及你自己)的時間。排查下 JVM 哪部分消耗了時間和記憶體,然後探索下 Elasticsearch/Lucene 原始碼,檢查是哪部分程式碼在執行或者分配記憶體。

一旦搞清楚是請求的哪一部分導致了響應變慢,你就可以透過嘗試修改請求來最佳化 (比如, 修改term聚合的執行提示  ,或者切換查詢型別)。修改查詢型別或者查詢順序,可以有較大影響。如果不湊效,還可以嘗試最佳化 ES/Lucene 程式碼。這看起來太誇張,卻可以為我們降低3到4倍的CPU消耗和4到8倍的記憶體使用。某些修改很細微(比如  indices query  ),但其他人可能要求我們完全重寫查詢執行。最終的程式碼嚴重依賴於我們的查詢模式,所以可能適合也可能不適合他人使用。因此目前為止我們並沒有開源這部分程式碼。不過這可能是下一篇博文的好素材。

圖表說明: 響應時間 。有/沒有 重寫 Lucene 查詢執行。同時也表明不再有節點每天多次發生記憶體不足。

順便說明下,因為我知道會面臨一個問題:從上一次效能測試我們知道透過升級到 ES 2.X 能小幅提升效能,但是並不能改變什麼。話雖如此,但如果你已經從 ES 1.X 叢集遷移到了 ES 2.X,我們很樂意聽取關於你如何完成遷移的實踐經驗。

如果讀到了這裡,說明你對 Elasticsearch 是真愛啊(或者至少你是真的需要它)。我們很樂意學習你的經驗,以及任何可以分享的內容。歡迎在評論區分享你的反饋和問題。

英文原文連結: http://underthehood.meltwater.com/blog/2018/02/06/running-a-400+-node-es-cluster/

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912579/viewspace-2642481/,如需轉載,請註明出處,否則將追究法律責任。

相關文章