衆所周知,ElasticSearch是一個基於Lucene的搜索服務器。它提供了一個分佈式多用戶能力的全文搜索引擎,功能非常強大。
在實際生成中,用的最多的也就是搜索,那麼如何讓搜索性能最大化,也是我們應該深入研究的方面。
下面本文將針對一些常規的地方講訴一些優化技巧,主要從設計、通信、設備幾個方面,涉及數據結構、磁盤I/O、CPU性能、網絡通信開銷、內存資源等
1.爲文件系統cache預留足夠的內存。
在一般情況下,應用程序的讀寫都會被操作系統“cache”(除了direct方式),cache保存在系統物理內存中(線上應該禁用swap),在命重cache可以降低對磁盤的直接訪問頻率。搜索很依賴系統cache的命重,如果某個請求需要從磁盤讀取數據,則一定會產生相對較高的延遲。一般來說至少爲系統cache預留一半的可用物理內存,更大的內存有更高的cache命中率。
2.預索引數據
可以針對某些查詢的模式進行優化數據的索引方式。例如,如果所有文檔都有一個price字段,並且大多數查詢在一個固定的範圍上運行range聚合,那麼可以通過將防衛“pre-indexing”到索引中並使用terms聚合來加快聚合速度。
比如:將原本數據在存入的時候加入price_range字段用於搜索(注:需要需求比較明確,會佔用空間,典型的空間換時間)
搜索的時候就可以使用該字段(price_range)進行聚合
3.爲只讀索引執行force-merge
爲不再更新得只讀索引執行force merge,將Lucene索引整合爲單個分片,可以提升查詢數據。當一個Lucene索引存在多個分段時,每個分段會單獨執行搜索再將結果合併,將只讀索引強制合併爲一個Lucene分段不僅可以優化搜索過程,對索引恢復速度也有好處。
基於如期進行輪詢得索引的舊一般都不會再更新。例如:像每天生成一個新索引,然後用別名關聯,或者使用索引通配符。這種,可以每天選一個時間對昨天的索引執行force-merge,Shrink等操作。
4.預熱指定搜索
在某些情況下,比如將要搞某活動,我們也許能遇見某些搜索QPS會很高,我們可以提前自己將該類搜索進行預熱,讓ES將相應的文檔以及數據緩存提前載入到緩存內存中。
5.調節搜索請求中的batched_reduce_size
該字段使搜索請求中的一個參數。默認情況下,聚合操作在協調節點需要等待所有的分片都取回結果後才執行,使用batched_reduce_size參數可以不等待全部分片返回結果,而是在指定數量的分片返回結果之後就可以先處理一部分reduace這樣可以避免協調節點在等待全部結果的過程中佔用大量內存,避免極端情況下可能導致的OOM。該字段的默認值爲512,從ES 5.4開始支持
6.優化日期搜索
在使用日期範圍進行檢索的時候,使用now的查詢通常不能緩存,因爲匹配到的範圍一直在變化。但是,從用戶體驗的角度來說,切換到一個完整的日期通常可以姐搜的,這樣可以更好利用查詢緩存。
在上例,我們將日期四捨五入到分組,因此如果當前時間是16:31:29.那麼range查詢將匹配my_date字段的值在15:31~16:31.如果幾個用戶同時運行一個包含此範圍的查詢,則查詢緩存可以加快查詢,用於舍入的時間間隔越長,查詢緩存就越有幫助(注:太高的舍入也有可能造成用戶體驗丟失)
當然如果爲了能夠利用查詢緩存,可以很容易將範圍分割城一個大的可緩存部分和一個小的不可換乘部分
篇幅過程,省略部分的反括號,這樣的做法可能在某些情況下運行得更慢,因爲bool查詢引入得開銷可能會冊小利用查詢緩存所節省得開銷
7.優化es的線程池
cache:這是無限制的線程池,爲每個傳入的請求創建一個線程。
fixed:這是一個有着固定大小的線程池,大小由size屬性指定,允許你指定一個隊列(使用queue_size屬性指定)用來保存請求,直到有一個空閒的線程來執行請求。如果Elasticsearch無法把請求放到隊列中(隊列滿了),該請求將被拒絕。有很多線程池(可以使用type屬性指定要配置的線程類型),然而,對於性能來說,最重要的是下面幾個。
index:此線程池用於索引和刪除操作。它的類型默認爲fixed,size默認爲可用處理器的數量,隊列的size默認爲300。
search:此線程池用於搜索和計數請求。它的類型默認爲fixed,size默認爲可用處理器的數量乘以3,隊列的size默認爲1000。
suggest:此線程池用於建議器請求。它的類型默認爲fixed,size默認爲可用處理器的數量,隊列的size默認爲1000。
get:此線程池用於實時的GET請求。它的類型默認爲fixed,size默認爲可用處理器的數量,隊列的size默認爲1000。
bulk:你可以猜到,此線程池用於批量操作。它的類型默認爲fixed,size默認爲可用處理器的數量,隊列的size默認爲50。
percolate:此線程池用於預匹配器操作。它的類型默認爲fixed,size默認爲可用處理器的數量,隊列的size默認爲1000。
elasticsearch.yml中可以設置 :
threadpool.index.type: fixed threadpool.index.size: 100 threadpool.index.queue_size: 500
8.更快的硬件
這一點很明確,ES寫入性能對CPU的性能更加敏感,而搜索性能在一般情況下更多的是在於I/O能力,使用SSD會比旋轉類存儲介質好得多。儘量避免使用NFS等遠程文件系統,如果NFS比本地存儲慢3倍,則在搜索場景下響應速度可能會慢10倍作用。這可能是因爲搜索請求有更多的隨機訪問。
如果搜索類型屬於計算比較多,考慮使用更快的CPU。
9.文檔模型
爲了讓搜索時的成本更低,文檔應該合理建模。特別是應該比秒join操作,嵌套(nested)會使查詢慢幾倍,父子(parent-child)關係可能使查詢慢數百倍,因此,如果可以通過非規範化(denormalizing)文檔來回答相同的問題,則可以顯著地提高搜索速度
10.字段映射
有些字段的內容是數值,但並不意味着其總是應該被映射爲數值類型,例如,一些標識符,將它們映射爲keyword可能會比integer或long更好。
11.避免使用腳本
一般來說,應該避免使用腳本。如果一定要用,則應該優先考慮painless和expressions
12.預熱全局序號(global ordinals)
全局序號是一種數據結構,用於再keyword字段上運行terms聚合。它用一個數值來代表字段中的字符串然後爲每一數值分配一個bucket。這需要一個對global ordinals 和 bucket的構建過程。默認情況下,它們被延遲構造,因爲ES不知道哪些字段將用於terms聚合,哪些字段不會。可以通過配置映射再刷新(refresh)時告訴ES預先加載全局序號:
13.execution hint
terms聚合有兩種不同的機制:
通過直接使用字段來聚合每個桶的數據(map)
通過使用字段的全局序號併爲每個全局序號分配一個bucket(global_ordinals)
ES使用global_ordinals作爲keyword字段的默認選項,它使用全局序號動態地分配bucket,因此內存使用與聚合結果中的字段數量是線性關係。大部分情況下,這種方式的速度很快。
當查詢只會匹配少量文檔時,可以考慮使用map。默認情況下,map只再腳本上運行聚合時使用,因爲它們沒有序號。
14.預加熱cache
如果ES主機重啓,則文件系統緩存將爲空,此時搜索會比較慢,可以使用index.store.preload設置,通過執行文件擴展名,顯式地告訴操作系統應該將文件加載到內存,
例如,配置到elasticsearch.yml文件中
或者在創建索引時設置:
如果文件系統緩存不夠大,則無法保存所有數據,那麼爲太多文件預加載數據到文件系統緩存中會使搜索速度緩慢
15.轉換查詢表達式
在組合查詢中可以通過bool過濾器進行and,or和not的多個邏輯組合檢索,這種組合查詢的表達式在下面的情況下可以做等價轉換:
16.使用近似聚合
近似聚合是以少量的精度爲代價,大幅度提高執行效率,降低了內存使用。近似聚合的使用方式可以參考官方手冊
17.深度優先還是廣度優先
ES 有兩種不同的聚合方式:深度優先和廣度優先。深度優先是默認設置,先構建完整的樹,然後修剪無用節點。大多數情況下深度聚合都能 正常工作,但是有些特殊的場景更適合廣度優先,先執行第一層聚合,再繼續下一層聚合之前會先做修剪。
18.限制搜索請求的分片數
一個搜索請求涉及的分片數量越多,協調節點的CPU內存壓力就越大。默認情況下,ES 會拒絕超過1000個分片的搜索請求。我們應該更好地組長數據,讓搜索請求的分片數更下。如果向調節這個值,則可以通過action.search.shard_count進行修改。
雖然限制了分片數並不能直接提升單個搜索請求的速度,但協調節點的壓力會間接影響搜索速度。
19.利用自適應副本選擇(ARS)提升ES響應速度
爲了充分利用計算機資源和負載均衡,協調節點將搜索輪詢轉發到分片的每個副本,輪詢策略是負載均衡中最簡單的策略,任何一個負載均衡器都具有這種基礎的策略,缺點是不考慮後端實際系統壓力和健康水平。
ES希望搜索請求足夠智能,提出了ARS負載策略,大致爲:對每個搜索請求,將分片的每個副本進行排序,以確定哪個最可能是轉發請求的“最佳”副本。與輪詢方式向分片的每個副本發送請求不同,ES選擇“最佳”副本並將請求路由到那裏。
ES 7.0開始,ARS將默認開啓。