為什麼Elasticsearch查詢變得這麼慢了?

2021-02-23 銘毅天下Elasticsearch
0、引言

Elasticsearch社區中經常看到慢查詢問題:「你能幫我看看Elasticsearch的響應時間嗎?」或者是:「我的ES查詢耗時很長,我該怎麼做?」

包含但不限於:Nested慢查詢、集群查詢慢、range查詢慢等問題。

1、兩個維度

每當我們得到這些類型的問題時,我們首先要深入研究兩個主要方面:

我們將首先關注開發方面的問題。 我們將獲得慢查詢,討論DSL查詢語言,並查看有助於改進Elasticsearch查詢的小型常規選項。

2、開發維度—你的查詢有多慢?

第一步是查看發送到群集的查詢所花費的時間。 在研究如何打開慢速日誌時,Elasticsearch文檔可能有點不清楚,因此我將在下面展示一些示例。

默認情況下,所有版本的Elasticsearch都會關閉慢速日誌,因此您必須對群集設置和索引設置進行一些更新。
這些示例適用於使用elasticsearch 6.2,但您可以在此處找到所有以前的版本。
只需將$ES_version替換為您正在使用的版本,
例如5.5版本設置官網參考:http://t.cn/E7Hq2NG。

向_cluster API發送放置請求以定義要打開的慢速日誌級別:警告,信息,調試和跟蹤。 (有關日誌記錄級別的更多信息參考:http://t.cn/E7Hqc5e。)

1curl -XPUT http://localhost:$ES_PORT/_cluster/settings -H 『Content-Type: application/json』 -d』
2{
3"transient" : {
4"logger.index.search.slowlog" : "DEBUG",
5"logger.index.indexing.slowlog" : "DEBUG"
6}
7}'
8

所有慢速日誌記錄都在索引級別啟用,因此您可以再次向index _settings API發送請求以打開,但如果您每月,每季度等都在滾動更新索引,則還必須添加到索引模板中。

將API調用調整為索引設置以匹配您想要命中的慢日誌時間閾值。 (您可以設置為0s以分析實例並收集正在發送的所有查詢,並設置為-1以關閉慢速日誌。)
使用您在_clustersettings中選擇使用的日誌級別設置。 在這個例子中,「DEBUG」。
ES_PORT是一個持久的環境變量。

1curl -XPUT http:
2 '{"index.search.slowlog.threshold.query.debug": "-1",
3   "index.search.slowlog.threshold.fetch.debug": "-1",}'

現在,您需要收集日誌。 每個分片生成慢速日誌並按數據節點收集。 如果您只有一個包含五個主分片的數據節點(這是默認值),您將在慢速日誌中看到一個查詢的五個條目。 由於Elasticsearch中的搜索發生在每個分片中,因此每個分片都會看到一個。 每個數據節點存儲慢速日誌,默認情況如下

/var/log/elasticsearch/$ClusterID_index_slowlog_query 

和/var/log/elasticsearch/$ClusterID_index_slowlog_fetch.

如您所見,搜索慢速日誌再次根據搜索階段分解為單獨的日誌文件:獲取(fetch)和查詢(query)。
現在我們在日誌中有結果,我們可以拉入一個條目並將其分開。

1[2018-05-21T12:35:53,352][DEBUG ][index.search.slowlog.query] 
2[DwOfjJF] [blogpost-slowlogs][4] took[1s], took_millis[0], types[], 
3stats[], search_type[QUERY_THEN_FETCH], total_shards[5], 
4source[{"query":{"match":{"name":{"query":"hello world",
5 "operator":"OR","prefix_length":0,"max_expansions":50,
6"fuzzy_transpositions" :true,"lenient":false,"zero_terms_query":
7 "NONE","boost":1.0}}},"sort":[{"price": {"order":"desc"}}]}], 

在這裡,您看到:

