Elasticsearch性能優化彙總——寫入&搜索

概述

本文沉澱Elasticsearch性能優化方式,包含寫入、搜索優化。
推薦結合《Elasticsearch必備原理理解》一起食用~

一、寫入速度優化

在Elasticsearch的默認設置下,是綜合考慮數據可靠性、搜索實時性、寫入速度等因素的。當離開默認設置、追求極致的寫入速度時,很多是以犧牲可靠性和搜索實時性爲代價的。有時候,業務上對數據可靠性和搜索實時性要求並不高,反而對寫入速度要求很高,此時可以調整一些策略,最大化寫入速度。

接下來的優化基於正常運行的前提下,如果是集羣首次批量導入數據,則可以將副本數設置爲0,導入完畢再將副本數調整回去,這樣副本分片只需要複製,節省了構建索引過程。

綜合來說,提升寫入速度從以下幾個方面入手:

  • 加大translog flush間隔,目的是降低iops、writeblock;
  • 加大index refresh間隔,除了降低I/O,更重要的是降低了segment merge頻率;
  • 調整bulk請求;
  • 優化磁盤間的任務均勻情況,將shard儘量均勻分佈到物理主機的各個磁盤;
  • 優化節點間的任務分佈,將任務儘量均勻地發到各節點;
  • 優化Lucene層建立索引的過程,目的是降低CPU佔用率及I/O,例如,禁用_all字段。

1.1 translog flush間隔調整

從ES 2.x開始,在默認設置下,translog的持久化策略爲:每個請求都”flush“。對應配置項如下:
index.translog.durability:request

這是影響ES寫入速度的最大因素。但是隻有這樣,寫操作纔有可能是可靠的。如果系統可以接受一定概率的數據丟失(如主分片寫入成功,副本還沒寫入成功的時候,主機斷電。由於數據既沒有刷到Lucene,translog也沒有刷盤,恢復時translog中沒有這個數據,數據丟失),則調整translog持久化策略爲週期性和一定大小的時候”flush“,例如:
index.translog.durability:async

設置爲async表示translog的刷盤策略按sync_interval配置指定的事件週期進行。

index.translog.sync_interval:120s

加大translog刷盤間隔時間。默認爲5s,不可低於100ms

index.translog.flush_threshold_size:1024mb
超過這個大小會導致refresh操作,產生新的Lucene分段。默認值爲512MB。

1.2 索引刷新間隔refresh_interval

默認情況下索引的refresh_interval爲1秒,這意味着數據寫1秒後就可以被搜索到,每次索引的refresh會產生一個新的Lucene段,這會導致頻繁的segment merge行爲,如果不需要這麼高的搜索實時性,應該降低索引refresh週期,例如:
index.refresh_interval: 30s

1.3 indexing buffer

indexing buffer在爲doc建立索引時使用,當緩衝滿時會刷入磁盤,生成一個新的segment,這是除refresh_interval刷新索引外,另一個生成新segment的機會。每個shard有自己的indexing buffer,下面的這個buffer大小的配置需要除以這個節點上所有shard的數量:

indices.memory.index_buffer_size
默認爲整個堆空間的10%

iindices.memory.min_index_buffer_size
默認爲48MB

indices.memory.max_index_buffer_size
默認爲無限制

在執行大量的索引操作時,indices.memory.index_buffer_size的默認設置可能不夠,這和可用堆內存、單節點上的shard數量相關,可以考慮適當增大該值。

1.4 使用bulk請求

批量寫比一個索引請求只寫單個文檔的效率高得多,但是要注意bulk請求的整體字節數不要太大,太大的請求可能會給集羣帶來內存壓力,因此每個請求最好避免超過幾十兆字節,即使較大的請求看上去執行得更好。

1.5 索引過程調整和優化

1.5.1 自動生成doc ID

通過ES寫入流程可以看出,寫入doc時如果外部指定了id,則ES會先嚐試讀取原來doc的版本號,以判斷是否需要更新。這會涉及一次讀取磁盤的操作,通過自動生成doc ID可以避免這個環節。

