1. 程式人生 > >Elasticsearch效能優化總結

Elasticsearch效能優化總結

Elasticsearch是目前大資料領域最熱門的技術棧之一,經過近8年的發展,已從0.0.X版升級至6.X版本,雖然增加了很多的特性和功能,但是在主體架構上,還是沒有太多的變化。下面就把我對於ES使用實踐的一些經驗總結一下,供大家參考;也請大家拍磚。

一、 硬體環境選擇:

如果有條件,儘可能使用SSD硬碟, 不錯的CPU。ES的厲害之處在於ES本身的分散式架構以及lucene的特性。IO的提升,會極大改進ES的速度和效能。

二、系統拓樸設計:

ES叢集在架構拓樸時,一般都會採用Hot-Warm的架構模式,即設定3種不同型別的節點:Master節點、Hot 節點和 Warm節點。

Master節點設定:一般會設定3個專用的maste節點,以提供最好的彈性擴充套件能力。當然,必須注意discovery.zen.minimum_master_nodes 屬性的設定,以防split-brain問題,使用公式設定:N/2+1(N為候選master節點數)。 該節點保持: node.data: false ; 因為master節點不參與查詢、索引操作,僅負責對於叢集管理,所以在CPU、記憶體、磁碟配置上,都可以比資料節點低很多。

Hot節點設定: 索引節點(寫節點),同時保持近期頻繁使用的索引。 屬於IO和CPU密集型操作,建議使用SSD的磁碟型別,保持良好的寫效能;節點的數量設定一般是大於等於3個。將節點設定為hot型別:

node.attr.box_type: hot

針對index, 通過設定index.routing.allocation.require.box_type: hot 可以設定將索引寫入hot節點。

Warm節點設定: 用於不經常訪問的read-only索引。由於不經常訪問,一般使用普通的磁碟即可。記憶體、CPU的配置跟Hot節點保持一致即可;節點數量一般也是大於等於3個。

當索引不再被頻繁查詢時,可通過index.routing.allocation.require.box_type: warm, 將索引標記為warm, 從而保證索引不寫入hot節點,以便將SSD磁碟資源用在刀刃上。一旦設定這個屬性,ES會自動將索引合併到warm節點。同時,也可以在elasticsearch.yml中設定 index.codec: best_compression 保證warm 節點的壓縮配置

Coordinating節點:協調節點用於做分散式裡的協調,將各分片或節點返回的資料整合後返回。在ES叢集中,所有的節點都有可能是協調節點,但是,可以通過設定node.master、node.data 、 node.ingest 都為 false 來設定專門的協調節點。需要較好的CPU和較高的記憶體。

三、ES的記憶體設定:

由於ES構建基於lucene, 而lucene設計強大之處在於lucene能夠很好的利用作業系統記憶體來快取索引資料,以提供快速的查詢效能。lucene的索引檔案segements是儲存在單檔案中的,並且不可變,對於OS來說,能夠很友好地將索引檔案保持在cache中,以便快速訪問;因此,我們很有必要將一半的實體記憶體留給lucene ; 另一半的實體記憶體留給ES(JVM heap )。所以, 在ES記憶體設定方面,可以遵循以下原則:

1. 當機器記憶體小於64G時,遵循通用的原則,50%給ES,50%留給lucene。

2.  當機器記憶體大於64G時,遵循以下原則:

a. 如果主要的使用場景是全文檢索, 那麼建議給ES Heap分配 4~32G的記憶體即可;其它記憶體留給作業系統, 供lucene使用(segments cache), 以提供更快的查詢效能。

b.  如果主要的使用場景是聚合或排序, 並且大多數是numerics, dates, geo_points 以及not_analyzed的字元型別, 建議分配給ES Heap分配 4~32G的記憶體即可,其它記憶體留給作業系統,供lucene使用(doc values cache),提供快速的基於文件的聚類、排序效能。

c.  如果使用場景是聚合或排序,並且都是基於analyzed 字元資料,這時需要更多的 heap size, 建議機器上執行多ES例項,每個例項保持不超過50%的ES heap設定(但不超過32G,堆記憶體設定32G以下時,JVM使用物件指標壓縮技巧節省空間),50%以上留給lucene。

3. 禁止swap,一旦允許記憶體與磁碟的交換,會引起致命的效能問題。 通過: 在elasticsearch.yml 中 bootstrap.memory_lock: true, 以保持JVM鎖定記憶體,保證ES的效能。

4. GC設定原則:

a. 保持GC的現有設定,預設設定為:Concurrent-Mark and Sweep (CMS),別換成G1GC,因為目前G1還有很多BUG。

b. 保持執行緒池的現有設定,目前ES的執行緒池較1.X有了較多優化設定,保持現狀即可;預設執行緒池大小等於CPU核心數。如果一定要改,按公式((CPU核心數* 3)/ 2)+ 1 設定;不能超過CPU核心數的2倍;但是不建議修改預設配置,否則會對CPU造成硬傷。

四、 叢集分片設定:

ES一旦建立好索引後,就無法調整分片的設定,而在ES中,一個分片實際上對應一個lucene 索引,而lucene索引的讀寫會佔用很多的系統資源,因此,分片數不能設定過大;所以,在建立索引時,合理配置分片數是非常重要的。一般來說,我們遵循一些原則:

1. 控制每個分片佔用的硬碟容量不超過ES的最大JVM的堆空間設定(一般設定不超過32G,參加上文的JVM設定原則),因此,如果索引的總容量在500G左右,那分片大小在16個左右即可;當然,最好同時考慮原則2。

2. 考慮一下node數量,一般一個節點有時候就是一臺物理機,如果分片數過多,大大超過了節點數,很可能會導致一個節點上存在多個分片,一旦該節點故障,即使保持了1個以上的副本,同樣有可能會導致資料丟失,叢集無法恢復。所以, 一般都設定分片數不超過節點數的3倍。