1 日期
2 時間戳 
3 日誌級別 
4 慢速類型 
5 節點名稱 
6 索引名稱 
7 分片號 
8 時間花費 
9 查詢的主體(_source>)

一旦我們獲得了我們認為花費的時間太長的查詢,我們就可以使用一些工具來分解查詢:

工具1:Profile API

Profile API提供有關搜索的信息頁面,並分解每個分片中發生的情況,直至每個搜索組件(match/range/match_phrase等)的各個時間。 搜索越詳細,_profile輸出越詳細。

工具2:Kibana profiling 工具

這與_profileAPI密切相關。 它提供了各個搜索組件的完美的可視化效果表徵各個分解階段以及各階段查詢的時間消耗。 同樣,這允許您輕鬆選擇查詢的問題區域。


3、開發維度—Elasticsearch的查詢原理

現在我們已經確定了一個很慢的查詢,我們通過一個分析器profile來運行它。 但是,查看單個組件時間結果並未使搜索速度更快。 怎麼辦?

通過兩個階段(下面)了解查詢的工作原理,允許您以從速度和相關性方面獲得Elasticsearch最佳結果的方式重新設計查詢。

3.1 Query階段3.2 fetch階段

獲取階段由路由節點開始,路由節點確定每個分片發送的50個(5個分片×10個結果)結果中的前10個文檔。

路由節點向分片發出對前10個文檔的請求。 (可能是包含最高得分文檔的一個分片,或者它們可能分散在多個分片中。)

返回列表後,主節點會在查詢響應的_hits部分中顯示文檔。

4、開發維度—filter過濾器查詢優化

結果分數是Elasticsearch的關鍵。 通常,當您使用搜尋引擎時,您需要最準確的結果。 例如,如果您正在搜索「蘋果」,您不希望結果包括「蘋果手機」。

Elasticsearch根據您提供的參數對查詢結果進行評分。

雖然查詢相關性不是本篇文章的重點,但重要的是在此提及,因為如果您有快速搜索需求但結果不是您要查找的結果,則整個搜索都是浪費時間。
那麼,你如何加快搜索速度?

4.1 查詢時,使用query-bool-filter組合取代普通query

提高搜索性能的一種方法是使用過濾器。 過濾後的查詢可能是您最需要的。

首先過濾是很重要的,因為搜索中的過濾器不會影響文檔分數的結果,因此您在資源方面使用很少的資源來將搜索結果範圍縮小到很小。

使用過濾查詢,結合使用布爾匹配,您可以在評分之前搜索包含X的所有文檔,或者不包含Y的所有文檔。此外,可以filter是可以被緩存的。

過濾器filter查詢不是加速Elasticsearch查詢的唯一方法。

【from騰訊】默認情況下,ES通過一定的算法計算返回的每條數據與查詢語句的相關度,並通過score欄位來表徵。

但對於非全文索引的使用場景,用戶並不care查詢結果與查詢條件的相關度,只是想精確的查找目標數據。

此時,可以通過query-bool-filter組合來讓ES不計算score,
並且儘可能的緩存filter的結果集,供後續包含相同filter的查詢使用,提高查詢效率。

filter原理推薦閱讀:吃透 | Elasticsearch filter和query的不同

5、開發維度——其他優化5.1 避免使用script查詢

避免使用腳本查詢來計算匹配。 推薦:建立索引時存儲計算欄位。

例如,我們有一個包含大量用戶信息的索引,我們需要查詢編號以「1234」開頭的所有用戶。 

您可能希望運行類似「source」的腳本查詢:

doc ['num'].value.startsWith('1234')

此查詢非常耗費資源並且會降低整個系統的速度。  合理的建議:考慮在索引時添加名為「num_prefix」的欄位。
然後我們可以查詢

「name_prefix」:「1234」。

5.2 避免使用wildcard查詢

主要原因:
wildcard類似mysql中的like,和分詞完全沒有了關係。

出現錯誤:
用戶輸入的字符串長度沒有做限制,導致首尾通配符中間可能是很長的一個字符串。 後果就是對應的wildcard Query執行非常慢,非常消耗CPU。

