ES的性能調優技巧


一、配置文件調優

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增大,導致查詢和寫入效率降低。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM