別再說你不會 ElasticSearch 調優了,都給你整理好了

 

 來源:http://tinyurl.com/y4gnzbjehtml

  1. 第一部分:調優索引速度java

  2. 第二部分-調優搜索速度node

  3. 第三部分:通用的一些建議算法


英文原文:https://www.elastic.co/guide/en/elasticsearch/reference/current/how-to.html數據庫

ES發佈時帶有的默認值,可爲es的開箱即用帶來很好的體驗。全文搜索、高亮、聚合、索引文檔 等功能無需用戶修改便可使用,當你更清楚的知道你想如何使用es後,你能夠做不少的優化以提升你的用例的性能,下面的內容告訴你 你應該/不該該 修改哪些配置express

第一部分:調優索引速度

(https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html)緩存

  1. 使用批量請求批量請求將產生比單文檔索引請求好得多的性能。數據結構

爲了知道批量請求的最佳大小,您應該在具備單個分片的單個節點上運行基準測試。首先嚐試索引100個文件,而後是200,而後是400,等等。當索引速度開始穩定時,您知道您達到了數據批量請求的最佳大小。在配合的狀況下,最好在太少而不是太多文件的方向上犯錯。請注意,若是羣集請求太大,可能會使羣集受到內存壓力,所以建議避免超出每一個請求幾十兆字節,即便較大的請求看起來效果更好。多線程

  1. 發送端使用多worker/多線程向es發送數據 發送批量請求的單個線程不太可能將Elasticsearch羣集的索引容量最大化。爲了使用集羣的全部資源,您應該從多個線程或進程發送數據。除了更好地利用集羣的資源,這應該有助於下降每一個fsync的成本。app

請確保注意TOO_MANY_REQUESTS(429)響應代碼(Java客戶端的EsRejectedExecutionException),這是Elasticsearch告訴您沒法跟上當前索引速率的方式。發生這種狀況時,應該再次嘗試暫停索引,理想狀況下使用隨機指數回退。

與批量調整大小請求相似,只有測試才能肯定最佳的worker數量。這能夠經過逐漸增長工做者數量來測試,直到集羣上的I / O或CPU飽和。

  1. 調大 refresh interval 默認的index.refresh_interval是1s,這迫使Elasticsearch每秒建立一個新的分段。增長這個價值(好比說30s)將容許更大的部分flush並減小將來的合併壓力。

  2. 加載大量數據時禁用refresh和replicas 若是您須要一次加載大量數據,則應該將index.refresh_interval設置爲-1並將index.number_of_replicas設置爲0來禁用刷新。這會暫時使您的索引處於危險之中,由於任何分片的丟失都將致使數據 丟失,可是同時索引將會更快,由於文檔只被索引一次。初始加載完成後,您能夠將index.refresh_interval和index.number_of_replicas設置回其原始值。

  3. 設置參數,禁止OS將es進程swap出去 您應該確保操做系統不會swapping out the java進程,經過禁止swap (https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration-memory.html)

  4. 爲filesystem cache分配一半的物理內存 文件系統緩存將用於緩衝I / O操做。您應該確保將運行Elasticsearch的計算機的內存至少減小到文件系統緩存的一半。

  5. 使用自動生成的id(auto-generated ids) 索引具備顯式id的文檔時,Elasticsearch須要檢查具備相同id的文檔是否已經存在於相同的分片中,這是昂貴的操做,而且隨着索引增加而變得更加昂貴。經過使用自動生成的ID,Elasticsearch能夠跳過這個檢查,這使索引更快。

  6. 買更好的硬件 搜索通常是I/O 密集的,此時,你須要 a.爲filesystem cache分配更多的內存 b.使用SSD硬盤 c.使用local storage(不要使用NFS、SMB 等remote filesystem) d.亞馬遜的 彈性塊存儲(Elastic Block Storage)也是極好的,固然,和local storage比起來,它仍是要慢點 若是你的搜索是 CPU-密集的,買好的CPU吧

  7. 加大 indexing buffer size 若是你的節點只作大量的索引,確保index.memory.index_buffer_size足夠大,每一個分區最多能夠提供512 MB的索引緩衝區,並且索引的性能一般不會提升。Elasticsearch採用該設置(java堆的一個百分比或絕對字節大小),並將其用做全部活動分片的共享緩衝區。很是活躍的碎片天然會使用這個緩衝區,而不是執行輕量級索引的碎片。

