后臺(tái)回復(fù)'書',獲取 來源:elasticsearch.cn/article/6202 因?yàn)榭偸强吹胶芏嗤瑢W(xué)在說Elasticsearch性能不夠好、集群不夠穩(wěn)定,詢問關(guān)于Elasticsearch的調(diào)優(yōu),但是每次都是一個(gè)個(gè)點(diǎn)的單獨(dú)講,很多時(shí)候都是case by case的解答,本文簡單梳理下日常的Elasticsearch使用調(diào)優(yōu),以下僅為自己日常經(jīng)驗(yàn)之談,如有疏漏,還請大家?guī)兔χ刚?/span> 一、配置文件調(diào)優(yōu) elasticsearch.yml bootstrap.memory_lock:true允許JVM鎖住內(nèi)存,禁止操作系統(tǒng)交換出去。 Elasticsearch默認(rèn)被配置為使用單播發(fā)現(xiàn),以防止節(jié)點(diǎn)無意中加入集群。組播發(fā)現(xiàn)應(yīng)該永遠(yuǎn)不被使用在生產(chǎn)環(huán)境了,否則你得到的結(jié)果就是一個(gè)節(jié)點(diǎn)意外的加入到了你的生產(chǎn)環(huán)境,僅僅是因?yàn)樗麄兪盏搅艘粋€(gè)錯(cuò)誤的組播信號(hào)。 ES是一個(gè)P2P類型的分布式系統(tǒng),使用gossip協(xié)議,集群的任意請求都可以發(fā)送到集群的任一節(jié)點(diǎn),然后ES內(nèi)部會(huì)找到需要轉(zhuǎn)發(fā)的節(jié)點(diǎn),并且與之進(jìn)行通信。 在ES1.x的版本,ES默認(rèn)是開啟組播,啟動(dòng)ES之后,可以快速將局域網(wǎng)內(nèi)集群名稱,默認(rèn)端口的相同實(shí)例加入到一個(gè)大的集群,后續(xù)再ES2.x之后,都調(diào)整成了單播,避免安全問題和網(wǎng)絡(luò)風(fēng)暴。 單播 discovery.zen.ping.unicast.hosts,建議寫入集群內(nèi)所有的節(jié)點(diǎn)及端口,如果新實(shí)例加入集群,新實(shí)例只需要寫入當(dāng)前集群的實(shí)例,即可自動(dòng)加入到當(dāng)前集群,之后再處理原實(shí)例的配置即可,新實(shí)例加入集群,不需要重啟原有實(shí)例; 節(jié)點(diǎn)zen相關(guān)配置:discovery.zen.ping_timeout:判斷master選舉過程中,發(fā)現(xiàn)其他node存活的超時(shí)設(shè)置,主要影響選舉的耗時(shí),參數(shù)僅在加入或者選舉 master 主節(jié)點(diǎn)的時(shí)候才起作用discovery.zen.join_timeout:節(jié)點(diǎn)確定加入到集群中,向主節(jié)點(diǎn)發(fā)送加入請求的超時(shí)時(shí)間,默認(rèn)為3sdiscovery.zen.minimum_master_nodes:參與master選舉的最小節(jié)點(diǎn)數(shù),當(dāng)集群能夠被選為master的節(jié)點(diǎn)數(shù)量小于最小數(shù)量時(shí),集群將無法正常選舉。 兩種情況下會(huì)進(jìn)行故障檢測:
https://www./guide/en/elasticsearch/reference/6.x/modules-discovery-zen.html 不建議盲目加大ES的隊(duì)列數(shù)量,如果是偶發(fā)的因?yàn)閿?shù)據(jù)突增,導(dǎo)致隊(duì)列阻塞,加大隊(duì)列size可以使用內(nèi)存來緩存數(shù)據(jù);如果是持續(xù)性的數(shù)據(jù)阻塞在隊(duì)列,加大隊(duì)列size除了加大內(nèi)存占用,并不能有效提高數(shù)據(jù)寫入速率,反而可能加大ES宕機(jī)時(shí)候,在內(nèi)存中可能丟失的上數(shù)據(jù)量。 哪些情況下,加大隊(duì)列size呢?GET /_cat/thread_pool,觀察api中返回的queue和rejected,如果確實(shí)存在隊(duì)列拒絕或者是持續(xù)的queue,可以酌情調(diào)整隊(duì)列size。 https://www./guide/en/elasticsearch/reference/6.x/modules-threadpool.html 設(shè)置indices的內(nèi)存熔斷相關(guān)參數(shù),根據(jù)實(shí)際情況進(jìn)行調(diào)整,防止寫入或查詢壓力過高導(dǎo)致OOM:
https://www./guide/en/elasticsearch/reference/6.x/circuit-breaker.html 根據(jù)實(shí)際情況調(diào)整查詢占用cache,避免查詢cache占用過多的jvm內(nèi)存,參數(shù)為靜態(tài)的,需要在每個(gè)數(shù)據(jù)節(jié)點(diǎn)配置。indices.queries.cache.size: 5%,控制過濾器緩存的內(nèi)存大小,默認(rèn)為10%。接受百分比值,5%或者精確值,例如512mb。 https://www./guide/en/elasticsearch/reference/6.x/query-cache.html 如果集群規(guī)模較大,可以阻止新建shard時(shí)掃描集群內(nèi)全部shard的元數(shù)據(jù),提升shard分配速度。 cluster.routing.allocation.disk.include_relocations: false,默認(rèn)為true。 https://www./guide/en/elasticsearch/reference/6.x/disk-allocator.html 二、系統(tǒng)層面調(diào)優(yōu) 當(dāng)前根據(jù)官方建議,選擇匹配的jdk版本。 首先,-Xms和-Xmx設(shè)置為相同的值,避免在運(yùn)行過程中再進(jìn)行內(nèi)存分配,同時(shí),如果系統(tǒng)內(nèi)存小于64G,建議設(shè)置略小于機(jī)器內(nèi)存的一半,剩余留給系統(tǒng)使用。 同時(shí),jvm heap建議不要超過32G(不同jdk版本具體的值會(huì)略有不同),否則jvm會(huì)因?yàn)閮?nèi)存指針壓縮導(dǎo)致內(nèi)存浪費(fèi),詳見: https://www./guide/cn/elasticsearch/guide/current/heap-sizing.html 關(guān)閉交換分區(qū),防止內(nèi)存發(fā)生交換導(dǎo)致性能下降(部分情況下,寧死勿慢) swapoff -a Lucene 使用了 大量的 文件。同時(shí),Elasticsearch 在節(jié)點(diǎn)和 HTTP 客戶端之間進(jìn)行通信也使用了大量的套接字,所有這一切都需要足夠的文件描述符,默認(rèn)情況下,linux默認(rèn)運(yùn)行單個(gè)進(jìn)程打開1024個(gè)文件句柄,這顯然是不夠的,故需要加大文件句柄數(shù) ulimit -n 65536。 https://www./guide/en/elasticsearch/reference/6.5/setting-system-settings.html Elasticsearch 對各種文件混合使用了 NioFs( 注:非阻塞文件系統(tǒng))和 MMapFs ( 注:內(nèi)存映射文件系統(tǒng))。請確保你配置的最大映射數(shù)量,以便有足夠的虛擬內(nèi)存可用于 mmapped 文件。 這可以暫時(shí)設(shè)置:sysctl -w vm.max_map_count=262144 或者你可以在 /etc/sysctl.conf 通過修改 vm.max_map_count 永久設(shè)置它。 https://www./guide/cn/elasticsearch/guide/current/_file_descriptors_and_mmap.html 如果你正在使用 SSDs,確保你的系統(tǒng) I/O 調(diào)度程序是配置正確的。當(dāng)你向硬盤寫數(shù)據(jù),I/O 調(diào)度程序決定何時(shí)把數(shù)據(jù)實(shí)際發(fā)送到硬盤。大多數(shù)默認(rèn) nix 發(fā)行版下的調(diào)度程序都叫做 cfq(完全公平隊(duì)列)。但它是為旋轉(zhuǎn)介質(zhì)優(yōu)化的:機(jī)械硬盤的固有特性意味著它寫入數(shù)據(jù)到基于物理布局的硬盤會(huì)更高效。這對 SSD 來說是低效的,盡管這里沒有涉及到機(jī)械硬盤。 但是,deadline 或者 noop 應(yīng)該被使用。deadline 調(diào)度程序基于寫入等待時(shí)間進(jìn)行優(yōu)化, noop 只是一個(gè)簡單的 FIFO 隊(duì)列。echo noop > /sys/block/sd/queue/scheduler。 mount -o noatime,data=writeback,barrier=0,nobh /dev/sd* /esdata* 其中,noatime,禁止記錄訪問時(shí)間戳;data=writeback,不記錄journal;barrier=0,因?yàn)殛P(guān)閉了journal,所以同步關(guān)閉barrier;nobh,關(guān)閉buffer_head,防止內(nèi)核影響數(shù)據(jù)IO。 使用 RAID 0。條帶化 RAID 會(huì)提高磁盤I/O,代價(jià)顯然就是當(dāng)一塊硬盤故障時(shí)整個(gè)就故障了,不要使用鏡像或者奇偶校驗(yàn) RAID 因?yàn)楦北疽呀?jīng)提供了這個(gè)功能。 另外,使用多塊硬盤,并允許 Elasticsearch 通過多個(gè) path.data 目錄配置把數(shù)據(jù)條帶化分配到它們上面。不要使用遠(yuǎn)程掛載的存儲(chǔ),比如 NFS 或者 SMB/CIFS。這個(gè)引入的延遲對性能來說完全是背道而馳的。 三、Elasticsearch使用方式調(diào)優(yōu) 當(dāng)Elasticsearch本身的配置沒有明顯的問題之后,發(fā)現(xiàn)ES使用還是非常慢,這個(gè)時(shí)候,就需要我們?nèi)ザㄎ籈S本身的問題了,首先祭出定位問題的第一個(gè)命令: GET /_nodes/hot_threads&interval=30s 抓取30s的節(jié)點(diǎn)上占用資源的熱線程,并通過排查占用資源最多的TOP線程來判斷對應(yīng)的資源消耗是否正常。一般情況下,bulk,search類的線程占用資源都可能是業(yè)務(wù)造成的,但是如果是merge線程占用了大量的資源,就應(yīng)該考慮是不是創(chuàng)建index或者刷磁盤間隔太小,批量寫入size太小造成的。 https://www./guide/en/elasticsearch/reference/6.x/cluster-nodes-hot-threads.html GET /_cluster/pending_tasks 有一些任務(wù)只能由主節(jié)點(diǎn)去處理,比如創(chuàng)建一個(gè)新的索引或者在集群中移動(dòng)分片,由于一個(gè)集群中只能有一個(gè)主節(jié)點(diǎn),所以只有這一master節(jié)點(diǎn)可以處理集群級(jí)別的元數(shù)據(jù)變動(dòng)。 在99.9999%的時(shí)間里,這不會(huì)有什么問題,元數(shù)據(jù)變動(dòng)的隊(duì)列基本上保持為零。在一些罕見的集群里,元數(shù)據(jù)變動(dòng)的次數(shù)比主節(jié)點(diǎn)能處理的還快,這會(huì)導(dǎo)致等待中的操作會(huì)累積成隊(duì)列。 這個(gè)時(shí)候可以通過pending_tasks api分析當(dāng)前什么操作阻塞了ES的隊(duì)列,比如,集群異常時(shí),會(huì)有大量的shard在recovery,如果集群在大量創(chuàng)建新字段,會(huì)出現(xiàn)大量的put_mappings的操作,所以正常情況下,需要禁用動(dòng)態(tài)mapping。 https://www./guide/en/elasticsearch/reference/current/cluster-pending.html 當(dāng)前es主要有doc_values,fielddata,storefield三種類型,大部分情況下,并不需要三種類型都存儲(chǔ),可根據(jù)實(shí)際場景進(jìn)行調(diào)整:
Elasticsearch 2.0之后為了保證不丟數(shù)據(jù),每次 index、bulk、delete、update 完成的時(shí)候,一定觸發(fā)刷新 translog 到磁盤上,才給請求返回 200 OK。這個(gè)改變在提高數(shù)據(jù)安全性的同時(shí)當(dāng)然也降低了一點(diǎn)性能。如果你不在意這點(diǎn)可能性,還是希望性能優(yōu)先,可以在 index template 里設(shè)置如下參數(shù): { 'index.translog.durability': 'async' } index.translog.sync_interval: 對于一些大容量的偶爾丟失幾秒數(shù)據(jù)問題也并不嚴(yán)重的集群,使用異步的 fsync 還是比較有益的。 比如,寫入的數(shù)據(jù)被緩存到內(nèi)存中,再每5秒執(zhí)行一次 fsync ,默認(rèn)為5s。小于的值100ms是不允許的。 index.translog.flush_threshold_size: translog存儲(chǔ)尚未安全保存在Lucene中的所有操作。雖然這些操作可用于讀取,但如果要關(guān)閉并且必須恢復(fù),則需要重新編制索引。 此設(shè)置控制這些操作的最大總大小,以防止恢復(fù)時(shí)間過長。達(dá)到設(shè)置的最大size后,將發(fā)生刷新,生成新的Lucene提交點(diǎn),默認(rèn)為512mb。 執(zhí)行刷新操作的頻率,這會(huì)使索引的最近更改對搜索可見,默認(rèn)為1s,可以設(shè)置-1為禁用刷新,對于寫入速率要求較高的場景,可以適當(dāng)?shù)募哟髮?yīng)的時(shí)長,減小磁盤io和segment的生成。 動(dòng)態(tài)mapping的壞處:
動(dòng)態(tài)mapping配置的可選值及含義如下:
批量請求顯然會(huì)大大提升寫入速率,且這個(gè)速率是可以量化的,官方建議每次批量的數(shù)據(jù)物理字節(jié)數(shù)5-15MB是一個(gè)比較不錯(cuò)的起點(diǎn),注意這里說的是物理字節(jié)數(shù)大小。 文檔計(jì)數(shù)對批量大小來說不是一個(gè)好指標(biāo)。 比如說,如果你每次批量索引 1000 個(gè)文檔,記住下面的事實(shí):1000 個(gè) 1 KB 大小的文檔加起來是 1 MB 大。1000 個(gè) 100 KB 大小的文檔加起來是 100 MB 大。這可是完完全全不一樣的批量大小了。 批量請求需要在協(xié)調(diào)節(jié)點(diǎn)上加載進(jìn)內(nèi)存,所以批量請求的物理大小比文檔計(jì)數(shù)重要得多。從 5–15 MB 開始測試批量請求大小,緩慢增加這個(gè)數(shù)字,直到你看不到性能提升為止。 然后開始增加你的批量寫入的并發(fā)度(多線程等等辦法)。用iostat 、 top 和 ps 等工具監(jiān)控你的節(jié)點(diǎn),觀察資源什么時(shí)候達(dá)到瓶頸。 如果你開始收到 EsRejectedExecutionException ,你的集群沒辦法再繼續(xù)了:至少有一種資源到瓶頸了?;蛘邷p少并發(fā)數(shù),或者提供更多的受限資源(比如從機(jī)械磁盤換成 SSD),或者添加更多節(jié)點(diǎn)。 ES的索引,shard都會(huì)有對應(yīng)的元數(shù)據(jù),且因?yàn)镋S的元數(shù)據(jù)都是保存在master節(jié)點(diǎn),且元數(shù)據(jù)的更新是要hang住集群向所有節(jié)點(diǎn)同步的。 當(dāng)ES的新建字段或者新建索引的時(shí)候,都會(huì)要獲取集群元數(shù)據(jù),并對元數(shù)據(jù)進(jìn)行變更及同步,此時(shí)會(huì)影響集群的響應(yīng),所以需要關(guān)注集群的index和shard數(shù)量。 建議如下:
段合并的計(jì)算量龐大, 而且還要吃掉大量磁盤 I/O。合并在后臺(tái)定期操作,因?yàn)樗麄兛赡芤荛L時(shí)間才能完成,尤其是比較大的段。 這個(gè)通常來說都沒問題,因?yàn)榇笠?guī)模段合并的概率是很小的。如果發(fā)現(xiàn)merge占用了大量的資源,可以設(shè)置:index.merge.scheduler.max_thread_count:1 特別是機(jī)械磁盤在并發(fā) I/O 支持方面比較差,所以我們需要降低每個(gè)索引并發(fā)訪問磁盤的線程數(shù)。這個(gè)設(shè)置允許 max_thread_count + 2 個(gè)線程同時(shí)進(jìn)行磁盤操作,也就是設(shè)置為 1 允許三個(gè)線程。 對于 SSD,你可以忽略這個(gè)設(shè)置,默認(rèn)是 Math.min(3, Runtime.getRuntime().availableProcessors() / 2) ,對 SSD 來說運(yùn)行的很好。 業(yè)務(wù)低峰期通過force_merge強(qiáng)制合并segment,降低segment的數(shù)量,減小內(nèi)存消耗;關(guān)閉冷索引,業(yè)務(wù)需要的時(shí)候再進(jìn)行開啟,如果一直不使用的索引,可以定期刪除,或者備份到hadoop集群。 當(dāng)寫入端使用特定的id將數(shù)據(jù)寫入ES時(shí),ES會(huì)去檢查對應(yīng)的index下是否存在相同的id,這個(gè)操作會(huì)隨著文檔數(shù)量的增加而消耗越來越大,所以如果業(yè)務(wù)上沒有強(qiáng)需求,建議使用ES自動(dòng)生成的id,加快寫入速率。 對于數(shù)據(jù)量較大的業(yè)務(wù)查詢場景,ES側(cè)一般會(huì)創(chuàng)建多個(gè)shard,并將shard分配到集群中的多個(gè)實(shí)例來分?jǐn)倝毫?,正常情況下,一個(gè)查詢會(huì)遍歷查詢所有的shard,然后將查詢到的結(jié)果進(jìn)行merge之后,再返回給查詢端。 此時(shí),寫入的時(shí)候設(shè)置routing,可以避免每次查詢都遍歷全量shard,而是查詢的時(shí)候也指定對應(yīng)的routingkey,這種情況下,ES會(huì)只去查詢對應(yīng)的shard,可以大幅度降低合并數(shù)據(jù)和調(diào)度全量shard的開銷。 生產(chǎn)提供服務(wù)的索引,切記使用別名提供服務(wù),而不是直接暴露索引名稱,避免后續(xù)因?yàn)闃I(yè)務(wù)變更或者索引數(shù)據(jù)需要reindex等情況造成業(yè)務(wù)中斷。 在索引中定義太多字段是一種可能導(dǎo)致映射爆炸的情況,這可能導(dǎo)致內(nèi)存不足錯(cuò)誤和難以恢復(fù)的情況,這個(gè)問題可能比預(yù)期更常見,index.mapping.total_fields.limit ,默認(rèn)值是1000。 因?yàn)樗饕∈柚?,對?yīng)的相鄰文檔id的delta值會(huì)很大,lucene基于文檔id做delta編碼壓縮導(dǎo)致壓縮率降低,從而導(dǎo)致索引文件增大。 同時(shí),ES的keyword,數(shù)組類型采用doc_values結(jié)構(gòu),每個(gè)文檔都會(huì)占用一定的空間,即使字段是空值,所以稀疏索引會(huì)造成磁盤size增大,導(dǎo)致查詢和寫入效率降低。 |
|