根本原因:
為了加速通配符和正則表達式的匹配速度,Lucene4.0開始會將輸入的字符串模式構建成一個DFA (Deterministic Finite Automaton),帶有通配符的pattern構造出來的DFA可能會很複雜,開銷很大。

可能的優化方案:

wildcard query應杜絕使用通配符打頭,實在不得已要這麼做,就一定需要限制用戶輸入的字符串長度。

最好換一種實現方式,通過在index time做文章,選用合適的分詞器,比如nGram tokenizer預處理數據,然後使用更廉價的term query來實現同等的模糊搜索功能。

對於部分輸入即提示的應用場景,可以考慮優先使用completion suggester, phrase/term/suggeter一類性能更好,模糊程度略差的方式查詢,待suggester沒有匹配結果的時候,再fall back到更模糊但性能較差的wildcard, regex, fuzzy一類的查詢。

詳盡原理參考:https://elasticsearch.cn/article/171

5.3 合理使用keyword類型

ES5.x裡對數值型欄位做TermQuery可能會很慢。

在ES5.x+裡,一定要注意數值類型是否需要做範圍查詢,看似數值,但其實只用於Term或者Terms這類精確匹配的,應該定義為keyword類型。

典型的例子就是索引web日誌時常見的HTTP Status code。

詳盡原理參考:https://elasticsearch.cn/article/446

5.4 控制欄位的返回

一是:數據建模規劃的時候,在Mapping節點對於僅存儲、是否構建倒排索引通過enabled、index參數進行優化。

二是:_source控制返回,不必要的欄位不需要返回,舉例:採集的原文章詳情內容頁,根據需要決定是否返回。

5.5 讓Elasticsearch幹它擅長的事情

在檢索/聚合結果後,業務系統還有沒有做其他複雜的操作,花費了多少時間?

這塊是最容易忽視的時間耗費擔當。

Elasticsearch顯然更擅長檢索、全文檢索,其他不擅長的事情,儘量不要ES處理。比如:頻繁更新、確保數據的ACID特性等操作。

6、配置維度——核心配置6.1 節點職責明晰區分路由節點、數據節點、候選主節點。

路由節點的主要優點是:

由於路由節點減少了搜索和聚合的壓力,因此數據節點上的內存壓力略有降低;

「智能路由」——因為他們知道所有數據存在的地方,他們可以避免額外的跳躍;「智能路由」——因為他們知道所有數據存在的地方,他們可以避免額外的跳躍;

從架構上講,將路由節點用作集群的訪問點非常有用,因此您的應用程式無需了解詳細信息。 從架構上講,將路由節點用作集群的訪問點非常有用,因此您的應用程式無需了解詳細信息。

儘量將主節點與數據節點分開,因為它將減少所有群集的負載。

以下時間開始考慮專用主節點:

群集大小開始變得難以駕馭,可能像10個節點或更高?

您會看到由於負載導致集群不穩定(通常由內存壓力引起,導致長GC,導致主節點暫時從集群中退出)您會看到由於負載導致集群不穩定(通常由內存壓力引起,導致長GC,導致主節點暫時從集群中退出)

分離主節點的主要目的是使「主節點的職責」與負載隔離,因為高負載可能導致長GC,從而導致集群不穩定。

分離主節點後,一個高負載的集群只會影響數據節點(顯然仍然不好),但能保證主節點穩定,一旦集群超載,基本上專門的主節點給你喘息的空間,而不是整個集群走向崩潰。

另外,與數據節點相比,主節點通常可以非常「輕」。幾GB的RAM,中等CPU,普通磁碟等或許就能滿足需求(需要根據實際業務場景權衡)。

推薦閱讀:http://t.cn/E7HM4ML

6.2 分配合理的堆內存

搜尋引擎旨在快速提供答案。 為此,他們使用的大多數數據結構必須駐留在內存中。 在很大程度上,他們假設你為他們提供了足夠的記憶。 如果不是這種情況,這可能會導致問題 - 不僅僅是性能問題,還有集群的可靠性問題。

合理的堆內存大小配置建議:宿主機內存大小的一半和31GB,取最小值

推薦閱讀:乾貨 | 吃透Elasticsearch 堆內存

6.3 設置合理的分片數和副本數

shard數量設置過多或過低都會引發一些問題。

shard數量過多,則批量寫入/查詢請求被分割為過多的子寫入/子查詢,導致該index的寫入、查詢拒絕率上升;

對於數據量較大的index,當其shard數量過小時,無法充分利用節點資源,造成機器資源利用率不高 或 不均衡,影響寫入/查詢的效率。

對於每個index的shard數量,可以根據數據總量、寫入壓力、節點數量等綜合考量後設定,然後根據數據增長狀態定期檢測下shard數量是否合理。

騰訊基礎架構部資料庫團隊的推薦方案是:

對於數據量較小(100GB以下)的index,往往寫入壓力查詢壓力相對較低,一般設置3~5個shard,副本設置為1即可(也就是一主一從,共兩副本)

對於數據量較大(100GB以上)的index:一般把單個shard的數據量控制在(20GB~50GB)

讓index壓力分攤至多個節點:可通過index.routing.allocation.totalshardsper_node參數,強制限定一個節點上該index的shard數量,讓shard儘量分配到不同節點上

綜合考慮整個index的shard數量,如果shard數量(不包括副本)超過50個,就很可能引發拒絕率上升的問題,此時可考慮把該index拆分為多個獨立的index,分攤數據量,同時配合routing使用,降低每個查詢需要訪問的shard數量。

建議參考:Elasticsearch究竟要設置多少分片數?

6.4 設置合理的線程池和隊列大小

節點包含多個線程池,以便改進節點內線程內存消耗的管理方式。 其中許多池也有與之關聯的隊列,這允許保留掛起的請求而不是丟棄。

search線程——用於計數/搜索/推薦操作。 線程池類型為fixed_auto_queue_size,大小為int((available of available_ * 3)/ 2)+ 1,初始隊列大小為1000。

5.X版本之後,線程池設置是節點級設置。因此,無法通過群集設置API更新線程池設置。

查看線程池的方法:

1GET /_cat/thread_pool

6.5 硬體資源的實時監控

排查一下慢查詢時間點的時候,注意觀察伺服器的CPU, load average消耗情況,是否有資源消耗高峰,可以藉助:xpack、cerbero或者elastic-hd工具查看。

當您遇到麻煩並且群集工作速度比平時慢並且使用大量CPU功率時,您知道需要做一些事情才能使其再次運行。 當Hot Threads API可以為您提供查找問題根源的必要信息。 熱線程hot thread是一個Java線程,它使用高CPU量並執行更長的時間。

Hot Threads API返回有關ElasticSearch代碼的哪一部分是最耗費cpu或ElasticSearch由於某種原因而被卡住的信息。

熱線程使用方法:

1GET /_nodes/hot_threads

7、小結

回答文章開頭的問題:——為什麼Elasticsearch查詢變得這麼慢了?

和大數據量的業務場景有關,您可以通過幾個簡單的步驟優化查詢:

啟用慢速日誌記錄,以便識別長時間運行的查詢

通過_profiling API運行已識別的搜索,以查看各個子查詢組件的時間通過_profiling API運行已識別的搜索,以查看各個子查詢組件的時間

過濾,過濾,過濾過濾,過濾,過濾

Elasticsearch優化非一朝一夕之功,需要反覆研究、實踐甚至閱讀源碼分析。
本文綜合了國外、國內很多優秀的實踐建議,核心點都已經實踐驗證可行。
歡迎大家留言討論!

參考:
http://t.cn/E7HJaPI
http://t.cn/RQSwH4X
http://t.cn/RInoI4c

