網上乾貨 ElasticSearch詳解與優化設計

原文 http://www.finalshares.com/read-6448

1、簡介

ElasticSearch(簡稱ES)是一個分佈式、Restful的搜索及分析服務器,設計用於分佈式計算;能夠達到實時搜索,穩定,可靠,快速。和Apache Solr一樣,它也是基於Lucence的索引服務器,而ElasticSearch對比Solr的優點在於:
- 輕量級:安裝啓動方便,下載文件之後一條命令就可以啓動。
- Schema free:可以向服務器提交任意結構的JSON對象,Solr中使用schema.xml指定了索引結構。
- 多索引文件支持:使用不同的index參數就能創建另一個索引文件,Solr中需要另行配置。
- 分佈式:Solr Cloud的配置比較複雜。

2013年初,GitHub拋棄了Solr,採取ElasticSearch 來做PB級的搜索。

近年ElasticSearch發展迅猛,已經超越了其最初的純搜索引擎的角色,現在已經增加了數據聚合分析(aggregation)和可視化的特性,如果你有數百萬的文檔需要通過關鍵詞進行定位時,ElasticSearch肯定是最佳選擇。當然,如果你的文檔是JSON的,你也可以把ElasticSearch當作一種“NoSQL數據庫”, 應用ElasticSearch數據聚合分析(aggregation)的特性,針對數據進行多維度的分析。
ElasticSearch一些國內外的優秀案例:
1、Github:
“GitHub使用ElasticSearch搜索20TB的數據,包括13億文件和1300億行代碼”。
2、SoundCloud:
“SoundCloud使用ElasticSearch爲1.8億用戶提供即時而精準的音樂搜索服務”。
3、百度:
百度目前廣泛使用ElasticSearch作爲文本數據分析,採集百度所有服務器上的各類指標數據及用戶自定義數據,通過對各種數據進行多維分析展示,輔助定位分析實例異常或業務層面異常。目前覆蓋百度內部20多個業務線(包括casio、雲分析、網盟、預測、文庫、直達號、錢包、風控等),單集羣最大100臺機器,200個ES節點,每天導入30TB+數據。

2、概念

Cluster和Node
ES可以以單點或者集羣方式運行,以一個整體對外提供search服務的所有節點組成cluster,組成這個cluster的各個節點叫做node。

Index
這是ES存儲數據的地方,類似於關係數據庫的database。

Shards
索引分片,這是ES提供分佈式搜索的基礎,其含義爲將一個完整的index分成若干部分存儲在相同或不同的節點上,這些組成index的部分就叫做shard。
Replicas
索引副本,ES可以設置多個索引的副本,副本的作用一是提高系統的容錯性,當個某個節點某個分片損壞或丟失時可以從副本中恢復。二是提高ES的查詢效率,ES會自動對搜索請求進行負載均衡。
Recovery
代表數據恢復或叫數據重新分佈,ES在有節點加入或退出時會根據機器的負載對索引分片進行重新分配,掛掉的節點重新啓動時也會進行數據恢復。
Gateway
ES索引快照的存儲方式,ES默認是先把索引存放到內存中,當內存滿了時再持久化到本地硬盤。gateway對索引快照進行存儲,當這個ES集羣關閉再重新啓動時就會從gateway中讀取索引備份數據。
Discovery.zen
代表ES的自動發現節點機制,ES是一個基於p2p的系統,它先通過廣播尋找存在的節點,再通過多播協議來進行節點之間的通信,同時也支持點對點的交互。
Transport
代表ES內部節點或集羣與客戶端的交互方式,默認內部是使用tcp協議進行交互,同時它支持http協議(json格式)。

3 安裝部署

ES由java語言實現,運行環境依賴java。ES 1.x版本,官方推薦使用jdk1.7+的環境,建議使用oracle jdk1.8;ES可以去官網下載,本文使用elasticsearch-1.6.0.tar.gz。

4 ES安裝

(1) 解壓elasticsearch-1.6.0.tar.gz,sudo tar -zvxf elasticsearch-1.6.0.tar.gz,在當前路徑生成目錄:elasticsearch-1.6.0;
(2) 配置ES。這裏只做最簡單的配置,修改ES_HOME/config/elasticsearch.yml文件, 相關配置參數

#集羣名稱
cluster.name: elasticsearch
#節點名稱
node.name: "node1"
#節點是否存儲數據
node.data: true
#索引分片數
index.number_of_shards: 5
#索引副本數
index.number_of_replicas: 1
#數據目錄存放位置
path.data: /data/elasticsearch/data
#日誌數據存放位置
path.logs: /data/elasticsearch/log
#索引緩存
index.cache.field.max_size: 500000
#索引緩存過期時間
index.cache.field.expire: 5m

(3)啓動ES。進入ES安裝目錄,執行命令:bin/elasticsearch -d -Xms512m -Xmx512m,然後在瀏覽器輸入http://ip:9200/,查看頁面信息,是否正常啓動。status=200表示正常啓動了。

5 數據索引

ES索引我們可以理解爲數據入庫的一個過程。我們知道ES是基於Lucene框架的一個分佈式檢索平臺。索引的同樣也是基於Lucene創建的,只不過在其上層做了一些封裝。ElasticSearch客戶端支持多種語言如PHP、Java、Python、Perl等,下面以Python爲例:
一,安裝官方提供的Python API

pip install elasticsearch
二,創建索引
調用create或index方法,創建索引。
三,批量錄入索引數據
ElasticSearch批量索引的命令是bulk,利用Python API提交
四,數據檢索查詢
五,數據更新、刪除
對於索引的批量刪除和更新操作,對應的文檔格式如下,更新文檔中的doc節點是必須的。
六、常見錯誤
SerializationError:JSON數據序列化出錯,通常是因爲不支持某個節點值的數據類型
RequestError:提交數據格式不正確
ConflictError:索引ID衝突
TransportError:連接無法建立

6 索引優化

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,這將可以提高將近一半的檢索速度。

內存優化

ES對於內存的消耗,和很多因素相關,諸如數據總量、mapping設置、查詢方式、查詢頻度等等。默認的設置雖開箱即用,但不能適用每一種使用場景。作爲ES的開發、運維人員,如果不瞭解ES對內存使用的一些基本原理,就很難針對特有的應用場景,有效的測試、規劃和管理集羣,從而踩到各種坑,被各種問題挫敗。
1、要理解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壓力,甚至可能讓問題更糟糕。
2、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佔用情況:
輸入圖片說明
2. 查看一個node上所有segment佔用的memory總和:
輸入圖片說明
那麼有哪些途徑減少data node上的segment memory佔用呢? 總結起來有三種方法:
刪除不用的索引。
關閉索引 (文件仍然存在於磁盤,只是釋放掉內存)。需要的時候可以重新打開。
定期對不再更新的索引做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是否夠用。

必須結合實際應用場景,並對集羣使用情況做持續的監控。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章