來源:http://tinyurl.com/y4gnzbjehtml
第一部分:調優索引速度java
第二部分-調優搜索速度node
第三部分:通用的一些建議算法
英文原文: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)緩存
使用批量請求批量請求將產生比單文檔索引請求好得多的性能。數據結構
爲了知道批量請求的最佳大小,您應該在具備單個分片的單個節點上運行基準測試。首先嚐試索引100個文件,而後是200,而後是400,等等。當索引速度開始穩定時,您知道您達到了數據批量請求的最佳大小。在配合的狀況下,最好在太少而不是太多文件的方向上犯錯。請注意,若是羣集請求太大,可能會使羣集受到內存壓力,所以建議避免超出每一個請求幾十兆字節,即便較大的請求看起來效果更好。多線程
發送端使用多worker/多線程向es發送數據 發送批量請求的單個線程不太可能將Elasticsearch羣集的索引容量最大化。爲了使用集羣的全部資源,您應該從多個線程或進程發送數據。除了更好地利用集羣的資源,這應該有助於下降每一個fsync的成本。app
請確保注意TOO_MANY_REQUESTS(429)響應代碼(Java客戶端的EsRejectedExecutionException),這是Elasticsearch告訴您沒法跟上當前索引速率的方式。發生這種狀況時,應該再次嘗試暫停索引,理想狀況下使用隨機指數回退。
與批量調整大小請求相似,只有測試才能肯定最佳的worker數量。這能夠經過逐漸增長工做者數量來測試,直到集羣上的I / O或CPU飽和。
調大 refresh interval 默認的index.refresh_interval是1s,這迫使Elasticsearch每秒建立一個新的分段。增長這個價值(好比說30s)將容許更大的部分flush並減小將來的合併壓力。
加載大量數據時禁用refresh和replicas 若是您須要一次加載大量數據,則應該將index.refresh_interval設置爲-1並將index.number_of_replicas設置爲0來禁用刷新。這會暫時使您的索引處於危險之中,由於任何分片的丟失都將致使數據 丟失,可是同時索引將會更快,由於文檔只被索引一次。初始加載完成後,您能夠將index.refresh_interval和index.number_of_replicas設置回其原始值。
設置參數,禁止OS將es進程swap出去 您應該確保操做系統不會swapping out the java進程,經過禁止swap (https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration-memory.html)
爲filesystem cache分配一半的物理內存 文件系統緩存將用於緩衝I / O操做。您應該確保將運行Elasticsearch的計算機的內存至少減小到文件系統緩存的一半。
使用自動生成的id(auto-generated ids) 索引具備顯式id的文檔時,Elasticsearch須要檢查具備相同id的文檔是否已經存在於相同的分片中,這是昂貴的操做,而且隨着索引增加而變得更加昂貴。經過使用自動生成的ID,Elasticsearch能夠跳過這個檢查,這使索引更快。
買更好的硬件 搜索通常是I/O 密集的,此時,你須要 a.爲filesystem cache分配更多的內存 b.使用SSD硬盤 c.使用local storage(不要使用NFS、SMB 等remote filesystem) d.亞馬遜的 彈性塊存儲(Elastic Block Storage)也是極好的,固然,和local storage比起來,它仍是要慢點 若是你的搜索是 CPU-密集的,買好的CPU吧
加大 indexing buffer size 若是你的節點只作大量的索引,確保index.memory.index_buffer_size足夠大,每一個分區最多能夠提供512 MB的索引緩衝區,並且索引的性能一般不會提升。Elasticsearch採用該設置(java堆的一個百分比或絕對字節大小),並將其用做全部活動分片的共享緩衝區。很是活躍的碎片天然會使用這個緩衝區,而不是執行輕量級索引的碎片。
默認值是10%,一般不少:例如,若是你給JVM 10GB的內存,它會給索引緩衝區1GB,這足以承載兩個索引很重的分片。
禁用_field_names字段 _field_names字段引入了一些索引時間開銷,因此若是您不須要運行存在查詢,您可能須要禁用它。(_field_names:https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-field-names-field.html)
剩下的,再去看看 「調優 磁盤使用」吧 (https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-disk-usage.html)中有許多磁盤使用策略也提升了索引速度。
filesystem cache越大越好 爲了使得搜索速度更快, es嚴重依賴filesystem cache 通常來講,須要至少一半的 可用內存 做爲filesystem cache,這樣es能夠在物理內存中 保有 索引的熱點區域(hot regions of the index)
用更好的硬件 搜索通常是I/O bound的,此時,你須要 a.爲filesystem cache分配更多的內存 b.使用SSD硬盤 c.使用local storage(不要使用NFS、SMB 等remote filesystem) d.亞馬遜的 彈性塊存儲(Elastic Block Storage)也是極好的,固然,和local storage比起來,它仍是要慢點 若是你的搜索是 CPU-bound,買好的CPU吧
文檔模型(document modeling) 文檔須要使用合適的類型,從而使得 search-time operations 消耗更少的資源。咋做呢?答:避免 join操做。具體是指 a.nested 會使得查詢慢 好幾倍 b.parent-child關係 更是使得查詢慢幾百倍 若是 無需join 能解決問題,則查詢速度會快不少
預索引 數據 根據「搜索數據最經常使用的方式」來最優化索引數據的方式 舉個例子:全部文檔都有price字段,大部分query 在 fixed ranges 上運行 range aggregation。你能夠把給定範圍的數據 預先索引下。而後,使用 terms aggregation
Mappings(能用 keyword 最好了) 數字類型的數據,並不意味着必定非得使用numeric類型的字段。通常來講,存儲標識符的 字段(書號ISBN、或來自數據庫的 標識一條記錄的 數字),使用keyword更好(integer,long 很差哦,親) 6.避免運行腳本 通常來講,腳本應該避免。若是他們是絕對須要的,你應該使用painless和expressions引擎。
搜索rounded 日期 日期字段上使用now,通常來講不會被緩存。但,rounded date則能夠利用上query cache rounded到分鐘等
強制merge只讀的index 只讀的index能夠從「merge成 一個單獨的 大segment」中收益
預熱 全局序數(global ordinals) 全局序數 用於 在 keyword字段上 運行 terms aggregations es不知道 哪些fields 將 用於/不用於 term aggregation,所以 全局序數 在須要時才加載進內存 但,能夠在mapping type上,定義 eager_global_ordinals==true,這樣,refresh時就會加載 全局序數
預熱 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來計算的
搜索時,「已刪除」的doc 固然是 永遠不會 出如今 結果集中的
索引統計時,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) 若是你發現
具備相同內容的文檔,其得分不一樣
徹底匹配 的查詢 並無排在第一位 這可能都是由 sharding 引發的
默認狀況下,搜索文檔時,每一個shard本身計算出本身的得分。
索引統計 又是打分時一個很是重要的因素。
若是每一個shard的 索引統計類似,則 搜索工做的很好 文檔是平分到每一個primary shard的,所以 索引統計 會很是類似,打分也會按着預期工做。但,萬事都有個可是:
索引時使用了 routing(文檔不能平分到每一個primary shard 啦)
查詢多個索引
索引中文檔的個數 很是少 這會致使:參與查詢的各個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的做用是
向 全部相關shard 發出請求,要求 全部相關shard 返回針對當前查詢的 索引統計
而後,coordinating node 將 merge這些 索引統計,從而獲得 merged statistics
coordinating node 要求 全部相關shard 執行 query phase,因而 發出請求,這時,也帶上 merged statistics。這樣,執行query的shard 將使用 全局的索引統計 大部分狀況下,要求 全部相關shard 返回針對當前查詢的 索引統計,這是很是cheap的。但,若是查詢中 包含 很是大量的 字段/term查詢,或者有 fuzzy查詢,此時,獲取 索引統計 可能並不cheap,由於 爲了獲得 索引統計 可能 term dictionary 中 全部的term都須要被查詢一遍