五、 Mapping建模:

1. 儘量避免使用nested或 parent/child,能不用就不用;nested query慢, parent/child query 更慢,比nested query慢上百倍;因此能在mapping設計階段搞定的(大寬表設計或採用比較smart的資料結構),就不要用父子關係的mapping。

2. 如果一定要使用nested fields,保證nested fields欄位不能過多,目前ES預設限制是50。參考:

index.mapping.nested_fields.limit :50

因為針對1個document, 每一個nested field, 都會生成一個獨立的document, 這將使Doc數量劇增,影響查詢效率,尤其是JOIN的效率。

3. 避免使用動態值作欄位(key),  動態遞增的mapping,會導致叢集崩潰;同樣,也需要控制欄位的數量,業務中不使用的欄位,就不要索引。控制索引的欄位數量、mapping深度、索引欄位的型別,對於ES的效能優化是重中之重。以下是ES關於欄位數、mapping深度的一些預設設定:

index.mapping.nested_objects.limit :10000

index.mapping.total_fields.limit:1000

index.mapping.depth.limit: 20

六、 索引優化設定:

1.設定refresh_interval 為-1,同時設定number_of_replicas 為0,通過關閉refresh間隔週期,同時不設定副本來提高寫效能。

2. 修改index_buffer_size 的設定,可以設定成百分數,也可設定成具體的大小,大小可根據叢集的規模做不同的設定測試。

indices.memory.index_buffer_size:10%(預設)

indices.memory.min_index_buffer_size: 48mb(預設)

indices.memory.max_index_buffer_size

3. 修改translog相關的設定:

a. 控制資料從記憶體到硬碟的操作頻率,以減少硬碟IO。可將sync_interval的時間設定大一些。

index.translog.sync_interval:5s(預設)。

b. 控制tranlog資料塊的大小,達到threshold大小時,才會flush到lucene索引檔案。

index.translog.flush_threshold_size:512mb(預設)

4. _id欄位的使用,應儘可能避免自定義_id, 以避免針對ID的版本管理;建議使用ES的預設ID生成策略或使用數字型別ID做為主鍵。

5. _all欄位及_source欄位的使用,應該注意場景和需要,_all欄位包含了所有的索引欄位,方便做全文檢索,如果無此需求,可以禁用;_source儲存了原始的document內容,如果沒有獲取原始文件資料的需求,可通過設定includes、excludes 屬性來定義放入_source的欄位。

6. 合理的配置使用index屬性,analyzed 和not_analyzed,根據業務需求來控制欄位是否分詞或不分詞。只有 groupby需求的欄位,配置時就設定成not_analyzed, 以提高查詢或聚類的效率。

七、 查詢優化:

1. query_string 或 multi_match的查詢欄位越多, 查詢越慢。可以在mapping階段,利用copy_to屬性將多欄位的值索引到一個新欄位,multi_match時,用新的欄位查詢。

2. 日期欄位的查詢, 尤其是用now 的查詢實際上是不存在快取的,因此, 可以從業務的角度來考慮是否一定要用now, 畢竟利用query cache 是能夠大大提高查詢效率的。

3. 查詢結果集的大小不能隨意設定成大得離譜的值, 如query.setSize不能設定成 Integer.MAX_VALUE, 因為ES內部需要建立一個數據結構來放指定大小的結果集資料。

4. 儘量避免使用script,萬不得已需要使用的話,選擇painless & experssions 引擎。一旦使用script查詢,一定要注意控制返回,千萬不要有死迴圈(如下錯誤的例子),因為ES沒有指令碼執行的超時控制,只要當前的指令碼沒執行完,該查詢會一直阻塞。

如: {

    “script_fields”:{

        “test1”:{

            “lang”:“groovy”,

            “script”:“while(true){print 'don’t use script'}”

        }

    }

}

5. 避免層級過深的聚合查詢, 層級過深的group by , 會導致記憶體、CPU消耗,建議在服務層通過程式來組裝業務,也可以通過pipeline的方式來優化。

6. 複用預索引資料方式來提高AGG效能:

如通過 terms aggregations 替代 range aggregations, 如要根據年齡來分組,分組目標是: 少年(14歲以下) 青年(14-28) 中年(29-50) 老年(51以上), 可以在索引的時候設定一個age_group欄位,預先將資料進行分類。從而不用按age來做range aggregations, 通過age_group欄位就可以了。

7. Cache的設定及使用:

a) QueryCache: ES查詢的時候,使用filter查詢會使用query cache, 如果業務場景中的過濾查詢比較多,建議將querycache設定大一些,以提高查詢速度。

indices.queries.cache.size: 10%(預設),可設定成百分比,也可設定成具體值,如256mb。

當然也可以禁用查詢快取(預設是開啟), 通過index.queries.cache.enabled:false設定。

b) FieldDataCache: 在聚類或排序時,field data cache會使用頻繁,因此,設定欄位資料快取的大小,在聚類或排序場景較多的情形下很有必要,可通過indices.fielddata.cache.size:30% 或具體值10GB來設定。但是如果場景或資料變更比較頻繁,設定cache並不是好的做法,因為快取載入的開銷也是特別大的。

c) ShardRequestCache: 查詢請求發起後,每個分片會將結果返回給協調節點(Coordinating Node), 由協調節點將結果整合。

如果有需求,可以設定開啟;  通過設定index.requests.cache.enable: true來開啟。

不過,shard request cache只快取hits.total, aggregations, suggestions型別的資料,並不會快取hits的內容。也可以通過設定indices.requests.cache.size: 1%(預設)來控制快取空間大小。

八、 叢集運維及恢復:

待續。