默認值是10%,一般不少:例如,若是你給JVM 10GB的內存,它會給索引緩衝區1GB,這足以承載兩個索引很重的分片。

  1. 禁用_field_names字段 _field_names字段引入了一些索引時間開銷,因此若是您不須要運行存在查詢,您可能須要禁用它。(_field_names:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-field-names-field.html)

  2. 剩下的,再去看看 「調優 磁盤使用」吧 (https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-disk-usage.html)中有許多磁盤使用策略也提升了索引速度。

第二部分-調優搜索速度

  1. filesystem cache越大越好 爲了使得搜索速度更快, es嚴重依賴filesystem cache 通常來講,須要至少一半的 可用內存 做爲filesystem cache,這樣es能夠在物理內存中 保有 索引的熱點區域(hot regions of the index)

  2. 用更好的硬件 搜索通常是I/O bound的,此時,你須要 a.爲filesystem cache分配更多的內存 b.使用SSD硬盤 c.使用local storage(不要使用NFS、SMB 等remote filesystem) d.亞馬遜的 彈性塊存儲(Elastic Block Storage)也是極好的,固然,和local storage比起來,它仍是要慢點 若是你的搜索是 CPU-bound,買好的CPU吧

  3. 文檔模型(document modeling) 文檔須要使用合適的類型,從而使得 search-time operations 消耗更少的資源。咋做呢?答:避免 join操做。具體是指 a.nested 會使得查詢慢 好幾倍 b.parent-child關係 更是使得查詢慢幾百倍 若是 無需join 能解決問題,則查詢速度會快不少

  4. 預索引 數據 根據「搜索數據最經常使用的方式」來最優化索引數據的方式 舉個例子:全部文檔都有price字段,大部分query 在 fixed ranges 上運行 range aggregation。你能夠把給定範圍的數據 預先索引下。而後,使用 terms aggregation

  5. Mappings(能用 keyword 最好了) 數字類型的數據,並不意味着必定非得使用numeric類型的字段。通常來講,存儲標識符的 字段(書號ISBN、或來自數據庫的 標識一條記錄的 數字),使用keyword更好(integer,long 很差哦,親) 6.避免運行腳本 通常來講,腳本應該避免。若是他們是絕對須要的,你應該使用painless和expressions引擎。

  6. 搜索rounded 日期 日期字段上使用now,通常來講不會被緩存。但,rounded date則能夠利用上query cache rounded到分鐘等

  7. 強制merge只讀的index 只讀的index能夠從「merge成 一個單獨的 大segment」中收益

  8. 預熱 全局序數(global ordinals) 全局序數 用於 在 keyword字段上 運行 terms aggregations es不知道 哪些fields 將 用於/不用於 term aggregation,所以 全局序數 在須要時才加載進內存 但,能夠在mapping type上,定義 eager_global_ordinals==true,這樣,refresh時就會加載 全局序數

  9. 預熱 filesystem cache 機器重啓時,filesystem cache就被清空。OS將index的熱點區域(hot regions of the index)加載進filesystem cache是須要花費一段時間的。設置 index.store.preload 能夠告知OS 這些文件須要提前加載進入內存

11使用索引排序來加速鏈接 索引排序對於以較慢的索引爲代價來加快鏈接速度很是有用。在索引分類文檔中閱讀更多關於它的信息。

12.使用preference來優化高速緩存利用率 有多個緩存能夠幫助提升搜索性能,例如文件系統緩存,請求緩存或查詢緩存。然而,全部這些緩存都維護在節點級別,這意味着若是連續運行兩次相同的請求,則有一個或多個副本,並使用循環(默認路由算法),那麼這兩個請求將轉到不一樣的分片副本,阻止節點級別的緩存幫助。

因爲搜索應用程序的用戶一個接一個地運行相似的請求是常見的,例如爲了分析索引的較窄的子集,使用標識當前用戶或會話的優選值能夠幫助優化高速緩存的使用。

13.副本可能有助於吞吐量,但不會一直存在 除了提升彈性外,副本能夠幫助提升吞吐量。例如,若是您有單個分片索引和三個節點,則須要將副本數設置爲2,以便共有3個分片副本,以便使用全部節點。

如今假設你有一個2-shards索引和兩個節點。在一種狀況下,副本的數量是0,這意味着每一個節點擁有一個分片。在第二種狀況下,副本的數量是1,這意味着每一個節點都有兩個碎片。哪一個設置在搜索性能方面表現最好?一般狀況下,每一個節點的碎片數少的設置將會更好。緣由在於它將可用文件系統緩存的份額提升到了每一個碎片,而文件系統緩存多是Elasticsearch的1號性能因子。同時,要注意,沒有副本的設置在發生單個節點故障的狀況下會出現故障,所以在吞吐量和可用性之間進行權衡。

