1. 程式人生 > 實用技巧 >一次看完28個關於ES的效能調優技巧,很贊,值得收藏!

一次看完28個關於ES的效能調優技巧,很贊,值得收藏!

因為總是看到很多同學在說Elasticsearch效能不夠好、叢集不夠穩定,詢問關於Elasticsearch的調優,但是每次都是一個個點的單獨講,很多時候都是case by case的解答,本文簡單梳理下日常的Elasticsearch使用調優,以下僅為自己日常經驗之談,如有疏漏,還請大家幫忙指正。

一、配置檔案調優

elasticsearch.yml

1、記憶體鎖定

bootstrap.memory_lock:true允許JVM鎖住記憶體,禁止作業系統交換出去。

2、zen.discovery

Elasticsearch預設被配置為使用單播發現,以防止節點無意中加入叢集。組播發現應該永遠不被使用在生產環境了,否則你得到的結果就是一個節點意外的加入到了你的生產環境,僅僅是因為他們收到了一個錯誤的組播訊號。

ES是一個P2P型別的分散式系統,使用gossip協議,叢集的任意請求都可以傳送到叢集的任一節點,然後ES內部會找到需要轉發的節點,並且與之進行通訊。

在ES1.x的版本,ES預設是開啟組播,啟動ES之後,可以快速將區域網內叢集名稱,預設埠的相同例項加入到一個大的叢集,後續再ES2.x之後,都調整成了單播,避免安全問題和網路風暴。

單播discovery.zen.ping.unicast.hosts,建議寫入叢集內所有的節點及埠,如果新例項加入叢集,新例項只需要寫入當前叢集的例項,即可自動加入到當前叢集,之後再處理原例項的配置即可,新例項加入叢集,不需要重啟原有例項;

節點zen相關配置:discovery.zen.ping_timeout:判斷master選舉過程中,發現其他node存活的超時設定,主要影響選舉的耗時,引數僅在加入或者選舉 master 主節點的時候才起作用discovery.zen.join_timeout:節點確定加入到叢集中,向主節點發送加入請求的超時時間,預設為3sdiscovery.zen.minimum_master_nodes:參與master選舉的最小節點數,當叢集能夠被選為master的節點數量小於最小數量時,叢集將無法正常選舉。

3、故障檢測(fault detection)

兩種情況下會進行故障檢測:

  • 第一種是由master向叢集的所有其他節點發起ping,驗證節點是否處於活動狀態;
  • 第二種是:叢集每個節點向master發起ping,判斷master是否存活,是否需要發起選舉。故障檢測需要配置以下設定使用 形如:discovery.zen.fd.ping_interval節點被ping的頻率,預設為1s。discovery.zen.fd.ping_timeout 等待ping響應的時間,預設為 30s,執行的叢集中,master 檢測所有節點,以及節點檢測 master 是否正常。discovery.zen.fd.ping_retries ping失敗/超時多少導致節點被視為失敗,預設為3。

https://www.elastic.co/guide/en/elasticsearch/reference/6.x/modules-discovery-zen.html

4、佇列數量

不建議盲目加大ES的佇列數量,如果是偶發的因為資料突增,導致佇列阻塞,加大佇列size可以使用記憶體來快取資料;如果是持續性的資料阻塞在佇列,加大佇列size除了加大記憶體佔用,並不能有效提高資料寫入速率,反而可能加大ES宕機時候,在記憶體中可能丟失的上資料量。

哪些情況下,加大佇列size呢?GET /_cat/thread_pool,觀察api中返回的queue和rejected,如果確實存在佇列拒絕或者是持續的queue,可以酌情調整佇列size。

https://www.elastic.co/guide/en/elasticsearch/reference/6.x/modules-threadpool.html

5、記憶體使用

設定indices的記憶體熔斷相關引數,根據實際情況進行調整,防止寫入或查詢壓力過高導致OOM:

  • indices.breaker.total.limit:50%,叢集級別的斷路器,預設為jvm堆的70%;
  • indices.breaker.request.limit:10%,單個request的斷路器限制,預設為jvm堆的60%;
  • indices.breaker.fielddata.limit:10%,fielddata breaker限制,預設為jvm堆的60%。

https://www.elastic.co/guide/en/elasticsearch/reference/6.x/circuit-breaker.html

根據實際情況調整查詢佔用cache,避免查詢cache佔用過多的jvm記憶體,引數為靜態的,需要在每個資料節點配置。indices.queries.cache.size: 5%,控制過濾器快取的記憶體大小,預設為10%。接受百分比值,5%或者精確值,例如512mb。

https://www.elastic.co/guide/en/elasticsearch/reference/6.x/query-cache.html

