1. 程式人生 > >Elasticsearch 叢集優化篇

Elasticsearch 叢集優化篇

這裡寫圖片描述
開啟微信掃一掃,關注微信公眾號【資料與演算法聯盟】

轉載請註明出處:http://blog.csdn.net/gamer_gyt
博主微博:http://weibo.com/234654758
Github:https://github.com/thinkgamer

寫在前邊的話

對於叢集的監控和優化是很重要的一部分,如果想持久維護叢集,單單靠增加實體記憶體,cpu,硬碟是不夠的,必須通過一些方法來進行優化。

叢集結點角色分配

在之前得文章中我們介紹到es叢集中得三個角色 master ,data,client

master結點:node.master: true node.data: false

該node伺服器只作為一個主節點,但不儲存任何索引資料,該node伺服器將使用自身空閒得資源,來協調各種建立索引請求或者查詢請求,將這些請求合理分發到相關得node伺服器上。

data結點:node.master: false node.data: true

該node伺服器只作為一個數據節點,只用於儲存索引資料。使該node伺服器功能 單一,只用於資料儲存和資料查詢,降低其資源消耗率。

client結點(負載均衡結點):node.master: false node.data: false

該node伺服器即不會被選作主節點,也不會儲存任何索引資料。該伺服器主要用 於查詢負載均衡。在查詢的時候,通常會涉及到從多個node伺服器上查詢資料,並請 求分發到多個指定的node伺服器,並對各個node伺服器返回的結果進行一個彙總處理, 最終返回給客戶端。

1:關閉data結點得http功能

    針對ElasticSearch叢集中的所有資料節點,不用開啟http服務。將其中的配置 引數這樣設定:http.enabled: false,同時也不要安裝head, bigdesk, marvel等監控 外掛,這樣保證data節點伺服器只需處理建立/更新/刪除/查詢索引資料等操作。

    http功能可以在非資料節點伺服器上開啟,上述相關的監控外掛也安裝到這些服 務器上,用於監控ElasticSearch叢集狀態等資料資訊。這樣做一來出於資料安全考慮,二來出於服務效能考慮。

2:一臺伺服器上最好只部署一個node

    一臺物理伺服器上可以啟動多個Node伺服器節點(通過設定不同的啟動port),但一臺伺服器上的CPU,記憶體,硬碟等資源畢竟有限,從伺服器效能考慮,不建議一臺伺服器上啟動多個node節點。

    在大規模局點,比如100個點,可以專門配備3個Master,可使用3臺具有記憶體的刀片即可,即引數配置為node.master: true,node.data: false;可以按比例配備資料匯聚節點,比如10個,即引數配置為node.master: false ,node.data: false;小規模節點,可以不用如此設定,當然如果依然有效能問題,也是一個優化的措施

叢集得機器記憶體設定

    Elasticsearch 預設採用的是Lucene,至於為什麼es採用這個,原因可能是因為Lucene是一個成熟的、高效能的、可擴充套件的、輕量級的,而且功能強大的搜尋引擎包。Lucene的核心jar包只有一個檔案,而且不依賴任何第三方jar包。更重要的是,它提供的索引資料和檢索資料的功能開箱即用。當然,Lucene也提供了多語言支援,具有拼寫檢查、高亮等功能。當然es使用Lucene作為分詞搜尋包,勢必會造成很大程度上的記憶體消耗。

1:預留一半記憶體給Lucene使用

    一個常見的問題是配置堆太大。你有一個64 GB的機器,覺得JVM記憶體越大越好,想給Elasticsearch所有64 GB的記憶體。

    當然,記憶體對於Elasticsearch來說絕對是重要的,用於更多的記憶體資料提供更快的操作。而且還有一個記憶體消耗大戶-Lucene

    Lucene的設計目的是把底層OS裡的資料快取到記憶體中。Lucene的段是分別儲存到單個檔案中的,這些檔案都是不會變化的,所以很利於快取,同時作業系統也會把這些段檔案快取起來,以便更快的訪問。

    Lucene的效能取決於和OS的互動,如果你把所有的記憶體都分配給Elasticsearch,不留一點給Lucene,那你的全文檢索效能會很差的。

    最後標準的建議是把50%的記憶體給elasticsearch,剩下的50%也不會沒有用處的,Lucene會很快吞噬剩下的這部分記憶體。

