ElasticSearch優化設計

Cha1681 8年前發布 | 11K 次閱讀 ElasticSearch 搜索引擎

來自: http://blog.csdn.net//jiao_fuyou/article/details/50494938


 

1 索引優化  

 

ES索引優化主要從兩個方面解決問題:

 

一、索引數據過程

 

大家可能會遇到索引數據比較慢的過程。其實明白索引的原理就可以有針對性的進行優化。ES索引的過程到相對Lucene的索引過程多了分布式數據的擴展,而這ES主要是用tranlog進行各節點之間的數據平衡。所以從上我可以通過索引的settings進行第一優化:

 

 

這兩個參數第一是到tranlog數據達到多少條進行平衡,默認為5000,而這個過程相對而言是比較浪費時間和資源的。所以我們可以將這個值調大一些還是設為-1關閉,進而手動進行tranlog平衡。第二參數是刷新頻率,默認為120s是指索引在生命周期內定時刷新,一但有數據進來能refresh像lucene里面commit,我們知道當數據addDoucment后,還不能檢索到要commit之后才能行數據的檢索,所以可以將其關閉,在最初索引完后手動refresh一之,然后將索引setting里面的index.refresh_interval參數按需求進行修改,從而可以提高索引過程效率。

 

另外的知道ES索引過程中如果有副本存在,數據也會馬上同步到副本中去。我個人建議在索引過程中將副本數設為0,待索引完成后將副本數按需量改回來,這樣也可以提高索引效率。

"number_of_replicas": 0

 

二、檢索過程

 

其實檢索速度快度與索引質量有很大的關系。而索引質量的好壞主要與以下幾方面有關:

 

1、分片數

 

分片數,與檢索速度非常相關的的指標,如果分片數過少或過多都會導致檢索比較慢。分片數過多會導致檢索時打開比較多的文件別外也會導致多臺服務器之間通訊。而分片數過少會導致單個分片索引過大,所以檢索速度慢。基于索引分片數=數據總量/單分片數的計算公式,在確定分片數之前需要進行單服務單索引單分片的測試,目前我們測試的結果單個分片的內容為10G。

 

2、副本數

 

副本數與索引的穩定性有比較大的關系,如果Node在非正常掛了,經常會導致分片丟失,為了保證這些數據的完整性,可以通過副本來解決這個問題。建議在建完索引后在執行Optimize后,馬上將副本數調整過來。

 

3、分詞

 

分詞對于索引的影響可大可小,看自己把握。大家或許認為詞庫越多,分詞效果越好,索引質量越好,其實不然。分詞有很多算法,大部分基于詞表進行分詞。也就是說詞表的大小決定索引大小。所以分詞與索引膨漲率有直接關系。詞表不應很多,而對文檔相關特征性較強的即可。比如論文的數據進行建索引,分詞的詞表與論文的特征越相似,詞表數量越小,在保證查全查準的情況下,索引的大小可以減少很多。索引大小減少了,那么檢索速度也就提高了。


4、索引段

 

索引段即lucene中的segments概念,我們知道ES索引過程中會refresh和tranlog也就是說我們在索引過程中segments number不只一個。而segments number與檢索是有直接聯系的,segments number越多檢索越慢,而將segments numbers 有可能的情況下保證為1,這將可以提高將近一半的檢索速度。

 

2 內存優化  

ES對于內存的消耗,和很多因素相關,諸如數據總量、mapping設置、查詢方式、查詢頻度等等。默認的設置雖開箱即用,但不能適用每一種使用場景。作為ES的開發、運維人員,如果不了解ES對內存使用的一些基本原理,就很難針對特有的應用場景,有效的測試、規劃和管理集群,從而踩到各種坑,被各種問題挫敗。

 

一、要理解ES如何使用內存,先要尊重下面兩個基本事實:

 

ES是JAVA應用

 

首先,作為一個JAVA應用,就脫離不開JVM和GC。很多人上手ES的時候,對GC一點概念都沒有就去網上抄各種JVM“優化”參數,卻仍然被heap不夠用,內存溢出這樣的問題搞得焦頭爛額。即使對于JVM GC機制不夠熟悉,頭腦里還是需要有這么一個基本概念: 應用層面生成大量長生命周期的對象,是給heap造成壓力的主要原因,例如讀取一大片數據在內存中進行排序,或者在heap內部建cache緩存大量數據。如果GC釋放的空間有限,而應用層面持續大量申請新對象,GC頻度就開始上升,同時會消耗掉很多CPU時間。嚴重時可能惡性循環,導致整個集群停工。因此在使用ES的過程中,要知道哪些設置和操作容易造成以上問題,有針對性的予以規避。

 

底層存儲引擎是基于Lucene的

 

Lucene的倒排索引(Inverted Index)是先在內存里生成,然后定期以段文件(segment file)的形式刷到磁盤的。每個段實際就是一個完整的倒排索引,并且一旦寫到磁盤上就不會做修改。 API層面的文檔更新和刪除實際上是增量寫入的一種特殊文檔,會保存在新的段里。不變的段文件易于被操作系統cache,熱數據幾乎等效于內存訪問。

 

