導(dǎo)言
ES 是在系統(tǒng)架構(gòu)設(shè)計(jì)時(shí)常用到的數(shù)據(jù)搜索與存儲系統(tǒng),對于輸入的數(shù)據(jù),ES會按照預(yù)先定義好的設(shè)置(如果沒有預(yù)先設(shè)置好,可以設(shè)置動(dòng)態(tài)映射)進(jìn)行分詞,建立倒排索引和Doc_values,以方便搜索查詢。ES本身具備對索引進(jìn)行分片和備份的功能,使用者可以在索引建立時(shí)進(jìn)行設(shè)置。ES 底層使用了Luence對數(shù)據(jù)進(jìn)行管理,對宿主機(jī)的內(nèi)存和磁盤IO有較高的要求,這使得每個(gè)ES的節(jié)點(diǎn)成本會比較高,因此在規(guī)劃階段需要對要使用的ES節(jié)點(diǎn)數(shù)目進(jìn)行評估,以了解后續(xù)的成本狀況。
節(jié)點(diǎn)
不考慮使用MachineLearning的情況,ES 的節(jié)點(diǎn)角色設(shè)置有以下幾種:
-
Master(主節(jié)點(diǎn))
集群的主節(jié)點(diǎn),負(fù)責(zé)集群的編排。對應(yīng)節(jié)點(diǎn)配置中的node.master設(shè)為true
-
Data(數(shù)據(jù)節(jié)點(diǎn))
數(shù)據(jù)節(jié)點(diǎn),負(fù)責(zé)數(shù)據(jù)的搜索與寫入。對應(yīng)節(jié)點(diǎn)配置中的node.data設(shè)為true
-
Ingest(ingest節(jié)點(diǎn))
負(fù)責(zé)進(jìn)行請求的預(yù)處理(pipeline)。對應(yīng)節(jié)點(diǎn)配置中的node.ingest設(shè)為true
-
coordinate(協(xié)調(diào)節(jié)點(diǎn))
接收外部請求,并將請求分配到相應(yīng)應(yīng)的節(jié)點(diǎn),并對節(jié)點(diǎn)返回的數(shù)據(jù)進(jìn)行合并處理。當(dāng)前三個(gè)角色的配置都是false的情況下,該節(jié)點(diǎn)會承擔(dān)專門協(xié)調(diào)的角色。實(shí)際情況下,所有節(jié)點(diǎn)都具備coordinator請求的能力。
對于小規(guī)模的集群,由于設(shè)備有限,往往node.master,node.data,node.ingest都設(shè)置為true,方便管理。對于較大規(guī)模的集群,由于這幾個(gè)角色對計(jì)算資源的要求不同,往往分配單獨(dú)角色節(jié)點(diǎn)以最優(yōu)化資源利用。
同時(shí),考慮到冷熱數(shù)據(jù)不同的索引需求,一般會將數(shù)據(jù)節(jié)點(diǎn)再按冷熱溫(Hot,Warm,Cold)進(jìn)行劃分,同時(shí)結(jié)合索引的生命周期管理(ILM)模塊進(jìn)行搭配使用。(ILM參考:http://www.itdecent.cn/p/9f7f39efeda3)
-
Hot節(jié)點(diǎn)
處理當(dāng)前最新數(shù)據(jù)的寫入和查詢,一般會配置大內(nèi)存和固態(tài)硬盤,具有較高的磁盤IO,SSD一般會具備200,000到400,000的IOPS,如果以存儲量來算,單塊SSD能夠達(dá)到1-2GBps。
相對而言,普通HDD的順序?qū)懭胨俣却蟾偶s240MBps,如果是隨機(jī)寫入則速度更低,可以通過配置Raid0來提示HDD的性能。由于ES本身在邏輯成本實(shí)現(xiàn)了高可用,因此不需要再通過搭建raid5的方式提供磁盤的高可用。
-
Warm節(jié)點(diǎn)
一般用來存儲非最新數(shù)據(jù),不會有實(shí)時(shí)寫入的請求,有時(shí)候會處理一些查詢,索引處于只讀狀態(tài)。一般會使用HDD配合中等配置的內(nèi)存。
-
Cold節(jié)點(diǎn)
Cold節(jié)點(diǎn)用來存放歸檔的數(shù)據(jù),以滿足一些法律法規(guī)。如果數(shù)據(jù)并沒有留存的需求,一般超過一定的時(shí)限就會刪除相關(guān)的索引。Cold節(jié)點(diǎn)需要廉價(jià)的大容量存儲,索引處于關(guān)閉狀態(tài),沒有實(shí)時(shí)的查詢需求。
ES的數(shù)據(jù)節(jié)點(diǎn)往往有較大的負(fù)載壓力,一般建議節(jié)點(diǎn)不要共用硬件設(shè)備,一防止因?yàn)橛布驅(qū)е露鄠€(gè)節(jié)點(diǎn)同時(shí)離線。
由于Java壓縮指針的原因,分配給節(jié)點(diǎn)JVM的堆內(nèi)存不建議超過32GB,一般推薦分配一般的內(nèi)存給JVM,另外一般內(nèi)存用來做page cache。節(jié)點(diǎn)的swap需要禁止,其原因在于如果不禁止swap,節(jié)點(diǎn)就可能交換JVM的堆內(nèi)存而不是page cache,這會導(dǎo)致更長的系統(tǒng)響應(yīng)時(shí)間。
業(yè)務(wù)場景
對于數(shù)據(jù)來說,對應(yīng)操作包括,增(index),刪(delete),改(update),查(search/query)。對于ES來說,比較重要的兩個(gè)場景為新增(數(shù)據(jù)寫入)和查詢。
數(shù)據(jù)寫入
如下圖所示,數(shù)據(jù)的寫入有以下流程:
- coordinator節(jié)點(diǎn)接收到外界的寫入請求。
- 如果集群有配置ingest pipeline,coordinator節(jié)點(diǎn)會將請求發(fā)放到ingest節(jié)點(diǎn)進(jìn)行預(yù)處理。
- 如果沒有配置pipeline或者在預(yù)處理結(jié)束后,相應(yīng)節(jié)點(diǎn)會根據(jù)routing規(guī)則決定該請求需要寫入到哪個(gè)分片(shard)(一般會根據(jù)_id值做hash。shard_id = hash(routing) % number_of_shard),并且請求分配到對應(yīng)節(jié)點(diǎn)。
- shard對應(yīng)節(jié)點(diǎn)處理寫入請求。
- 寫入請求完成后,向副本分片所在節(jié)點(diǎn)發(fā)出replication請求。
- 副本分片所在節(jié)點(diǎn)處理副本寫入請求。