2:32GB限制

    在java中,所有的物件都分配在堆上,然後有一個指標引用它。指向這些物件的指標大小通常是CPU的字長的大小,不是32bit就是64bit,這取決於你的處理器,指標指向了你的值的精確位置。

    對於32位系統,你的記憶體最大可使用4G。對於64系統可以使用更大的記憶體。但是64位的指標意味著更大的浪費,因為你的指標本身大了。浪費記憶體不算,更糟糕的是,更大的指標在主記憶體和快取器(例如LLC, L1等)之間移動資料的時候,會佔用更多的頻寬。

    java 使用一個叫記憶體指標壓縮的技術來解決這個問題。它的指標不再表示物件在記憶體中的精確位置,而是表示偏移量。這意味著32位的指標可以引用40億個物件,而不是40億個位元組。最終,也就是說堆記憶體長到32G的實體記憶體,也可以用32bit的指標表示。

    一旦你越過那個神奇的30-32G的邊界,指標就會切回普通物件的指標,每個物件的指標都變長了,就會使用更多的CPU記憶體頻寬,也就是說你實際上失去了更多的記憶體。事實上當記憶體到達40-50GB的時候,有效記憶體才相當於使用記憶體物件指標壓縮技術時候的32G記憶體。

    這段描述的意思就是說:即便你有足夠的記憶體,也儘量不要超過32G,因為它浪費了記憶體,降低了CPU的效能,還要讓GC應對大記憶體。

3:機器記憶體大於64GB

    你可以考慮一臺機器上建立兩個或者更多ES節點,而不要部署一個使用32+GB記憶體的節點。仍然要 堅持50%原則,假設 你有個機器有128G記憶體,你可以建立兩個node,使用32G記憶體。也就是說64G記憶體給ES的堆記憶體,剩下的64G給Lucene。
    如果你選擇第二種,你需要配置

cluster.routing.allocation.same_shard.host:true

這會防止同一個shard的主副本存在同一個物理機上(因為如果存在一個機器上,副本的高可用性就沒有了)

4:ES叢集的heap引數優化

    所謂的heap即資料快取的記憶體大小,ES叢集中消耗記憶體的有以下幾個:

1):segment Memory

    Lucene 把每次生成的倒排索引,叫做一個段(segment)。然後另外使用一個 commit 檔案,記錄索引內所有的 segment。而生成 segment 的資料來源,則是記憶體中的 buffer。由於詞典的size會很大,全部裝載到heap裡不現實,因此Lucene為詞典做了一層字首索引(Term Index),這個索引在Lucene4.0以後採用的資料結構是FST (Finite State Transducer)。這種資料結構佔用空間很小,Lucene開啟索引的時候將其全量裝載到記憶體中,加快磁碟上詞典查詢速度的同時減少隨機磁碟訪問次數。所以ES的data node儲存資料並非只是耗費磁碟空間的,為了加速資料的訪問,每個segment都有會一些索引資料駐留在heap裡。因此segment越多,瓜分掉的heap也越多,並且這部分heap是無法被GC掉的! 理解這點對於監控和管理叢集容量很重要,當一個node的segment memory佔用過多的時候,就需要考慮刪除、歸檔資料,或者擴容了。

2):Filter Cache

    Filter cache是用來快取使用過的filter的結果集的,需要注意的是這個快取也是常駐heap,無法GC的。預設的10% heap size設定工作得夠好了,如果實際使用中heap沒什麼壓力的情況下,才考慮加大這個設定。