基于以上2個基本事實,我們不難理解,為何官方建議的heap size不要超過系統可用內存的一半。heap以外的內存并不會被浪費,操作系統會很開心的利用他們來cache被用讀取過的段文件。

 

Heap分配多少合適?遵從官方建議就沒錯。 不要超過系統可用內存的一半,并且不要超過32GB。JVM參數呢?對于初級用戶來說,并不需要做特別調整,仍然遵從官方的建議,將xms和xmx設置成和heap一樣大小,避免動態分配heap size就好了。雖然有針對性的調整JVM參數可以帶來些許GC效率的提升,當有一些“壞”用例的時候,這些調整并不會有什么魔法效果幫你減輕heap壓力,甚至可能讓問題更糟糕。

 

二、ES的heap是如何被瓜分掉的? 


以下分別做解讀幾個我知道的內存消耗大戶:

 

Segment Memory

 

Segment不是file嗎?segment memory又是什么?前面提到過,一個segment是一個完備的lucene倒排索引,而倒排索引是通過詞典 (Term Dictionary)到文檔列表(Postings List)的映射關系,快速做查詢的。 由于詞典的size會很大,全部裝載到heap里不現實,因此Lucene為詞典做了一層前綴索引(Term Index),這個索引在Lucene4.0以后采用的數據結構是FST (Finite State Transducer)。 這種數據結構占用空間很小,Lucene打開索引的時候將其全量裝載到內存中,加快磁盤上詞典查詢速度的同時減少隨機磁盤訪問次數。


下面是詞典索引和詞典主存儲之間的一個對應關系圖:

 

 

說了這么多,要傳達的一個意思就是,ES的data node存儲數據并非只是耗費磁盤空間的,為了加速數據的訪問,每個segment都有會一些索引數據駐留在heap里。因此segment越多,瓜分掉的heap也越多,并且這部分heap是無法被GC掉的! 理解這點對于監控和管理集群容量很重要,當一個node的segment memory占用過多的時候,就需要考慮刪除、歸檔數據,或者擴容了。

 

怎么知道segment memory占用情況呢?  CAT API可以給出答案。


  1.  查看一個索引所有segment的memory占用情況:</p>

     

     

    2.  查看一個node上所有segment占用的memory總和:

     

     

    那么有哪些途徑減少data node上的segment memory占用呢? 總結起來有三種方法:

     

    1. 刪除不用的索引。

    2. 關閉索引 (文件仍然存在于磁盤,只是釋放掉內存)。需要的時候可以重新打開。

    3. 定期對不再更新的索引做optimize (ES2.0以后更改為force merge api)。這Optimze的實質是對segment file強制做合并,可以節省大量的segment memory。

     

    Filter Cache

     

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

     

    Field Data cache

     

    對搜索結果做排序或者聚合操作,需要將倒排索引里的數據進行解析,然后進行一次倒排。在有大量排序、數據聚合的應用場景,可以說field data cache是性能和穩定性的殺手。這個過程非常耗費時間,因此ES 2.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過的,排序的實際對象其實是詞典,在數據量很大情況下這種情況非常致命。

     

    Bulk Queue

     

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

     

    Indexing Buffer

     

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

     

    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信息不會膨脹得過大。

     

    超大搜索聚合結果集的fetch

     

    ES是分布式搜索引擎,搜索和聚合計算除了在各個data node并行計算以外,還需要將結果返回給匯總節點進行匯總和排序后再返回。無論是搜索,還是聚合,如果返回結果的size設置過大,都會給heap造成很大的壓力,特別是數據匯聚節點。超大的size多數情況下都是用戶用例不對,比如本來是想計算cardinality,卻用了terms aggregation + size:0這樣的方式; 對大結果集做深度分頁;一次性拉取全量數據等等。

     

    在開發與維護過程中我們總結出以下優化建議:

     

    1. 盡量運行在Sun/Oracle JDK1.7以上環境中,低版本的jdk容易出現莫名的bug,ES性能體現在在分布式計算中,一個節點是不足以測試出其性能,一個生產系統至少在三個節點以上。

    2. 倒排詞典的索引需要常駐內存,無法GC,需要監控data node上segment memory增長趨勢。

    3. 根據機器數,磁盤數,索引大小等硬件環境,根據測試結果,設置最優的分片數和備份數,單個分片最好不超過10GB,定期刪除不用的索引,做好冷數據的遷移。

    4. 保守配置內存限制參數,盡量使用doc value存儲以減少內存消耗,查詢時限制size、from參數。

    5. 如果不使用_all字段最好關閉這個屬性,否則在創建索引和增大索引大小的時候會使用額外更多的CPU,如果你不受限CPU計算能力可以選擇壓縮文檔的_source。這實際上就是整行日志,所以開啟壓縮可以減小索引大小。

    6. 避免返回大量結果集的搜索與聚合。缺失需要大量拉取數據可以采用scan & scroll api來實現。

    7. 熟悉各類緩存作用,如field cache, filter cache, indexing cache, bulk queue等等,要設置合理的大小,并且要應該根據最壞的情況來看heap是否夠用。

    8. 必須結合實際應用場景,并對集群使用情況做持續的監控。

 本文由用戶 Cha1681 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!