那麼複製品的數量是多少?若是您有一個具備num_nodes節點的羣集,那麼num_primaries總共是主分片,若是您但願可以一次處理max_failures節點故障,那麼正確的副本數是max(max_failures,ceil(num_nodes / num_primaries) - 1)。

14.打開自適應副本選擇 當存在多個數據副本時,elasticsearch可使用一組稱爲自適應副本選擇的標準,根據包含分片的每一個副本的節點的響應時間,服務時間和隊列大小來選擇數據的最佳副本。這能夠提升查詢吞吐量並減小搜索量大的應用程序的延遲。

第三部分:通用的一些建議

一、不要 返回大的結果集 es設計來做爲搜索引擎,它很是擅長返回匹配query的top n文檔。但,如「返回知足某個query的 全部文檔」等數據庫領域的工做,並非es最擅長的領域。若是你確實須要返回全部文檔,你可使用Scroll API

二、避免 大的doc。即,單個doc 小了 會更好 given that(考慮到) http.max_context_length默認==100MB,es拒絕索引操做100MB的文檔。固然你能夠提升這個限制,但,Lucene自己也有限制的,其爲2GB 即便不考慮上面的限制,大的doc 會給 network/memory/disk帶來更大的壓力;a.任何搜索請求,都須要獲取 _id 字段,因爲filesystem cache工做方式。即便它不請求 _source字段,獲取大doc _id 字段消耗更大 b.索引大doc時消耗內存會是 doc自己大小 的好幾倍 c.大doc的 proximity search, highlighting 也更加昂貴。它們的消耗直接取決於doc自己的大小

三、避免 稀疏 a.不相關數據 不要 放入同一個索引 b.通常化文檔結構(Normalize document structures) c.避免類型 d.在 稀疏 字段上,禁用 norms & doc_values 屬性

稀疏爲何很差?Lucene背後的數據結構 更擅長處理 緊湊的數據 text類型的字段,norms默認開啓;numerics, date, ip, keyword,doc_values默認開啓 Lucene內部使用 integer的doc_id來標識文檔 和 內部API交互。舉個例子:使用match查詢時生成doc_id的迭代器,這些doc_id被用於獲取它們的norm,以便計算score。當前的實現是每一個doc中保留一個byte用於存儲norm值。獲取norm值其實就是讀取doc_id位置處的一個字節 這很是高效,Lucene經過此值能夠快速訪問任何一個doc的norm值;但,給定一個doc,即便某個field沒有值,仍須要爲此doc的此field保留一個字節 doc_values也有一樣的問題。2.0以前的fielddata被如今的doc_values所替代了。稀疏性 最明顯的影響是 對存儲的需求(任何doc的每一個field,都須要一個byte);可是呢,稀疏性 對 索引速度和查詢速度 也是有影響的,由於:即便doc並無某些字段值,但,索引時,依然須要寫這些字段,查詢時,須要skip這些字段的值 某個索引中擁有少許稀疏字段,這徹底沒有問題。但,這不該該成爲常態 稀疏性影響最大的是 norms&doc_values ,但,倒排索引(用於索引 text以及keyword字段),二維點(用於索引geo_point字段)也會受到較小的影響

如何避免稀疏呢?一、不相關數據 不要 放入同一個索引 給個tip:索引小(即:doc的個數較少),則,primary shard也要少 二、通常化文檔結構(Normalize document structures) 三、避免類型(Avoid mapping type) 同一個index,最好就一個mapping type 在同一個index下面,使用不一樣的mapping type來存儲數據,聽起來不錯,但,其實很差。given that(考慮到)每個mapping type會把數據存入 同一個index,所以,多個不一樣mapping type,各個的field又互不相同,這一樣帶來了稀疏性 問題 四、在 稀疏 字段上,禁用 norms & doc_values 屬性 a.norms用於計算score,無需score,則能夠禁用它(全部filtering字段,均可以禁用norms) b.doc_vlaues用於sort&aggregations,無需這兩個,則能夠禁用它 可是,不要輕率的作出決定,由於 norms&doc_values沒法修改。只能reindex

祕訣1:混合 精確查詢和提取詞幹(mixing exact search with stemming) 對於搜索應用,提取詞幹(stemming)都是必須的。例如:查詢 skiing時,ski和skis都是指望的結果 但,若是用戶就是要查詢skiing呢?解決方法是:使用multi-field。同一分內容,以兩種不一樣的方式來索引存儲 query.simple_query_string.quote_field_suffix,居然是 查詢徹底匹配的