3):Field Data cache

    對搜尋結果做排序或者聚合操作,需要將倒排索引裡的資料進行解析,然後進行一次倒排。在有大量排序、資料聚合的應用場景,可以說field data cache是效能和穩定性的殺手。這個過程非常耗費時間,因此ES2.0以前的版本主要依賴這個cache快取已經計算過的資料,提升效能。但是由於heap空間有限,當遇到使用者對海量資料做計算的時候,就很容易導致heap吃緊,叢集頻繁GC,根本無法完成計算過程。ES2.0以後,正式預設啟用Doc Values特性(1.x需要手動更改mapping開啟),將field data在indexing time構建在磁碟上,經過一系列優化,可以達到比之前採用field data cache機制更好的效能。因此需要限制對field data cache的使用,最好是完全不用,可以極大釋放heap壓力。這裡需要注意的是,排序、聚合欄位必須為not analyzed。設想如果有一個欄位是analyzed過的,排序的實際物件其實是詞典,在資料量很大情況下這種情況非常致命。

4):Bulk Queue

    Bulk Queue是做什麼用的?當所有的bulk thread都在忙,無法響應新的bulk request的時候,將request在記憶體裡排列起來,然後慢慢清掉。一般來說,Bulk queue不會消耗很多的heap,但是見過一些使用者為了提高bulk的速度,客戶端設定了很大的併發量,並且將bulk Queue設定到不可思議的大,比如好幾千。這在應對短暫的請求爆發的時候有用,但是如果叢集本身索引速度一直跟不上,設定的好幾千的queue都滿了會是什麼狀況呢? 取決於一個bulk的資料量大小,乘上queue的大小,heap很有可能就不夠用,記憶體溢位了。一般來說官方預設的thread pool設定已經能很好的工作了,建議不要隨意去“調優”相關的設定,很多時候都是適得其反的效果。

5):Indexing Buffer

    Indexing Buffer是用來快取新資料,當其滿了或者refresh/flush interval到了,就會以segment file的形式寫入到磁碟。這個引數的預設值是10% heap size。根據經驗,這個預設值也能夠很好的工作,應對很大的索引吞吐量。但有些使用者認為這個buffer越大吞吐量越高,因此見過有使用者將其設定為40%的。到了極端的情況,寫入速度很高的時候,40%都被佔用,導致OOM。

6):Cluster State Buffer

    ES被設計成每個Node都可以響應使用者的api請求,因此每個Node的記憶體裡都包含有一份叢集狀態的拷貝。這個Cluster state包含諸如叢集有多少個Node,多少個index,每個index的mapping是什麼?有少shard,每個shard的分配情況等等(ES有各類stats api獲取這類資料)。在一個規模很大的叢集,這個狀態資訊可能會非常大的,耗用的記憶體空間就不可忽視了。並且在ES2.0之前的版本,state的更新是由Master Node做完以後全量散播到其他結點的。頻繁的狀態更新都有可能給heap帶來壓力。在超大規模叢集的情況下,可以考慮分叢集並通過tribe node連線做到對使用者api的透明,這樣可以保證每個叢集裡的state資訊不會膨脹得過大。

7):超大搜索聚合結果集的fetch

    ES是分散式搜尋引擎,搜尋和聚合計算除了在各個data node平行計算以外,還需要將結果返回給彙總節點進行彙總和排序後再返回。無論是搜尋,還是聚合,如果返回結果的size設定過大,都會給heap造成很大的壓力,特別是資料匯聚節點。

5:優化建議:

一般分配主機1/4-1/2的記憶體
編輯:elasticsearch/bin/ elasticsearch
加上(10g換成你自己設定的記憶體數):

ES_MIN_MEM=10g
ES_MAX_MEM=10g
ES_HEAP_NEWSIZE=1g

叢集的硬碟和CPU設定

1:硬碟選型:

    硬碟對叢集非常重要,特別是建索引多的情況。磁碟是一個伺服器最慢的系統,對於寫比較重的叢集,磁碟很容易成為叢集的瓶頸。如果可以承擔的器SSD盤,最好使用SSD盤。如果使用SSD,最好調整I/O排程演算法。RAID0是加快速度的不錯方法。

