公司的業(yè)務(wù)日志數(shù)據(jù)量暴漲,內(nèi)存很快就不夠用,時(shí)常出現(xiàn)OOM,并且在 failover 的過(guò)程中寫(xiě)入速度不理想,很是揪心??焖賹W(xué)習(xí)了一下原理,進(jìn)行了一系列優(yōu)化,遂紀(jì)錄一下。
背景知識(shí)
了解 LSM 樹(shù)的同學(xué)就不用看這塊了,對(duì)標(biāo) HBase 的概念來(lái)說(shuō):
- Translog 類似 HLog;
- Type 類似 HTable;
- Document 類似 Row;
- Field 類似 Column;
- Segment 類似 HFile(數(shù)據(jù)結(jié)構(gòu)當(dāng)然完全不一樣);
- Optimize/force merge 類似 Compaction;
- refresh 是 ES 特有的保證數(shù)據(jù)實(shí)時(shí)可被搜索;
- flush 類似 flush;
Segment 的類似日志式的 append 寫(xiě)入,是為了優(yōu)化寫(xiě)性能;Optimize/force merge 是為了減少 segment 數(shù)量(同時(shí)影響文件句柄/內(nèi)存/消耗CPU等資源),優(yōu)化讀性能。
倒排索引
與傳統(tǒng)的數(shù)據(jù)庫(kù)不同,在 ES 中,每個(gè)字段里面的每個(gè)單詞都是可以被搜索的。如 hobby:"dance,sing,swim,run",我們?cè)谒阉麝P(guān)鍵字 swim 時(shí),所有包含 swim 的文檔都會(huì)被匹配到,ES 的這個(gè)特性也叫做全文搜索。
為了支持這個(gè)特性,ES 中會(huì)維護(hù)一個(gè)叫做 invertedindex(也叫倒排索引)的表,表內(nèi)包含了所有文檔中出現(xiàn)的所有單詞,同時(shí)記錄了這個(gè)單詞在哪個(gè)文檔中出現(xiàn)過(guò)。
例:
當(dāng)前有3個(gè)文檔
Doc1:"brown,fox,quick,the"
Doc2:"fox,quick"
Doc3:"brown,fox,the"
那么 ES 會(huì)維護(hù)如下數(shù)據(jù)結(jié)構(gòu):

這樣我們隨意搜索任意一個(gè)單詞,ES 只要遍歷一下這個(gè)表,就可以知道有些文檔被匹配到了。
倒排索引里不止記錄了單詞與文檔的對(duì)應(yīng)關(guān)系,它還維護(hù)了很多其他有用的數(shù)據(jù)。如:每個(gè)文檔一共包含了多少個(gè)單詞,單詞在不同文檔中的出現(xiàn)頻率,每個(gè)文檔的長(zhǎng)度,所有文檔的總長(zhǎng)度等等。這些數(shù)據(jù)用來(lái)給搜索結(jié)果進(jìn)行打分,如搜索單詞 apple 時(shí),那么出現(xiàn) apple 這個(gè)單詞次數(shù)最多的文檔會(huì)被優(yōu)先返回,因?yàn)樗ヅ涞拇螖?shù)最多,和我們的搜索條件關(guān)聯(lián)性最大,因此得分也最多。
倒排索引是不可更改的,一旦它被建立了,里面的數(shù)據(jù)就不會(huì)再進(jìn)行更改。這樣做就帶來(lái)了以下幾個(gè)好處:
- 沒(méi)有必要給倒排索引加鎖,因?yàn)椴辉试S被更改,只有讀操作,所以就不用考慮多線程導(dǎo)致互斥等問(wèn)題;
- 索引一旦被加載到了緩存中,大部分訪問(wèn)操作都是對(duì)內(nèi)存的讀操作,省去了訪問(wèn)磁盤(pán)帶來(lái)的 IO 開(kāi)銷;
- 因?yàn)榈古潘饕牟豢勺冃?,所有基于該索引而產(chǎn)生的緩存也不需要更改,因?yàn)闆](méi)有數(shù)據(jù)變更;
- 使用倒排索引可以壓縮數(shù)據(jù),減少磁盤(pán) IO 及對(duì)內(nèi)存的消耗;
Segment
既然倒排索引是不可更改的,那么如何添加新的數(shù)據(jù),刪除數(shù)據(jù)以及更新數(shù)據(jù)?為了解決這個(gè)問(wèn)題,Lucene 將一個(gè)大的倒排索引拆分成了多個(gè)小的段 (segment)。每個(gè) segment 本質(zhì)上就是一個(gè)倒排索引。在 Lucene 中,同時(shí)還會(huì)維護(hù)一個(gè)文件 commit point,用來(lái)記錄當(dāng)前所有可用的 segment ,當(dāng)我們?cè)谶@個(gè) commit point 上進(jìn)行搜索時(shí),就相當(dāng)于在它下面的 segment 中進(jìn)行搜索,每個(gè) segment 返回自己的搜索結(jié)果,然后進(jìn)行匯總返回給用戶。
引入了segment和commit point的概念之后,數(shù)據(jù)的更新流程如下圖:

