因為總是看到很多同學在說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=https://www.cnblogs.com/bainannan/archive/2020/11/24/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 +
資料獲取方式:關注公眾號:“程式員白楠楠”獲取
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/227064.html
標籤:其他