2:自動調整儲存頻寬

    在2.0.0之前,elasticsearch會限制合併速度(merges),預設為20MB/sec。但是這個速率經常是顯得太小,導致合併速度落後於索引速度,進而限制了索引速度。

    現在Elasticsearch2.0.0之後,使用了自動調整合並IO速度方式:如果合併落於索引速度,合併IO速度會逐漸增大,並且隨著合併的持續進行會減小。在索引吞吐量小的時候,即使突然來了一個大的合併任務,這種情況也不會吞噬整個節點可用的IO,極小化的降低對正在進行的查詢和索引的影響。
但是對索引請求大的情況下,允許的合併速度會自動調整到跟上索引的速度。有了2.0.0這個特性,意味著我們不需要管任何的限制值了,只要用預設的就好了。

3:多個path.data 路徑

    如果磁碟空間和IO效能是Elasticsearch的瓶頸的話,使用多個IO裝置(通過設定多個path.data路徑)儲存shards,能夠增加總的儲存空間和提升IO效能。

    在Elasticsearch2.0之前的版本,也是配置多個path.data路徑,但是其相當於RAID 0,每個shards的資料會分佈在所有的磁碟上。當一個節點上有一塊盤壞了的情況下,該節點上所有的shards都會損壞了。需要恢復該節點上的所有shards。

    在2.0.0版本,把這個實現改成了:每個shards所有的資料只會在一塊磁碟上面。這樣即使一個節點的一塊磁碟損壞了,也只是損失了該磁碟上的shards,其它磁碟上的shards安然無事。只需要恢復該塊盤上的shards即可。

    升級到2.0.0版本時,舊版本一個shard分佈到所有磁碟上的資料,會拷貝到一塊盤上。

    對應這個改變,在設計shards時,如果一個節點有10塊磁碟,共3個節點,則shards至少30個,才能分佈在30塊盤上(即最大限度使用磁碟空間)。

叢集的分片和副本配置

分片(Shard)

一個索引會分成多個分片儲存,分片數量在索引建立後不可更改
分片數是與檢索速度非常相關的的指標,如果分片數過少或過多都會導致檢索比較慢。分片數過多會導致檢索時開啟比較多的檔案別外也會導致多臺伺服器之間通訊。而分片數過少會導致單個分片索引過大,所以檢索速度慢。基於索引分片數=資料總量/單分片數的計算公式,在確定分片數之前需要進行單服務單索引單分片的測試,目前我們測試的結果單個分片的內容為10G。

副本(replicas)

每個索引的資料備份數量。
ElasticSearch在建立索引資料時,最好指定相關的shards數量和replicas, 否則會使用伺服器中的預設配置引數shards=5,replicas=1。

因為這兩個屬性的設定直接影響叢集中索引和搜尋操作的執行。假設你有足夠的機器來持有碎片和副本,那麼可以按如下規則設定這兩個值:

  • 1) 擁有更多的碎片可以提升索引執行能力,並允許通過機器分發一個大型的索引;
  • 2) 擁有更多的副本能夠提升搜尋執行能力以及叢集能力。

對於一個索引來說,number_of_shards只能設定一次,而number_of_replicas可以使用索引更新設定API在任何時候被增加或者減少。

這兩個配置引數在配置檔案的配置如下:

index.number_of_shards: 5
number_of_replicas: 1

Elastic官方文件建議:一個Node中一個索引最好不要多於三個shards.配置total_shards_per_node引數,限制每個index每個節點最多分配多少個發片.

叢集的優化總結

1:java jdk版本儘量高一點,否則容易出現bug
2:es叢集結點規劃好,master,client,data node 分開,關閉data node 的http功能
3:合理利用記憶體
4:根據機器數,磁碟數,索引大小等硬體環境,根據測試結果,設定最優的分片數和備份數,單個分片最好不超過10GB,定期刪除不用的索引,做好冷資料的遷移。
5:保守配置記憶體限制引數,儘量使用doc value儲存以減少記憶體消耗,查詢時限制size、from引數。
6:結合實際場景,做好叢集監控