- 新增的文檔首先會(huì)被存放在內(nèi)存的緩存中;
- 當(dāng)文檔數(shù)足夠多或者到達(dá)一定時(shí)間點(diǎn)時(shí),就會(huì)對(duì)緩存進(jìn)行 commit:
- 生成一個(gè)新的 segment ,并寫(xiě)入磁盤(pán);
- 生成一個(gè)新的 commit point ,記錄當(dāng)前所有可用的 segment;
- 等待所有數(shù)據(jù)都已寫(xiě)入磁盤(pán);
- 打開(kāi)新增的 segment,這樣我們就可以對(duì)新增的文檔進(jìn)行搜索了;
- 清空緩存,準(zhǔn)備接收新的文檔;
文檔的更新與刪除
segment 是不能更改的,那么如何刪除或者更新文檔?
每個(gè) commit point 都會(huì)維護(hù)一個(gè) .del 文件,文件內(nèi)記錄了在某個(gè) segment 內(nèi)某個(gè)文檔已經(jīng)被刪除。在 segment 中,被刪除的文檔依舊是能夠被搜索到的,不過(guò)在返回搜索結(jié)果前,會(huì)根據(jù) .del 把那些已經(jīng)刪除的文檔從搜索結(jié)果中過(guò)濾掉。
對(duì)于文檔的更新,采用和刪除文檔類似的實(shí)現(xiàn)方式。當(dāng)一個(gè)文檔發(fā)生更新時(shí),首先會(huì)在 .del 中聲明這個(gè)文檔已經(jīng)被刪除,同時(shí)新的文檔會(huì)被存放到一個(gè)新的 segment 中。這樣在搜索時(shí),雖然新的文檔和老的文檔都會(huì)被匹配到,但是 .del 會(huì)把老的文檔過(guò)濾掉,返回的結(jié)果中只包含更新后的文檔。
Refresh
ES 的一個(gè)特性就是提供實(shí)時(shí)搜索,新增加的文檔可以在很短的時(shí)間內(nèi)就被搜索到。在創(chuàng)建一個(gè) commit point 時(shí),為了確保所有的數(shù)據(jù)都已經(jīng)成功寫(xiě)入磁盤(pán),避免因?yàn)閿嚯姷仍驅(qū)е戮彺嬷械臄?shù)據(jù)丟失,在創(chuàng)建 segment 時(shí)需要一個(gè) fsync 的操作來(lái)確保磁盤(pán)寫(xiě)入成功。但是如果每次新增一個(gè)文檔都要執(zhí)行一次 fsync 就會(huì)產(chǎn)生很大的性能影響。在文檔被寫(xiě)入 segment 之后,segment 首先被寫(xiě)入了文件系統(tǒng)的緩存中,這個(gè)過(guò)程僅使用很少的資源。之后 segment 會(huì)從文件系統(tǒng)的緩存中逐漸 flush 到磁盤(pán),這個(gè)過(guò)程時(shí)間消耗較大。但是實(shí)際上存放在文件緩存中的文件同樣可以被打開(kāi)讀取。ES 利用這個(gè)特性,在 segment 被 commit 到磁盤(pán)之前,就打開(kāi)對(duì)應(yīng)的 segment,這樣存放在這個(gè)segment中的文檔就可以立即被搜索到了。