6、建立shard

如果叢集規模較大,可以阻止新建shard時掃描叢集內全部shard的元資料,提升shard分配速度。

cluster.routing.allocation.disk.include_relocations: false,預設為true。

https://www.elastic.co/guide/en/elasticsearch/reference/6.x/disk-allocator.html

二、系統層面調優

1、jdk版本

當前根據官方建議,選擇匹配的jdk版本。

2、jdk記憶體配置

首先,-Xms和-Xmx設定為相同的值,避免在執行過程中再進行記憶體分配,同時,如果系統記憶體小於64G,建議設定略小於機器記憶體的一半,剩餘留給系統使用。

同時,jvm heap建議不要超過32G(不同jdk版本具體的值會略有不同),否則jvm會因為記憶體指標壓縮導致記憶體浪費,詳見:

https://www.elastic.co/guide/cn/elasticsearch/guide/current/heap-sizing.html

3、交換分割槽

關閉交換分割槽,防止記憶體發生交換導致效能下降(部分情況下,寧死勿慢) swapoff -a

4、檔案控制代碼

Lucene 使用了 大量的 檔案。同時,Elasticsearch 在節點和 HTTP 客戶端之間進行通訊也使用了大量的套接字,所有這一切都需要足夠的檔案描述符,預設情況下,linux預設執行單個程序開啟1024個檔案控制代碼,這顯然是不夠的,故需要加大檔案控制代碼數 ulimit -n 65536。

https://www.elastic.co/guide/en/elasticsearch/reference/6.5/setting-system-settings.html

5、mmap

Elasticsearch 對各種檔案混合使用了 NioFs( 注:非阻塞檔案系統)和 MMapFs ( 注:記憶體對映檔案系統)。請確保你配置的最大對映數量,以便有足夠的虛擬記憶體可用於 mmapped 檔案。

這可以暫時設定:sysctl -w vm.max_map_count=262144 或者你可以在 /etc/sysctl.conf 通過修改 vm.max_map_count 永久設定它。

https://www.elastic.co/guide/cn/elasticsearch/guide/current/_file_descriptors_and_mmap.html

6、磁碟

如果你正在使用 SSDs,確保你的系統 I/O 排程程式是配置正確的。當你向硬碟寫資料,I/O 排程程式決定何時把資料實際傳送到硬碟。大多數預設 nix 發行版下的排程程式都叫做 cfq(完全公平佇列)。但它是為旋轉介質優化的:機械硬碟的固有特性意味著它寫入資料到基於物理佈局的硬碟會更高效。這對 SSD 來說是低效的,儘管這裡沒有涉及到機械硬碟。

但是,deadline 或者 noop 應該被使用。deadline 排程程式基於寫入等待時間進行優化, noop 只是一個簡單的 FIFO 佇列。echo noop > /sys/block/sd/queue/scheduler。

7、磁碟掛載

mount -o noatime,data=writeback,barrier=0,nobh /dev/sd* /esdata* 其中,noatime,禁止記錄訪問時間戳;data=writeback,不記錄journal;barrier=0,因為關閉了journal,所以同步關閉barrier;nobh,關閉buffer_head,防止核心影響資料IO。

8、磁碟其他注意事項

使用 RAID 0。條帶化 RAID 會提高磁碟I/O,代價顯然就是當一塊硬碟故障時整個就故障了,不要使用映象或者奇偶校驗 RAID 因為副本已經提供了這個功能。

另外,使用多塊硬碟,並允許 Elasticsearch 通過多個 path.data 目錄配置把資料條帶化分配到它們上面。不要使用遠端掛載的儲存,比如 NFS 或者 SMB/CIFS。這個引入的延遲對效能來說完全是背道而馳的。

三、Elasticsearch使用方式調優

當Elasticsearch本身的配置沒有明顯的問題之後,發現ES使用還是非常慢,這個時候,就需要我們去定位ES本身的問題了,首先祭出定位問題的第一個命令:

1、hot_threads

GET /_nodes/hot_threads&interval=30s

抓取30s的節點上佔用資源的熱執行緒,並通過排查佔用資源最多的TOP執行緒來判斷對應的資源消耗是否正常。一般情況下,bulk,search類的執行緒佔用資源都可能是業務造成的,但是如果是merge執行緒佔用了大量的資源,就應該考慮是不是建立index或者刷磁碟間隔太小,批量寫入size太小造成的。

https://www.elastic.co/guide/en/elasticsearch/reference/6.x/cluster-nodes-hot-threads.html

2、pending_tasks

GET /_cluster/pending_tasks

有一些任務只能由主節點去處理,比如建立一個新的索引或者在叢集中移動分片,由於一個叢集中只能有一個主節點,所以只有這一master節點可以處理叢集級別的元資料變動。

在99.9999%的時間裡,這不會有什麼問題,元資料變動的佇列基本上保持為零。在一些罕見的叢集裡,元資料變動的次數比主節點能處理的還快,這會導致等待中的操作會累積成佇列。

這個時候可以通過pending_tasks api分析當前什麼操作阻塞了ES的佇列,比如,叢集異常時,會有大量的shard在recovery,如果叢集在大量建立新欄位,會出現大量的put_mappings的操作,所以正常情況下,需要禁用動態mapping。

https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-pending.html

3、欄位儲存

當前es主要有doc_values,fielddata,storefield三種類型,大部分情況下,並不需要三種類型都儲存,可根據實際場景進行調整:

  • 當前用得最多的就是doc_values,列儲存,對於不需要進行分詞的欄位,都可以開啟doc_values來進行儲存(且只保留keyword欄位),節約記憶體,當然,開啟doc_values會對查詢效能有一定的影響,但是,這個效能損耗是比較小的,而且是值得的;
  • fielddata構建和管理 100% 在記憶體中,常駐於 JVM 記憶體堆,所以可用於快速查詢,但是這也意味著它本質上是不可擴充套件的,有很多邊緣情況下要提防,如果對於欄位沒有分析需求,可以關閉fielddata;
  • storefield主要用於_source欄位,預設情況下,資料在寫入es的時候,es會將doc資料儲存為_source欄位,查詢時可以通過_source欄位快速獲取doc的原始結構,如果沒有update,reindex等需求,可以將_source欄位disable;
  • _all,ES在6.x以前的版本,預設將寫入的欄位拼接成一個大的字串,並對該欄位進行分詞,用於支援整個doc的全文檢索,在知道doc欄位名稱的情況下,建議關閉掉該欄位,節約儲存空間,也避免不帶欄位key的全文檢索;
  • norms:搜尋時進行評分,日誌場景一般不需要評分,建議關閉。

4、tranlog

Elasticsearch 2.0之後為了保證不丟資料,每次 index、bulk、delete、update 完成的時候,一定觸發重新整理 translog 到磁碟上,才給請求返回 200 OK。這個改變在提高資料安全性的同時當然也降低了一點效能。如果你不在意這點可能性,還是希望效能優先,可以在 index template 裡設定如下引數:

{
    "index.translog.durability": "async"
}

index.translog.sync_interval:

對於一些大容量的偶爾丟失幾秒資料問題也並不嚴重的叢集,使用非同步的 fsync 還是比較有益的。

比如,寫入的資料被快取到記憶體中,再每5秒執行一次 fsync ,預設為5s。小於的值100ms是不允許的。

index.translog.flush_threshold_size:

translog儲存尚未安全儲存在Lucene中的所有操作。雖然這些操作可用於讀取,但如果要關閉並且必須恢復,則需要重新編制索引。

此設定控制這些操作的最大總大小,以防止恢復時間過長。達到設定的最大size後,將發生重新整理,生成新的Lucene提交點,預設為512mb。

5、refresh_interval

執行重新整理操作的頻率,這會使索引的最近更改對搜尋可見,預設為1s,可以設定-1為禁用重新整理,對於寫入速率要求較高的場景,可以適當的加大對應的時長,減小磁碟io和segment的生成。

6、禁止動態mapping

動態mapping的壞處:

  • 造成叢集元資料一直變更,導致叢集不穩定;
  • 可能造成資料型別與實際型別不一致;
  • 對於一些異常欄位或者是掃描類的欄位,也會頻繁的修改mapping,導致業務不可控。

動態mapping配置的可選值及含義如下:

  • true:支援動態擴充套件,新增資料有新的欄位屬性時,自動新增對於的mapping,資料寫入成功;
  • false:不支援動態擴充套件,新增資料有新的欄位屬性時,直接忽略,資料寫入成功 ;
  • strict:不支援動態擴充套件,新增資料有新的欄位時,報錯,資料寫入失敗。

7、批量寫入

批量請求顯然會大大提升寫入速率,且這個速率是可以量化的,官方建議每次批量的資料物理位元組數5-15MB是一個比較不錯的起點,注意這裡說的是物理位元組數大小。

文件計數對批量大小來說不是一個好指標。

比如說,如果你每次批量索引 1000 個文件,記住下面的事實:1000 個 1 KB 大小的文件加起來是 1 MB 大。1000 個 100 KB 大小的文件加起來是 100 MB 大。這可是完完全全不一樣的批量大小了。

批量請求需要在協調節點上載入進記憶體,所以批量請求的物理大小比文件計數重要得多。從 5–15 MB 開始測試批量請求大小,緩慢增加這個數字,直到你看不到效能提升為止。

然後開始增加你的批量寫入的併發度(多執行緒等等辦法)。用iostat 、 top 和 ps 等工具監控你的節點,觀察資源什麼時候達到瓶頸。

如果你開始收到 EsRejectedExecutionException ,你的叢集沒辦法再繼續了:至少有一種資源到瓶頸了。或者減少併發數,或者提供更多的受限資源(比如從機械磁碟換成 SSD),或者新增更多節點。

8、索引和shard

ES的索引,shard都會有對應的元資料,且因為ES的元資料都是儲存在master節點,且元資料的更新是要hang住叢集向所有節點同步的。

當ES的新建欄位或者新建索引的時候,都會要獲取叢集元資料,並對元資料進行變更及同步,此時會影響叢集的響應,所以需要關注叢集的index和shard數量。

建議如下:

  • 使用shrink和rollover api,相對生成合適的資料shard數;
  • 根據資料量級及對應的效能需求,選擇建立index的名稱,形如:按月生成索引:test-YYYYMM,按天生成索引:test-YYYYMMDD;
  • 控制單個shard的size,正常情況下,日誌場景,建議單個shard不大於50GB,線上業務場景,建議單個shard不超過20GB。

9、segment merge

段合併的計算量龐大, 而且還要吃掉大量磁碟 I/O。合併在後臺定期操作,因為他們可能要很長時間才能完成,尤其是比較大的段。

這個通常來說都沒問題,因為大規模段合併的概率是很小的。如果發現merge佔用了大量的資源,可以設定:index.merge.scheduler.max_thread_count:1

特別是機械磁碟在併發 I/O 支援方面比較差,所以我們需要降低每個索引併發訪問磁碟的執行緒數。這個設定允許 max_thread_count + 2 個執行緒同時進行磁碟操作,也就是設定為 1 允許三個執行緒。

對於 SSD,你可以忽略這個設定,預設是 Math.min(3, Runtime.getRuntime().availableProcessors() / 2) ,對 SSD 來說執行的很好。

業務低峰期通過force_merge強制合併segment,降低segment的數量,減小記憶體消耗;關閉冷索引,業務需要的時候再進行開啟,如果一直不使用的索引,可以定期刪除,或者備份到hadoop叢集。

10、二級自動生成_id

當寫入端使用特定的id將資料寫入ES時,ES會去檢查對應的index下是否存在相同的id,這個操作會隨著文件數量的增加而消耗越來越大,所以如果業務上沒有強需求,建議使用ES自動生成的id,加快寫入速率。

11、routing

對於資料量較大的業務查詢場景,ES側一般會建立多個shard,並將shard分配到叢集中的多個例項來分攤壓力,正常情況下,一個查詢會遍歷查詢所有的shard,然後將查詢到的結果進行merge之後,再返回給查詢端。

此時,寫入的時候設定routing,可以避免每次查詢都遍歷全量shard,而是查詢的時候也指定對應的routingkey,這種情況下,ES會只去查詢對應的shard,可以大幅度降低合併資料和排程全量shard的開銷。

12、使用alias

生產提供服務的索引,切記使用別名提供服務,而不是直接暴露索引名稱,避免後續因為業務變更或者索引資料需要reindex等情況造成業務中斷。

13、避免寬表

在索引中定義太多欄位是一種可能導致對映爆炸的情況,這可能導致記憶體不足錯誤和難以恢復的情況,這個問題可能比預期更常見,index.mapping.total_fields.limit ,預設值是1000。

14、避免稀疏索引

因為索引稀疏之後,對應的相鄰文件id的delta值會很大,lucene基於文件id做delta編碼壓縮導致壓縮率降低,從而導致索引檔案增大。

同時,ES的keyword,陣列型別採用doc_values結構,每個文件都會佔用一定的空間,即使欄位是空值,所以稀疏索引會造成磁碟size增大,導致查詢和寫入效率降低。

Java 的知識面非常廣,面試問的涉及也非常廣泛,重點包括:Java 基礎、Java 併發,JVM、MySQL、資料結構、演算法、Spring、微服務、MQ 等等,涉及的知識點何其龐大,所以我們在複習的時候也往往無從下手,今天小編給大家帶來一套 Java 面試題,題庫非常全面,包括 Java 基礎、Java 集合、JVM、Java 併發、Spring全家桶、Redis、MySQL、Dubbo、Netty、MQ 等等,包含 Java 後端知識點 2000 +

資料獲取方式:關注公眾號:“程式設計師白楠楠”獲取