祕訣2:獲取一致性的打分 score不能重現 同一個請求,連續運行2次,但,兩次返回的文檔順序不一致。這是至關壞的用戶體驗

若是存在 replica,則就可能發生這種事,這是由於:search時,replication group中的shard是按round-robin方式來選擇的,所以兩次運行一樣的請求,請求若是打到 replication group中的不一樣shard,則兩次得分就可能不一致

那問題來了,「你不是成天說 primary和replica是in-sync的,是徹底一致的」嘛,爲啥打到「in-sync的,徹底一致的shard」卻算出不一樣的得分?

緣由就是標註爲「已刪除」的文檔。如你所知,doc更新或刪除時,舊doc並不刪除,而是標註爲「已刪除」,只有等到 舊doc所在的segment被merge時,「已刪除」的doc纔會從磁盤刪除掉

索引統計(index statistic)是打分時很是重要的一部分,但,因爲 deleted doc 的存在,在同一個shard的不一樣copy(即:各個replica)上 計算出的 索引統計 並不一致

我的理解:a. 所謂 索引統計 應該就是df,即 doc_freq b. 索引統計 是基於shard來計算的

  1. 搜索時,「已刪除」的doc 固然是 永遠不會 出如今 結果集中的

  2. 索引統計時,for practical reasons,「已刪除」doc 依然是統計在內的

假設,shard A0 剛剛完成了一次較大的segment merge,而後移除了不少「已刪除」doc,shard A1 還沒有執行 segment merge,所以 A1 依然存在那些「已刪除」doc

因而:兩次請求打到 A0 和 A1 時,二者的 索引統計 是顯著不一樣的

如何規避 score不能重現 的問題?使用 preference 查詢參數 發出搜索請求時候,用 標識字符串 來標識用戶,將 標識字符串 做爲查詢請求的preference參數。這確保屢次執行同一個請求時候,給定用戶的請求老是達到同一個shard,所以得分會更爲一致(固然,即便同一個shard,兩次請求 跨了 segment merge,則依然會得分不一致) 這個方式還有另一個優勢,當兩個doc得分一致時,則默認按着doc的 內部Lucene doc id 來排序(注意:這並非es中的 _id 或 _uid)。可是呢,shard的不一樣copy間,同一個doc的 內部Lucene doc id 可能並不相同。所以,若是老是達到同一個shard,則,具備相同得分的兩個doc,其順序是一致的

score錯了 score錯了(Relevancy looks wrong) 若是你發現

  1. 具備相同內容的文檔,其得分不一樣

  2. 徹底匹配 的查詢 並無排在第一位 這可能都是由 sharding 引發的

  3. 默認狀況下,搜索文檔時,每一個shard本身計算出本身的得分。

  4. 索引統計 又是打分時一個很是重要的因素。

若是每一個shard的 索引統計類似,則 搜索工做的很好 文檔是平分到每一個primary shard的,所以 索引統計 會很是類似,打分也會按着預期工做。但,萬事都有個可是:

  1. 索引時使用了 routing(文檔不能平分到每一個primary shard 啦)

  2. 查詢多個索引

  3. 索引中文檔的個數 很是少 這會致使:參與查詢的各個shard,各自的 索引統計 並不類似(而,索引統計對 最終的得分 又影響巨大),因而 打分出錯了(relevancy looks wrong)

那,如何繞過 score錯了(Relevancy looks wrong)?

若是數據集較小,則,只使用一個primary shard(es默認是5個),這樣兩次查詢 索引統計 不會變化,於是得分也就一致啦 另外一種方式是,將search_type設置爲:dfs_query_then_fetech(默認是query_then_fetch) dfs_query_then_fetch的做用是

  1. 向 全部相關shard 發出請求,要求 全部相關shard 返回針對當前查詢的 索引統計

  2. 而後,coordinating node 將 merge這些 索引統計,從而獲得 merged statistics

  3. coordinating node 要求 全部相關shard 執行 query phase,因而 發出請求,這時,也帶上 merged statistics。這樣,執行query的shard 將使用 全局的索引統計 大部分狀況下,要求 全部相關shard 返回針對當前查詢的 索引統計,這是很是cheap的。但,若是查詢中 包含 很是大量的 字段/term查詢,或者有 fuzzy查詢,此時,獲取 索引統計 可能並不cheap,由於 爲了獲得 索引統計 可能 term dictionary 中 全部的term都須要被查詢一遍

相關文章
相關標籤/搜索