1.5.2 調整字段Mappings

  1. 減少字段數量,對於不需要建立索引的字段,不寫入ES;
  2. 將不需要建立索引的字段index屬性設置爲not_analyzed或no。對字段不分詞,或者不索引,可以減少很多運算操作,降低CPU佔用。尤其是binary類型,默認情況下佔用CPU非常高,而這種類型進行分詞通常沒有什麼意義;
  3. 減少字段內容長度,如果原始數據的大段內容無須全部建立索引,則可以儘量減少不必要的內容;
  4. 使用不同的分詞器(analyzer),不同的分析器在索引過程中運算複雜度也有較大的差異。

1.5.3 調整_source字段

_source字段用於存儲doc原始數據,對於部分不需要存儲的字段,可以通過includes excludes過濾,或者將_source禁用,一般用於索引和數據分離。

這樣可以降低I/O的壓力,不過實際場景中大多不會禁用_source,而即使過濾某些字段,對於寫入速度的提升作用也不大,滿負荷寫入情況下,基本是CPU先跑滿了,瓶頸在於CPU。

1.5.4 禁用_all字段

從ES 6.0開始,_all字段默認認爲不啓用,而在此前的版本中,_all字段默認是開啓的。_all字段中包含所有字段分詞後的關鍵詞,作用是可以在搜索的時候不指定特定字段,從所有字段中檢索。

1.5.5 對Analyzed的字段禁用Norms

Norms用於在搜索時計算doc的評分,如果不需要評分,則可以將其禁用:
"title " : { "type" : "string" , "norms" : { "enabled" : false}}

1.5.6 index_options設置

index_options用於控制在建立倒排索引過程中,哪些內容會被添加到倒排索引,例如,doc數量、詞頻、positions、offsets等信息,優化這些設置可以一定程度降低索引過程中的運算任務,節省CPU佔用率。
不過在實際場景中,通常很難確定業務將來會不會用到這些信息,除非一開始方案就明確是這樣設計的。

二、搜索速度的優化

2.1 爲文件系統cache預留足夠的內存

在一般情況下,應用程序的讀寫都會被操作系統“cache”(除了direct方式),cache保存在系統物理內存中(線上應該禁用swap),命中cache可以降低對磁盤的直接訪問率。搜索很依賴對系統cache的命中,如果某個請求需要從磁盤讀取數據,則一定會產生相對較高的延遲。應該至少爲系統cache預留一半的可用物理內存,更大的內存有更高的cache命中率。

2.2 使用更快的硬件

寫入性能對CPU的性能更敏感,而搜索性能在一般情況下更多的是在於I/O能力,使用SSD會比旋轉類存儲介質好得多。儘量避免使用NFS等遠程文件系統,如果NFS比本地存儲慢3倍,則在搜索場景下響應速度可能會慢10倍左右。這可能是因爲搜索請求有更多的隨機訪問。

如果搜索類型屬於計算比較多,則可以考慮使用更快的CPU。

2.3 文檔模型

爲了讓搜索時的成本更低,文檔應該合理建模。特別是應該避免join操作,嵌套(nested)會使查詢慢幾倍,父子(parent-child)關係可能使查詢慢數百倍,因此,如果可以通過非規範化文檔來回答相同的問題,則可以顯著地提高搜索速度。

2.4 字段映射

有些字段的內容是數值,但並不意味着其總是應該被映射爲數值類型,例如,一些標識符,將它們映射爲keyword可能會比integer或long更好。

2.5 避免使用腳本

一般來說,應該避免使用腳本。如果一定要用,則應該優先考慮painless和expressions。

2.6 爲只讀索引執行force-merge

可以對舊數據,定時強制執行強制段合併,將Lucene索引合併爲單個分段,可以提升查詢速度。當一個Lucene索引不存在多個分段時,每個分段會單獨執行搜索再將結果合併,將只讀索引強制合併爲Lucene分段不僅可以優化搜索過程,對索引恢復速度也有好處。

參考資料:
《Elasticsearch源碼解析與優化實戰》

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