ES官方調優指南翻譯
Table of Contents
- 第一部分:調優索引速度
- 第二部分-調優搜索速度
- 第三部分:通用的一些建議
原文:https://www.elastic.co/guide/en/elasticsearch/reference/current/how-to.html
轉載請註明出處:http://wangnan.tech或簡書:http://www.jianshu.com/u/244399b1d776
ES發布時帶有的默認值,可為es的開箱即用帶來很好的體驗。全文搜索、高亮、聚合、索引文檔 等功能無需用戶修改即可使用,當你更清楚的知道你想如何使用es後,你可以作很多的優化以提高你的用例的性能,下面的內容告訴你 你應該/不應該 修改哪些配置
第一部分:調優索引速度
(https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-indexing-speed.html)
- 使用批量請求批量請求將產生比單文檔索引請求好得多的性能。
為了知道批量請求的最佳大小,您應該在具有單個分片的單個節點上運行基準測試。 首先嘗試索引100個文件,然後是200,然後是400,等等。 當索引速度開始穩定時,您知道您達到了數據批量請求的最佳大小。 在配合的情況下,最好在太少而不是太多文件的方向上犯錯。 請注意,如果群集請求太大,可能會使群集受到內存壓力,因此建議避免超出每個請求幾十兆位元組,即使較大的請求看起來效果更好。
- 發送端使用多worker/多線程向es發送數據發送批量請求的單個線程不太可能將Elasticsearch群集的索引容量最大化。 為了使用集群的所有資源,您應該從多個線程或進程發送數據。 除了更好地利用集群的資源,這應該有助於降低每個fsync的成本。
請確保注意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 cacherounded到分鐘等
- 強制merge只讀的index只讀的index可以從「merge成 一個單獨的 大segment」中收益
- 預熱 全局序數(global ordinals)全局序數 用於 在 keyword欄位上 運行 terms aggregationses不知道 哪些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可以使用一組稱為自適應副本選擇的標準,根據包含分片的每個副本的節點的響應時間,服務時間和隊列大小來選擇數據的最佳副本。這可以提高查詢吞吐量並減少搜索量大的應用程序的延遲。第三部分:通用的一些建議
1、不要 返回大的結果集
es設計來作為搜索引擎,它非常擅長返回匹配query的top n文檔。但,如「返回滿足某個query的 所有文檔」等資料庫領域的工作,並不是es最擅長的領域。如果你確實需要返回所有文檔,你可以使用Scroll API
2、避免 大的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本身的大小3、避免 稀疏
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欄位)也會受到較小的影響如何避免稀疏呢?
1、不相關數據 不要 放入同一個索引給個tip:索引小(即:doc的個數較少),則,primary shard也要少2、一般化文檔結構(Normalize document structures)3、避免類型(Avoid mapping type)同一個index,最好就一個mapping type在同一個index下面,使用不同的mapping type來存儲數據,聽起來不錯,但,其實不好。given that(考慮到)每一個mapping type會把數據存入 同一個index,因此,多個不同mapping type,各個的field又互不相同,這同樣帶來了稀疏性 問題4、在 稀疏 欄位上,禁用 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_freqb. 索引統計 是基於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都需要被查詢一遍
推薦閱讀:
※ElasticSearch代碼和知識點總結(一)
※使用ElasticSearch搭建日誌系統
※elastic search集群配置
※elasticsearch 初學入門
※ElasticSearch優化系列三:索引過程
TAG:Elasticsearch |