數(shù)據(jù)搜索
對數(shù)據(jù)進(jìn)行搜索時(shí),會有不同于寫入時(shí)的處理流程。不會進(jìn)行pipeline處理。相關(guān)流程為:
- coordinator節(jié)點(diǎn)接收到查詢請求。
- 在本地找到需要查詢的分片后,coordinator節(jié)點(diǎn)將請求分配到相關(guān)shard所在節(jié)點(diǎn)。
- 各shard處理完搜索后,將結(jié)果返回給coordinator節(jié)點(diǎn)。
- coordinator節(jié)點(diǎn)會將各shard返回來的結(jié)果進(jìn)行合并。
- coordinator節(jié)點(diǎn)將合并好的結(jié)果返回給請求客戶端。

資源
可以看出coordinator節(jié)點(diǎn)負(fù)責(zé)處理外界的request,并將相應(yīng)的操作指派到相應(yīng)節(jié)點(diǎn)上去。在搜索或者聚合的情況下,還要合并從各個(gè)節(jié)點(diǎn)返回的結(jié)果,再將結(jié)果返回給客戶端。因此可以看出,coordinator節(jié)點(diǎn)對于網(wǎng)絡(luò)帶寬,CPU,和內(nèi)存的要求都較高。對于大規(guī)模集群來說,coordinator節(jié)點(diǎn)本身負(fù)載壓力就比較大,不適合在擔(dān)任其他角色。
ingest節(jié)點(diǎn)負(fù)責(zé)在文檔寫入之前對數(shù)據(jù)進(jìn)行預(yù)處理,功能比較單一,主要是CPU和內(nèi)存。
數(shù)據(jù)節(jié)點(diǎn)負(fù)責(zé)具體的寫入和搜索邏輯,對內(nèi)存要求比較高,數(shù)據(jù)節(jié)點(diǎn)的CPU能力決定了其可以承載的并發(fā)數(shù)。同時(shí)數(shù)據(jù)節(jié)點(diǎn)也承擔(dān)了數(shù)據(jù)的存儲職能,對磁盤IO有一定的要求。由于數(shù)據(jù)在做replication時(shí),會有n倍的流量在集群內(nèi)部備份節(jié)點(diǎn)之間發(fā)生,因此數(shù)據(jù)節(jié)點(diǎn)對網(wǎng)絡(luò)也有一定的要求,這取決于數(shù)據(jù)的副本數(shù)。
下表是各種角色的節(jié)點(diǎn)對資源的依賴情況下,僅代表個(gè)人觀點(diǎn)。
| 角色 | CPU | 內(nèi)存 | IO | 網(wǎng)絡(luò) |
|---|---|---|---|---|
| coordinator | 高 | 高 | 一般 | 高 |
| ingest | 一般 | 一般 | 一般 | 一般 |
| data | 一般 | 高 | 高 | 高 |
| master | 一般 | 取決于集群大小 | 一般 | 一般 |
對數(shù)據(jù)量進(jìn)行估算
在規(guī)劃ES集群之前,我們需要問自己幾個(gè)問題:
- 我的數(shù)據(jù)是什么樣的。
- 每天會有多少新增數(shù)據(jù)?
- 這些數(shù)據(jù)需要保留多少天?
- 給這些數(shù)據(jù)設(shè)置幾個(gè)副本?
- 原始數(shù)據(jù)在寫入ES后大小會有所變化,比如,要建立倒排索引。keyword字段和其他結(jié)構(gòu)化字段會構(gòu)建doc_values正排索引,這些都會導(dǎo)致落盤后的數(shù)據(jù)發(fā)生變化。
- 我的資源是怎么樣的
- 數(shù)據(jù)節(jié)點(diǎn)有多少堆內(nèi)存
- 我需要留出部分磁盤空間給系統(tǒng)和后臺,按5%計(jì)算。
- ES默認(rèn)的磁盤low.watermark是85%,超過該閾值后,將不允許向該節(jié)點(diǎn)分配shard。
- 考慮到系統(tǒng)的高可用,需要考慮在一個(gè)或多個(gè)節(jié)點(diǎn)下線的情況下,離線的分片可以得到重新分配。
獲得了這些數(shù)據(jù)后,我們就可以進(jìn)行計(jì)算了。
- 總數(shù)據(jù)量 = 每天新增原始數(shù)據(jù)量 * 保留天數(shù) * (副本數(shù)+1)*膨脹系數(shù)
- 磁盤空間 = 總數(shù)據(jù)量 / (1-15%-5%) = 總數(shù)據(jù)量 * 1.25
- 所需節(jié)點(diǎn)數(shù) = 磁盤空間 / (節(jié)點(diǎn)內(nèi)存/內(nèi)存磁盤比)
- 一個(gè)經(jīng)驗(yàn)數(shù)字是,使用SSD的Hot類型節(jié)點(diǎn)(內(nèi)存/磁盤)為1/30。使用HDD的Warm類型節(jié)點(diǎn)(內(nèi)存/磁盤)比為1/160。而用來歸檔,使用廉價(jià)硬盤存儲的Cold類型節(jié)點(diǎn)(內(nèi)存/磁盤)為1/1000。
- 一個(gè)經(jīng)驗(yàn)數(shù)字,所需的磁盤空間一般為原始數(shù)據(jù)大小的3.38倍。僅供參考,具體使用的磁盤空間。
使用多個(gè)類型(Hot,Warm,Cold)分片時(shí),需要按照每個(gè)階段分別進(jìn)行計(jì)算。
對分片數(shù)進(jìn)行估算
在進(jìn)行分片估計(jì)時(shí),我們需要先準(zhǔn)備以下數(shù)據(jù):
- 有多少個(gè)索引
- 每個(gè)索引設(shè)置多少個(gè)shard以及幾個(gè)replication
- 每個(gè)索引保留多長時(shí)間
- 每個(gè)數(shù)據(jù)節(jié)點(diǎn)有多少內(nèi)存
現(xiàn)在我們有以下一些經(jīng)驗(yàn)數(shù)據(jù):
- 每1GB堆內(nèi)存最多包含20個(gè)分片(每個(gè)分片50MB,是一個(gè)非常小的值,建議按情況適當(dāng)提高該數(shù)值)
- 每個(gè)分片不超過50GB。(一般30-40GB)
可以計(jì)算得到:
- 總分片數(shù) = 索引數(shù)量 * shard數(shù) * (副本數(shù) + 1)
- 所需節(jié)點(diǎn) = 總分片數(shù) / (節(jié)點(diǎn)堆內(nèi)存[GB]/每GB堆內(nèi)存承載分片數(shù) )
ES權(quán)威指南里提到了一種通過實(shí)踐的評估方式,可以參考:https://www.elastic.co/guide/cn/elasticsearch/guide/cn/capacity-planning.html
根據(jù)搜索量進(jìn)行估算
我們需要預(yù)先假設(shè)幾個(gè)數(shù)據(jù):
- 峰值每秒請求數(shù)。
- 平均請求延時(shí)。
我們預(yù)先知道每個(gè)節(jié)點(diǎn)的線程池大小,搜索線程池大小一般配為:
線程池大小 = int(核數(shù) * 3/2) + 1,該公式來源于https://www.elastic.co/guide/cn/elasticsearch/guide/current/dont-touch-these-settings.html
先計(jì)算:
- 每線程每秒處理請求數(shù) = 1s/平均請求延時(shí)
- 處理峰值請求所需的并發(fā)數(shù) = 峰值每秒請求數(shù)/每線程每秒處理請求數(shù)
- 所需節(jié)點(diǎn)數(shù) = 峰值并發(fā)數(shù)/每節(jié)點(diǎn)線程池大小。