上圖中灰色部分即存放在緩存中,還沒(méi)有被 commit 到磁盤(pán)的 segment 。此時(shí)這個(gè) segment 已經(jīng)可以進(jìn)行搜索。
在 ES 中,將緩存中的文檔寫(xiě)入 segment,并打開(kāi) segment 使之可以被搜索的過(guò)程叫做 refresh。默認(rèn)情況下,分片的 refresh 頻率是每秒 1 次。這就解釋了為什么 ES 聲稱提供實(shí)時(shí)搜索功能,新增加的文檔會(huì)在 1s 內(nèi)就可以進(jìn)行搜索了。
Refresh 的頻率通過(guò) index.refresh_interval:1s 參數(shù)控制,一條新寫(xiě)入 ES 的日志,在進(jìn)行 refresh 之前,是在 ES 中不能立即搜索不到的。
通過(guò)執(zhí)行 curl -XPOST 127.0.0.1:9200/_refresh,可以手動(dòng)觸發(fā) refresh 行為。
Flush 與 Translog
前面講到,refresh 行為會(huì)立即把緩存中的文檔寫(xiě)入 segment 中,但是此時(shí)新創(chuàng)建的 segment 是寫(xiě)在文件系統(tǒng)的緩存中的。如果出現(xiàn)斷電等異常,那么這部分?jǐn)?shù)據(jù)就丟失了。所以 ES 會(huì)定期執(zhí)行 flush 操作,將緩存中的 segment 全部寫(xiě)入磁盤(pán)并確保寫(xiě)入成功,同時(shí)創(chuàng)建一個(gè) commit point ,整個(gè)過(guò)程就是一個(gè)完整的 commit 過(guò)程。
但是如果斷電的時(shí)候,緩存中的 segment 還沒(méi)有來(lái)得及被 commit 到磁盤(pán),那么數(shù)據(jù)依舊會(huì)產(chǎn)生丟失。為了防止這個(gè)問(wèn)題,ES 中又引入了 translog 文件。
-
每當(dāng) ES 接收一個(gè)文檔時(shí),在把文檔放在 buffer 的同時(shí),都會(huì)把文檔記錄在 translog 中。
image.png -
執(zhí)行 refresh 操作時(shí),會(huì)將緩存中的文檔寫(xiě)入 segment 中,但是此時(shí) segment 是放在緩存中的,并沒(méi)有落入磁盤(pán),此時(shí)新創(chuàng)建的 segment 是可以進(jìn)行搜索的。
image.png -
按照如上的流程,新的 segment 繼續(xù)被創(chuàng)建,同時(shí)這期間新增的文檔會(huì)一直被寫(xiě)到 translog 中。
image.png - 當(dāng)達(dá)到一定的時(shí)間間隔,或者 translog 足夠大時(shí),就會(huì)執(zhí)行 commit 行為,將所有緩存中的 segment 寫(xiě)入磁盤(pán)。確保寫(xiě)入成功后,translog 就會(huì)被清空。
image.png
執(zhí)行 commit 并清空 translog 的行為,在 ES 中可以通過(guò)_flushAPI 進(jìn)行手動(dòng)觸發(fā)。
如:curl -XPOST 127.0.0.1:9200/{INDEX_NAME}|{INDEX_PATTERN}/_flush?v
通常這個(gè) flush 行為不需要人工干預(yù),交給 ES 自動(dòng)執(zhí)行就好了。同時(shí),在重啟 ES 或者關(guān)閉索引之間,建議先執(zhí)行 flush 行為,確保所有數(shù)據(jù)都被寫(xiě)入磁盤(pán),避免照成數(shù)據(jù)丟失。通過(guò)調(diào)用 sh service.sh start/restart,會(huì)自動(dòng)完成 flush 操作。
Segment 的合并
前面講到 ES 會(huì)定期的將收到的文檔寫(xiě)入新的 segment 中,這樣經(jīng)過(guò)一段時(shí)間之后,就會(huì)出現(xiàn)很多 segment 。但是每個(gè) segment 都會(huì)占用獨(dú)立的文件句柄/內(nèi)存/消耗CPU資源,而且,在查詢的時(shí)候,需要在每個(gè)segment上都執(zhí)行一次查詢,這樣是很消耗性能的。
為了解決這個(gè)問(wèn)題,ES 會(huì)自動(dòng)定期的將多個(gè)小 segment 合并為一個(gè)大的 segment 。前面講到刪除文檔的時(shí)候,并沒(méi)有真正從 segment 中將文檔刪除,而是維護(hù)了一個(gè) .del 文件,但是當(dāng) segment 合并的過(guò)程中,就會(huì)自動(dòng)將 .del 中的文檔丟掉,從而實(shí)現(xiàn)真正意義上的刪除操作。
當(dāng)新合并后的 segment 完全寫(xiě)入磁盤(pán)之后,ES 就會(huì)自動(dòng)刪除掉那些零碎的 segment,之后的查詢都在新合并的segment 上執(zhí)行。Segment 的合并會(huì)消耗大量的 IO 和 CPU 資源,這會(huì)影響查詢性能。
在 ES 中,可以使用 optimize/force merge 接口,來(lái)控制 segment 的合并。
如:curl -X POST/{INDEX_NAME}|{INDEX_PATTERN}/_optimize?max_num_segments=1
這樣,ES 就會(huì)將 segment 合并為 1 個(gè)。但是對(duì)于那些更新比較頻繁的索引,不建議使用 optimize/force merge 去執(zhí)行分片合并,交給后臺(tái)自己處理就好了。
內(nèi)存優(yōu)化
為什么會(huì)導(dǎo)致OOM?
為什么會(huì)導(dǎo)致我們的ES出現(xiàn)了OOM?這個(gè)問(wèn)題比較復(fù)雜,不能一概而論,先從內(nèi)存占用組成說(shuō)起。
- Segment Memory(段內(nèi)存,永駐)
每個(gè) segment 對(duì)標(biāo)一個(gè) Lucene 倒排索引,而倒排索引是通過(guò)詞典 ( Term Dictionary ) 到文檔列表 (Postings List) 的映射關(guān)系,用于快速查詢。 由于詞典的 size 會(huì)很大,全部裝載到 heap 并里不現(xiàn)實(shí),因此 Lucene 為詞典做了一層前綴索引 ( Term Index ) ,這個(gè)索引在 Lucene4.0 以后采用的數(shù)據(jù)結(jié)構(gòu)是 FST ( Finite State Transducer ) 。 這種數(shù)據(jù)結(jié)構(gòu)占用空間很小,Lucene 打開(kāi)索引的時(shí)候?qū)⑵淙垦b載到內(nèi)存中,加快磁盤(pán)上詞典查詢速度的同時(shí)減少隨機(jī)磁盤(pán)訪問(wèn)次數(shù)。因此 segment 越多,占用的 heap 也越多,并且這部分內(nèi)存是無(wú)法被 GC 的。理解這點(diǎn)對(duì)于監(jiān)控和管理集群容量很重要,當(dāng)一個(gè) node 的 segment memory 占用過(guò)多,就需要考慮刪除、歸檔數(shù)據(jù),或者擴(kuò)容了。
查看 segment 的占用情況 API:
- 按照索引維度進(jìn)行查詢:
GET _cat/segments/{INDEX_NAME}|{INDEX_PATTERN}?v&h=shard,segment,size,size.memory
- 按照節(jié)點(diǎn)維度進(jìn)行查詢:
GET _cat/nodes?v&h=name,port,sm
- Filter Cache(Filter結(jié)果集,永駐)
Filter cache 是用來(lái)緩存使用過(guò)的 filter 的結(jié)果集的,需要注意的是這個(gè)緩存也是常駐 heap,無(wú)法 GC。
- Field Data Cache
在有大量排序、數(shù)據(jù)聚合的應(yīng)用場(chǎng)景,可以說(shuō) field data cache 是性能和穩(wěn)定性的殺手。 對(duì)搜索結(jié)果做排序或者聚合操作,需要將倒排索引里的數(shù)據(jù)進(jìn)行解析,然后進(jìn)行一次倒排。 這個(gè)過(guò)程非常耗費(fèi)時(shí)間,因此 ES 2.0 以前的版本主要依賴這個(gè) cache 緩存已經(jīng)計(jì)算過(guò)的數(shù)據(jù),提升性能。但是由于 heap 空間有限,當(dāng)遇到用戶對(duì)海量數(shù)據(jù)做計(jì)算的時(shí)候,就很容易導(dǎo)致 heap 吃緊,集群頻繁 GC,根本無(wú)法完成計(jì)算過(guò)程。 ES2.0以后,正式默認(rèn)啟用Doc Values 特性 ( 1.x需要手動(dòng)更改mapping開(kāi)啟 ),將 field data 在 indexing time 構(gòu)建在磁盤(pán)上,經(jīng)過(guò)一系列優(yōu)化,可以達(dá)到比之前采用 field data cache 機(jī)制更好的性能。因此需要限制對(duì) field data cache 的使用,最好是完全不用,可以極大釋放 heap 壓力。 需要注意的是,很多同學(xué)已經(jīng)升級(jí)到 ES2.0,或者 1.0 里已經(jīng)設(shè)置 mapping 啟用了 doc values,在 kibana 里仍然會(huì)遇到問(wèn)題。 這里一個(gè)陷阱就在于 kibana 的 table panel 可以對(duì)所有字段排序。 設(shè)想如果有一個(gè)字段是 analyzed 過(guò)的,而用戶去點(diǎn)擊對(duì)應(yīng)字段的排序是什么后果? 一來(lái)排序的結(jié)果并不是用戶想要的,排序的對(duì)象實(shí)際是詞典; 二來(lái) analyzed 過(guò)的字段無(wú)法利用 doc values ,需要裝載到 field data cache,數(shù)據(jù)量很大的情況下可能集群就在忙著 GC 或者根本出不來(lái)結(jié)果。
- Bulk Queue
一般來(lái)說(shuō),Bulk queue 不會(huì)消耗很多的 heap,但是見(jiàn)過(guò)一些用戶為了提高 bulk 的速度,客戶端設(shè)置了很大的并發(fā)量,并且將 Bulk Queue 設(shè)置到不可思議的大,比如好幾千。 Bulk Queue 是做什么用的?當(dāng)所有的 bulk thread 都在忙,無(wú)法響應(yīng)新的 bulk request 的時(shí)候,將 request 在內(nèi)存里排列起來(lái),然后慢慢清掉。 這在應(yīng)對(duì)短暫的請(qǐng)求爆發(fā)的時(shí)候有用,但是如果集群本身索引速度一直跟不上,設(shè)置的好幾千的 queue 都滿了會(huì)是什么狀況呢? 取決于一個(gè) bulk 的數(shù)據(jù)量大小,乘上 queue 的大小,heap 很有可能就不夠用,內(nèi)存溢出了。
- Indexing Buffer
Indexing Buffer 是用來(lái)緩存新數(shù)據(jù),由 data node 上所有 shards 共享,當(dāng)其滿了或者 flush interval 到了,就會(huì)以 segment file 的形式寫(xiě)入到磁盤(pán)。
- Cluster State Buffer
ES 被設(shè)計(jì)成每個(gè) node 都可以響應(yīng)用戶的 API 請(qǐng)求,因此每個(gè) node 的內(nèi)存里都包含有一份集群狀態(tài)的拷貝。這個(gè) cluster state 包含諸如集群有多少個(gè) node,多少個(gè) index,每個(gè) index 的 mapping 是什么?有多少 shard,每個(gè) shard 的分配情況等等 ( ES 有各類 stats API 獲取這類數(shù)據(jù))。 在一個(gè)規(guī)模很大的集群,這個(gè)狀態(tài)信息可能會(huì)非常大的,耗用的內(nèi)存空間就不可忽視了。并且在 ES2.0 之前的版本,state 的更新是由 master node 做完以后全量散播到其他結(jié)點(diǎn)的。 頻繁的狀態(tài)更新都有可能給 heap 帶來(lái)壓力。
- 超大搜索聚合結(jié)果集的 fetch
ES 是分布式搜索引擎,搜索和聚合計(jì)算除了在各個(gè) data node 并行計(jì)算以外,還需要將結(jié)果返回給匯總節(jié)點(diǎn)進(jìn)行匯總和排序后再返回。無(wú)論是搜索,還是聚合,如果返回結(jié)果的 size 設(shè)置過(guò)大,都會(huì)給 heap 造成很大的壓力,特別是數(shù)據(jù)匯聚節(jié)點(diǎn)。超大的 size 多數(shù)情況下都是用戶用例不對(duì),比如本來(lái)是想計(jì)算 cardinality,卻用了 terms aggregation + size:0 這樣的方式,對(duì)大結(jié)果集做深度分頁(yè)、一次性拉取全量數(shù)據(jù)等等。
怎么進(jìn)行內(nèi)存優(yōu)化?
- Segment Memory
那么有哪些途徑減少 data node 上的 segment memory 呢? 總結(jié)起來(lái)有三種方法:
- 刪除不用的索引;
刪除索引 API:
DELETE {INDEX_NAME}|{INDEX_PATTERN}
- 關(guān)閉索引 (文件仍然存在于磁盤(pán),只是釋放掉內(nèi)存),需要的時(shí)候可以重新打開(kāi);
關(guān)閉索引 API:
POST {INDEX_NAME}|{INDEX_PATTERN}/_close
- 定期對(duì)不再更新的索引做 optimize (ES2.0以后更改為 force merge API)。這Optimze的實(shí)質(zhì)是對(duì)segment file 強(qiáng)制做合并,可以節(jié)省大量的 segment memory;(會(huì)占用大量 IO,建議業(yè)務(wù)低峰期觸發(fā))
在合并前需要對(duì)合并速度進(jìn)行合理限制,默認(rèn)是20MBps:
PUT /_cluster/settings { "persistent" : { "indices.store.throttle.max_bytes_per_sec" : "20mb" } }強(qiáng)制合并 API,示例表示的是最終合并為一個(gè) segment file:
POST /{INDEX_NAME}|{INDEX_PATTERN}/_forcemerge?max_num_segments=1
- Filter Cache
默認(rèn)的 10% heap 設(shè)置工作得夠好,如果實(shí)際使用中 heap 沒(méi)什么壓力的情況下,才考慮加大這個(gè)設(shè)置。
- Field Data Cache
升級(jí)至 ES 2.0+(我想現(xiàn)在至少是5.X了吧?),并且對(duì)需要排序的字段不進(jìn)行 analyzed,盡量使用 doc values。對(duì)于不參與搜索的字段 ( fields ), 將其 index 方法設(shè)置為 no,如果對(duì)分詞沒(méi)有需求,對(duì)參與搜索的字段,其 index方法設(shè)置為 not_analyzed。
- Bulk Queue
一般來(lái)說(shuō)官方默認(rèn)的 thread pool 設(shè)置已經(jīng)能很好的工作了,建議不要隨意去調(diào)優(yōu)相關(guān)的設(shè)置,很多時(shí)候都是適得其反的效果。
- Indexing Buffer
這個(gè)參數(shù)的默認(rèn)值是10% heap size。根據(jù)經(jīng)驗(yàn),這個(gè)默認(rèn)值也能夠很好的工作,應(yīng)對(duì)很大的索引吞吐量。 但有些用戶認(rèn)為這個(gè) buffer 越大吞吐量越高,因此見(jiàn)過(guò)有用戶將其設(shè)置為 40% 的。到了極端的情況,寫(xiě)入速度很高的時(shí)候,40%都被占用,導(dǎo)致OOM。
- Cluster State Buffer
在超大規(guī)模集群的情況下,可以考慮分集群并通過(guò) tribe node 連接做到對(duì)用戶透明,這樣可以保證每個(gè)集群里的 state 信息不會(huì)膨脹得過(guò)大。
- 超大搜索聚合結(jié)果集的 fetch
避免用戶 fetch 超大搜索聚合結(jié)果集,確實(shí)需要大量拉取數(shù)據(jù)可以采用 scan & scroll API 來(lái)實(shí)現(xiàn)。
開(kāi)啟慢查詢?nèi)罩?/h3>
不論是數(shù)據(jù)庫(kù)還是搜索引擎,對(duì)于問(wèn)題的排查,開(kāi)啟慢查詢?nèi)罩臼鞘直匾模珽S 開(kāi)啟慢查詢的方式有多種,但是最常用的是調(diào)用模板 API 進(jìn)行全局設(shè)置:
PUT /_template/{TEMPLATE_NAME}
{
"template":"{INDEX_PATTERN}",
"settings" : {
"index.indexing.slowlog.level": "INFO",
"index.indexing.slowlog.threshold.index.warn": "10s",
"index.indexing.slowlog.threshold.index.info": "5s",
"index.indexing.slowlog.threshold.index.debug": "2s",
"index.indexing.slowlog.threshold.index.trace": "500ms",
"index.indexing.slowlog.source": "1000",
"index.search.slowlog.level": "INFO",
"index.search.slowlog.threshold.query.warn": "10s",
"index.search.slowlog.threshold.query.info": "5s",
"index.search.slowlog.threshold.query.debug": "2s",
"index.search.slowlog.threshold.query.trace": "500ms",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.search.slowlog.threshold.fetch.info": "800ms",
"index.search.slowlog.threshold.fetch.debug": "500ms",
"index.search.slowlog.threshold.fetch.trace": "200ms"
},
"version" : 1
}
對(duì)于已經(jīng)存在的 index 使用 settings API:
PUT {INDEX_PAATERN}/_settings
{
"index.indexing.slowlog.level": "INFO",
"index.indexing.slowlog.threshold.index.warn": "10s",
"index.indexing.slowlog.threshold.index.info": "5s",
"index.indexing.slowlog.threshold.index.debug": "2s",
"index.indexing.slowlog.threshold.index.trace": "500ms",
"index.indexing.slowlog.source": "1000",
"index.search.slowlog.level": "INFO",
"index.search.slowlog.threshold.query.warn": "10s",
"index.search.slowlog.threshold.query.info": "5s",
"index.search.slowlog.threshold.query.debug": "2s",
"index.search.slowlog.threshold.query.trace": "500ms",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.search.slowlog.threshold.fetch.info": "800ms",
"index.search.slowlog.threshold.fetch.debug": "500ms",
"index.search.slowlog.threshold.fetch.trace": "200ms"
}
這樣,在日志目錄下的慢查詢?nèi)罩揪蜁?huì)有輸出記錄必要的信息了。
{CLUSTER_NAME}_index_indexing_slowlog.log
{CLUSTER_NAME}_index_search_slowlog.log
寫(xiě)入性能優(yōu)化
之前描述了 ES 在內(nèi)存管理方面的優(yōu)化,接下來(lái)梳理下如何對(duì)寫(xiě)入性能進(jìn)行優(yōu)化,寫(xiě)入性能的優(yōu)化也和 HBase 類似,無(wú)非就是增加吞吐,而增加吞吐的方法就是增大刷寫(xiě)間隔、合理設(shè)置線程數(shù)量、開(kāi)啟異步刷寫(xiě)(允許數(shù)據(jù)丟失的情況下)。
增大刷寫(xiě)間隔
通過(guò)修改主配置文件 elasticsearch.yml 或者 Rest API 都可以對(duì) index.refresh_interval 進(jìn)行修改,增大該屬性可以提升寫(xiě)入吞吐。
PUT /_template/{TEMPLATE_NAME}
{
"template":"{INDEX_PATTERN}",
"settings" : {
"index.refresh_interval" : "30s"
}
}
PUT {INDEX_PAATERN}/_settings
{
"index.refresh_interval" : "30s"
}
合理設(shè)置線程數(shù)量
調(diào)整 elasticsearch.yml ,對(duì) bulk/flush 線程池進(jìn)行調(diào)優(yōu),根據(jù)本機(jī)實(shí)際配置:
threadpool.bulk.type:fixed
threadpool.bulk.size:8 #(CPU核數(shù))
threadpool.flush.type:fixed
threadpool.flush.size:8 #(CPU核數(shù))
開(kāi)啟異步刷寫(xiě)
如果允許數(shù)據(jù)丟失,可以對(duì)特定 index 開(kāi)啟異步刷寫(xiě):
PUT /_template/{TEMPLATE_NAME}
{
"template":"{INDEX_PATTERN}",
"settings" : {
"index.translog.durability": "async"
}
}
PUT {INDEX_PAATERN}/_settings
{
"index.translog.durability": "async"
}