加入星球,更短時間更快習得更多乾貨!

相關焦點

  • 在elasticsearch中使用function_score查詢
    前端時間一直在研究kubernets+.net core相關,本打算寫幾篇k8s的文章,但在公司聽到同事遇到個需求一直沒有搞定,於是出於程式設計師的本能了解了一下,就是需要根據左上角,右下角的經緯度坐標查詢一批在地圖上均勻分布!
  • ElasticSearch-hard插件及IK分詞器安裝
    當然有了:elasticSeard-head這個插件就可以實現這個功能。我們先來看看使用插件後的效果:ElasticSearch-head是什麼?ElasticSearch-head:是集群管理、數據可視化、增刪改查、查詢語句可視化工具。
  • ElasticSearch
    6.2 詞條查詢6.2.1 termQuery6.2.2 matchQuery6.3 模糊查詢6.4 範圍查詢6.5 queryString查詢 -- 多欄位查詢6.6 布爾查詢-- 多條件查詢6.7 聚合查詢6.8 高亮查詢6.9 重建索引及別名
  • 讓Elasticsearch飛起來!百億級實時查詢優化實戰
    面對這樣一個數據量級的需求,我們的數據如何存儲和實現實時查詢將是一個嚴峻的挑戰。經過對 Elasticsearch 多方調研和超過幾百億條數據的插入和聚合查詢的驗證之後,我們總結出以下幾種能夠有效提升性能和解決這一問題的方案:本文所使用的 Elasticsearch 版本為 5.3.3。
  • 深入淺出 spring-data-elasticsearch 之 ElasticSearch 架構初探(一)
    在索引中全文搜索,然後會查詢到每個分片,將每個分配的結果進行全局地收集處理,並返回。二、Elasticsearch 工作原理2.1 文檔存儲的路由當索引到一個文檔(如:報價系統),具體的文檔數據(如:報價數據)會存儲到一個分片。具體文檔數據會被切分,並分別存儲在分片 1 或者 分片 2 … 那麼如何確定存在哪個分片呢?
  • Open Distro for Elasticsearch 1.1.0 發布,Elasticsearch 發行版
    Open Distro for Elasticsearch 是 AWS 開源的 Elasticsearch 發行版。可以 GitHub 存儲庫中的發行說明中找到有關增強、bug 修復和更多信息的詳細信息:https://github.com/opendistro-for-elasticsearch新特性此版本中提供一些新的插件,以便集成到開發者的應用程式中,雖然這些插件屬於 alpha 版本。
  • 這篇實戰攻略,帶你輕鬆入門Elastic search
    什麼是Elasticsearch,為什麼要使用它?Elasticsearch 是一個分布式、RESTful 風格的搜索和數據分析引擎。它基於Lunece實現,使用java語言編寫。Lunece是一個優秀的搜尋引擎庫,但它使用起來非常複雜。
  • Elasticsearch實現原理分析
    在elasticsearch.yml中,將nodes.master屬性設置為true(默認),可以將節點配置為有資格成為主節點的節點。對於大型生產集群,建議擁有一個專用主節點來控制集群,並且不服務任何用戶請求。數據節點用來保存數據和倒排索引。默認情況下,每個節點都配置為一個data節點,並且在elasticsearch.yml中將屬性node.data設置為true。
  • Hadoop 讀寫 Elasticsearch
    使用Hadoop操作Elasticsearch的方法,關鍵是指定InputFormatClass : EsInputFormat.classOutputFormatClass: EsOutputFormat.class,這個好像是ES官方提供的集成包。網上查了相關資料貌似挺少的。
  • 全文搜尋引擎ElasticSearch入門教程
    $ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.1.zip  $ unzip elasticsearch-5.5.1.zip  $ cd elasticsearch-5.5.1/  接著,進入解壓後的目錄,運行下面的命令,啟動 Elastic。
  • elasticsearch 八、重要的配置更改
    主要是對elastic中Important Configuration Changes這一節的翻譯吧,譯的不好,所以關鍵英文原文也會帶一些吧。為什麼選這一節,因為真的是重要吧,剛好今天有人問了個配置的問題,所以整理下。也供自己以後參考吧,不經常用到時間久了,經常要重頭來過也挺煩的。。
  • 《Elasticsearch 權威指南》中文版
    在幾十位社區同學的共同努力下,《Elasticsearch 權威指南》的翻譯工作接近尾聲,在線訪問連結如下:
  • Elasticsearch Analyzer
    接下來,更新index template,指定index階段和search階段均使用該自定義分析器對moduleName field進行處理。最後,模糊匹配使用match_phrase進行查詢即可。注意:對於存量日誌索引,上述實現不會生效,必須要刪除。因為Elasticsearch不支持更新存量索引mapping。
  • ElasticSearch 億級數據檢索案例實戰!
    改進版本目標:數據能跨月查詢,並且支持1年以上的歷史數據查詢與導出。按條件的數據查詢秒級返回。三、elasticsearch檢索原理3.1 關於ES和Lucene基礎結構談到優化必須能了解組件的基本原理,才容易找到瓶頸所在,以免走多種彎路,先從ES的基礎結構說起(如下圖):一些基本概念:
  • ElasticSearch 索引 VS MySQL 索引
    前言這段時間在維護產品的搜索功能,每次在管理臺看到 elasticsearch 這麼高效的查詢效率我都很好奇他是如何做到的。同時由於數據也是有序的,所以自然也能支持區間查詢;這麼看來有序數組適合用做索引咯?自然是不行,它有另一個重大問題;假設我們插入了 id=2.5 的數據,就得同時將後續的所有數據都移動一位,這個寫入效率就會變得非常低。
  • 基於Ansj 的 elasticsearch 2.3.1 中文分詞插件
    前言這是一個elasticsearch的中文分詞插件,基於Ansj中文分詞。發起者Onni大神。
  • Elasticsearch官方文檔離線訪問實操指南
    步驟3:輸入elasticsearch 搜索下載官方文檔到本地。你上面這麼操作的前提,不是得聯網下載嗎?其實2步驟有了,拿走安裝包、拷貝離線文檔,就能搞定了。上連結——我做成了綠色包。step1:安裝zeal-0.6.1-windows-x64.msiStep2:將 Elasticsearch 離線文檔 docs.zip 解壓到指定路徑,如:D:\test\docs.step3:Edit -> Preferences -> Docset storage -> Directory -> 設置 elasticsearch離線文檔的路徑(見step2 設置
  • 2021-Java後端工程師面試指南-(Elasticsearch)
    Cluster(集群)集群包含多個節點,每個節點屬於哪個集群是通過一個配置(集群名稱,默認是elasticsearch)來決定的,對於中小型應用來說,剛開始一個集群就一個節點很正常Node(節點)集群中的一個節點,節點也有一個名稱(默認是隨機分配的),節點名稱很重要(在執行運維管理操作的時候),默認節點會去加入一個名稱為
  • Elasticsearch高階使用方法有哪些?
    布爾組合查詢、範圍查詢…等多種高級查詢。以及非常重要的聚合查詢,其兩種常用類型:桶和度量的說明與使用。昨天學了match匹配和term匹配,這是兩種最基礎也很重要的查詢方式,使用起來也簡單。也就是可以通過excludes指定不想要顯示的欄位,很好理解二、高階查詢1布爾組合查詢關鍵字是bool,它本身並不是一種查詢方式,而是將查詢方式通過邏輯運算組合起來了。
  • elasticsearch Discovery 發現模塊學習
    一、基於單播的方式發現可以在 elasticsearch.yml 配置文件中使用discovery.zen.ping.unicast.hosts靜態設置設置主機列表。discovery.zen.ping.unicast.hosts: ["host1", "host2"]具體的值是一個主機數組或逗號分隔的字符串。