我們知道Druid能夠同時(shí)提供對(duì)大數(shù)據(jù)集的實(shí)時(shí)攝入和高效復(fù)雜查詢的性能诱担,主要原因就是它獨(dú)到的架構(gòu)設(shè)計(jì)和基于Datasource與Segment的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)倾哺。接下來我們會(huì)分別從數(shù)據(jù)存儲(chǔ)和系統(tǒng)節(jié)點(diǎn)架構(gòu)兩方面來深入了解一下Druid的架構(gòu)揽咕。
數(shù)據(jù)存儲(chǔ)
Druid將數(shù)據(jù)組織成Read-Optimized的結(jié)構(gòu)蟋座,而這也是Druid能夠支持交互式查詢的關(guān)鍵褐澎。Druid中的數(shù)據(jù)存儲(chǔ)在被稱為datasource中,類似RDMS中的table烈评。每個(gè)datasource按照時(shí)間劃分火俄,如果你有需求也可以進(jìn)一步按其它屬性劃分。每個(gè)時(shí)間范圍稱為一個(gè)chunk(比如你按天分區(qū)讲冠,則一個(gè)chunk為一天)瓜客。在chunk中數(shù)據(jù)由被分為一個(gè)或多個(gè)segment(segment是數(shù)據(jù)實(shí)際存儲(chǔ)結(jié)構(gòu),Datasource竿开、Chunk只是一個(gè)邏輯概念)谱仪,每個(gè)segment都是一個(gè)單獨(dú)的文件,通常包含幾百萬行數(shù)據(jù)否彩,這些segment是按照時(shí)間組織成的chunk芽卿,所以在按照時(shí)間查詢數(shù)據(jù)時(shí),效率非常高胳搞。
數(shù)據(jù)分區(qū)
任何分布式存儲(chǔ)/計(jì)算系統(tǒng),都需要對(duì)數(shù)據(jù)進(jìn)行合理的分區(qū)称杨,從而實(shí)現(xiàn)存儲(chǔ)和計(jì)算的均衡肌毅,以及數(shù)據(jù)并行化。而Druid本身處理的是事件數(shù)據(jù)姑原,每條數(shù)據(jù)都會(huì)帶有一個(gè)時(shí)間戳悬而,所以很自然的就可以使用時(shí)間進(jìn)行分區(qū)。比如上圖锭汛,我們指定了分區(qū)粒度為為天笨奠,那么每天的數(shù)據(jù)都會(huì)被單獨(dú)存儲(chǔ)和查詢(一個(gè)分區(qū)下有多個(gè)Segment的原因往下看)。
使用時(shí)間分區(qū)我們很容易會(huì)想到一個(gè)問題唤殴,就是很可能每個(gè)時(shí)間段的數(shù)據(jù)量是不均衡的(想一想我們的業(yè)務(wù)場(chǎng)景)般婆,而Duid為了解決這種問題,提供了“二級(jí)分區(qū)”朵逝,每一個(gè)二級(jí)分區(qū)稱為一個(gè)Shard(這才是物理分區(qū))蔚袍。通過設(shè)置每個(gè)Shard的所能存儲(chǔ)的目標(biāo)值和Shard策略,來完成shard的分區(qū)。Druid目前支持兩種Shard策略:Hash(基于維值的Hash)和Range(基于某個(gè)維度的取值范圍)啤咽。上圖中晋辆,2000-01-01和2000-01-03的每個(gè)分區(qū)都是一個(gè)Shard,由于2000-01-02的數(shù)據(jù)量比較多宇整,所以有兩個(gè)Shard瓶佳。
Segment
Shard經(jīng)過持久化之后就稱為了Segment,Segment是數(shù)據(jù)存儲(chǔ)鳞青、復(fù)制霸饲、均衡(Historical的負(fù)載均衡)和計(jì)算的基本單元了。Segment具有不可變性盼玄,一個(gè)Segment一旦創(chuàng)建完成后(MiddleManager節(jié)點(diǎn)發(fā)布后)就無法被修改贴彼,只能通過生成一個(gè)新的Segment來代替舊版本的Segment。
Segment內(nèi)部存儲(chǔ)結(jié)構(gòu)
接下來我們可以看下Segment文件的內(nèi)部存儲(chǔ)結(jié)構(gòu)埃儿。因?yàn)镈ruid采用列式存儲(chǔ)器仗,所以每列數(shù)據(jù)都是在獨(dú)立的結(jié)構(gòu)中存儲(chǔ)(并不是獨(dú)立的文件,是獨(dú)立的數(shù)據(jù)結(jié)構(gòu)童番,因?yàn)樗辛卸紩?huì)存儲(chǔ)在一個(gè)文件中)精钮。Segment中的數(shù)據(jù)類型主要分為三種:時(shí)間戳、維度列和指標(biāo)列剃斧。
對(duì)于時(shí)間戳列和指標(biāo)列轨香,實(shí)際存儲(chǔ)是一個(gè)數(shù)組,Druid采用LZ4壓縮每列的整數(shù)或浮點(diǎn)數(shù)幼东。當(dāng)收到查詢請(qǐng)求后臂容,會(huì)拉出所需的行數(shù)據(jù)(對(duì)于不需要的列不會(huì)拉出來),并且對(duì)其進(jìn)行解壓縮根蟹。解壓縮完之后脓杉,在應(yīng)用具體的聚合函數(shù)。
對(duì)于維度列不會(huì)像指標(biāo)列和時(shí)間戳這么簡(jiǎn)單简逮,因?yàn)樗枰С謋ilter和group by球散,所以Druid使用了字典編碼(Dictionary Encoding)和位圖索引(Bitmap Index)來存儲(chǔ)每個(gè)維度列。每個(gè)維度列需要三個(gè)數(shù)據(jù)結(jié)構(gòu):
- 需要一個(gè)字典數(shù)據(jù)結(jié)構(gòu)散庶,將維值(維度列值都會(huì)被認(rèn)為是字符串類型)映射成一個(gè)整數(shù)ID蕉堰。
- 使用上面的字典編碼,將該列所有維值放在一個(gè)列表中悲龟。
- 對(duì)于列中不同的值屋讶,使用bitmap數(shù)據(jù)結(jié)構(gòu)標(biāo)識(shí)哪些行包含這些值。
Druid針對(duì)維度列之所以使用這三個(gè)數(shù)據(jù)結(jié)構(gòu)须教,是因?yàn)椋?/p>
- 使用字典將字符串映射成整數(shù)ID丑婿,可以緊湊的表示結(jié)構(gòu)2和結(jié)構(gòu)3中的值。
- 使用Bitmap位圖索引可以執(zhí)行快速過濾操作(找到符合條件的行號(hào),以減少讀取的數(shù)據(jù)量)羹奉,因?yàn)锽itmap可以快速執(zhí)行AND和OR操作秒旋。
- 對(duì)于group by和TopN操作需要使用結(jié)構(gòu)2中的列值列表。
我們以上面"Page"維度列為例诀拭,可以具體看下Druid是如何使用這三種數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)維度列:
1. 使用字典將列值映射為整數(shù)
{
"Justin Bieher":0,
"ke$ha":1
}
2. 使用1中的編碼迁筛,將列值放到一個(gè)列表中
[0,0,1,1]
3. 使用bitmap來標(biāo)識(shí)不同列值
value = 0: [1,1,0,0] //1代表該行含有該值,0標(biāo)識(shí)不含有
value = 1: [0,0,1,1]
下圖是以advertiser列為例耕挨,描述了advertiser列的實(shí)際存儲(chǔ)結(jié)構(gòu):
前兩種存儲(chǔ)結(jié)構(gòu)在最壞情況下會(huì)根據(jù)數(shù)據(jù)量增長(zhǎng)而成線性增長(zhǎng)(列數(shù)據(jù)中的每行都不相同)细卧,而第三種由于使用Bitmap存儲(chǔ)(本身是一個(gè)稀疏矩陣),所以對(duì)它進(jìn)行壓縮筒占,可以得到非程懊恚客觀的壓縮比。Druid而且運(yùn)用了Roaring Bitmap(http://roaringbitmap.org/)能夠?qū)嚎s后的位圖直接進(jìn)行布爾運(yùn)算翰苫,可以大大提高查詢效率和存儲(chǔ)效率(不需要解壓縮)止邮。
Segment命名
高效的數(shù)據(jù)查詢,不僅僅體現(xiàn)在文件內(nèi)容的存儲(chǔ)結(jié)構(gòu)上奏窑,還有一點(diǎn)很重要导披,就是文件的命名上。試想一下,如果一個(gè)Datasource下有幾百萬個(gè)Segment文件,我們又如何快速找出我們所需要的文件呢拓颓?答案就是通過文件名稱快速索引查找。
Segment的命名包含四部分:數(shù)據(jù)源(Datasource)令境、時(shí)間間隔(包含開始時(shí)間和結(jié)束時(shí)間兩部分)、版本號(hào)和分區(qū)(Segment有分片的情況下才會(huì)有)。
test-datasource_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T16:00:00.000Z_1
數(shù)據(jù)源名稱_開始時(shí)間_結(jié)束時(shí)間_版本號(hào)_分區(qū)
分片號(hào)是從0開始,如果分區(qū)號(hào)為0扁凛,則可以省略:test-datasource_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T16:00:00.000Z
還需要注意如果一個(gè)時(shí)間間隔segment由多個(gè)分片組成,則在查詢?cè)搒egment的時(shí)候棚潦,需要等到所有分片都被加載完成后,才能夠查詢(除非使用線性分片規(guī)范(linear shard spec)膝昆,允許在未加載完成時(shí)查詢)丸边。
字段 | 是否必須 | 描述 |
---|---|---|
datasource | 是 | segment所在的Datasource |
開始時(shí)間 | 是 | 該Segment所存儲(chǔ)最早的數(shù)據(jù),時(shí)間格式是ISO 8601荚孵。開始時(shí)間和結(jié)束時(shí)間是通過segmentGranularity設(shè)置的時(shí)間間隔 |
結(jié)束時(shí)間 | 是 | 該segment所存儲(chǔ)最晚的數(shù)據(jù)妹窖,時(shí)間格式是ISO 8601 |
版本號(hào) | 是 | 因?yàn)镈ruid支持批量覆蓋操作,當(dāng)批量攝入與之前相同數(shù)據(jù)源收叶、相同時(shí)間間隔數(shù)據(jù)時(shí)骄呼,數(shù)據(jù)就會(huì)被覆蓋,這時(shí)候版本號(hào)就會(huì)被更新。Druid系統(tǒng)的其它部分感知到這個(gè)信號(hào)后蜓萄,就會(huì)把就舊數(shù)據(jù)刪除隅茎,使用新版本的數(shù)據(jù)(這個(gè)切換很快)。版本號(hào)也是是用的ISO 8601時(shí)間戳嫉沽,但是這個(gè)時(shí)間戳代表首次啟動(dòng)的時(shí)間 |
分區(qū)號(hào) | 否 | segment如果采用分區(qū)辟犀,才會(huì)有該標(biāo)識(shí) |
Segment物理存儲(chǔ)實(shí)例
下面我們以一個(gè)實(shí)例來看下Segment到底以什么形式存儲(chǔ)的,我們以本地導(dǎo)入方式將下面數(shù)據(jù)導(dǎo)入到Druid中绸硕。
{"time": "2018-11-01T00:47:29.913Z","city": "beijing","sex": "man","gmv": 20000}
{"time": "2018-11-01T00:47:33.004Z","city": "beijing","sex": "woman","gmv": 50000}
{"time": "2018-11-01T00:50:33.004Z","city": "shanghai","sex": "man","gmv": 10000}
我們以單機(jī)形式運(yùn)行Druid堂竟,這樣Druid生成的Segment文件都在${DRUID_HOME}/var/druid/segments 目錄下。
segment通過datasource_beginTime_endTime_version_shard用于唯一標(biāo)識(shí)玻佩,在實(shí)際存儲(chǔ)中是以目錄的形式表現(xiàn)的出嘹。
可以看到Segment中包含了Segment描述文件(descriptor.json)和壓縮后的索引數(shù)據(jù)文件(index.zip),我們主要看的也是index.zip這個(gè)文件咬崔,對(duì)其進(jìn)行解壓縮税稼。
首先看下factory.json這個(gè)文件,這個(gè)文件并不是segment具體存儲(chǔ)段數(shù)據(jù)的文件刁赦。因?yàn)镈ruid通過使用MMap(一種內(nèi)存映射文件的方式)的方式訪問Segment文件娶聘,通過查看這個(gè)文件內(nèi)容來看,貌似是用于MMap讀取文件所使用的(不太了解MMap)?
#factory.json文件內(nèi)容
{"type":"mMapSegmentFactory"}
Druid實(shí)際存儲(chǔ)Segment數(shù)據(jù)文件是:version.bin甚脉、meta.smoosh和xxxxx.smoosh這三個(gè)文件丸升,下面分別看下這三個(gè)文件的內(nèi)容。
version.bin是一個(gè)存儲(chǔ)了4個(gè)字節(jié)的二進(jìn)制文件牺氨,它是Segment內(nèi)部版本號(hào)(隨著Druid發(fā)展狡耻,Segment的格式也在發(fā)展),目前是V9猴凹,以Sublime打開該文件可以看到:
0000 0009
meta.smoosh里面存儲(chǔ)了關(guān)于其它smoosh文件(xxxxx.smoosh)的元數(shù)據(jù)夷狰,里面記錄了每一列對(duì)應(yīng)文件和在文件的偏移量。除了列信息外郊霎,smoosh文件還包含了index.drd和metadata.drd沼头,這部分是關(guān)于Segment的一些額外元數(shù)據(jù)信息。
#版本號(hào),該文件所能存儲(chǔ)的最大值(2G),smooth文件數(shù)
v1,2147483647,1
# 列名,文件名,起始偏移量,結(jié)束偏移量
__time,0,0,154
city,0,306,577
gmv,0,154,306
index.drd,0,841,956
metadata.drd,0,956,1175
sex,0,577,841
再看00000.smoosh文件前书劝,我們先想一下為什么這個(gè)文件被命名為這種樣式进倍?因?yàn)镈ruid為了最小化減少打開文件的句柄數(shù),它會(huì)將一個(gè)Segment的所有列數(shù)據(jù)都存儲(chǔ)在一個(gè)smoosh文件中购对,也就是xxxxx.smoosh這個(gè)文件猾昆。但是由于Druid使用MMap來讀取Segment文件,而MMap需要保證每個(gè)文件大小不能超過2G(Java中的MMapByteBuffer限制)骡苞,所以當(dāng)一個(gè)smoosh文件大于2G時(shí)垂蜗,Druid會(huì)將新數(shù)據(jù)寫入到下一個(gè)smoosh文件中楷扬。這也就是為什么這些文件命名是這樣的,這里也對(duì)應(yīng)上了meta文件中為什么還要標(biāo)識(shí)列所在的文件名贴见。
通過meta.smoosh的偏移量也能看出烘苹,00000.smoosh文件中數(shù)據(jù)是按列進(jìn)行存儲(chǔ)的,從上到下分別存儲(chǔ)的是時(shí)間列蝇刀、指標(biāo)列螟加、維度列。對(duì)于每列主要包會(huì)含兩部分信息:ColumnDescriptor和binary數(shù)據(jù)吞琐。columnDescriptor是一個(gè)使用Jackson序列化的對(duì)象捆探,它包含了該列的一些元數(shù)據(jù)信息,比如數(shù)據(jù)類型站粟、是否是多值等黍图。而binary則是根據(jù)不同數(shù)據(jù)類型進(jìn)行壓縮存儲(chǔ)的二進(jìn)制數(shù)據(jù)。
^@^@^@d{"valueType":"LONG","hasMultipleValues":false,"parts":[{"type":"long","byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^C^@^@ ^@^A^A^@^@^@^@"^@^@^@^A^@^@^@^Z^@^@^@^@¢yL?ìf^A^@^@<8c>X^H^@<80>?^Wàìf^A^@^@^@^@^@d{"valueType":"LONG","hasMultipleValues":false,"parts":[{"type":"long","byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^C^@^@ ^@^A^A^@^@^@^@ ^@^@^@^A^@^@^@^X^@^@^@^@1 N^@^A^@"P?^H^@<80>^P'^@^@^@^@^@^@^@^@^@<9a>{"valueType":"STRING","hasMultipleValues":false,"parts":[{"type":"stringDictionary","bitmapSerdeFactory":{"type":"concise"},"byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^@^A^A^@^@^@#^@^@^@^B^@^@^@^K^@^@^@^W^@^@^@^@beijing^@^@^@^@shanghai^B^A^@^@^@^C^@^A^@^@^A^A^@^@^@^@^P^@^@^@^A^@^@^@^H^@^@^@^@0^@^@^A^A^@^@^@^@^\^@^@^@^B^@^@^@^H^@^@^@^P^@^@^@^@<80>^@^@^C^@^@^@^@<80>^@^@^D^@^@^@<9a>{"valueType":"STRING","hasMultipleValues":false,"parts":[{"type":"stringDictionary","bitmapSerdeFactory":{"type":"concise"},"byteOrder":"LITTLE_ENDIAN"}]}^B^@^@^@^@^A^A^@^@^@^\^@^@^@^B^@^@^@^G^@^@^@^P^@^@^@^@man^@^@^@^@woman^B^A^@^@^@^C^@^A^@^@^A^A^@^@^@^@^P^@^@^@^A^@^@^@^H^@^@^@^@0^@^A^@^A^@^@^@^@^\^@^@^@^B^@^@^@^H^@^@^@^P^@^@^@^@<80>^@^@^E^@^@^@^@<80>^@^@^B^A^@^@^@^@&^@^@^@^C^@^@^@^G^@^@^@^O^@^@^@^V^@^@^@^@gmv^@^@^@^@city^@^@^@^@sex^A^A^@^@^@^[^@^@^@^B^@^@^@^H^@^@^@^O^@^@^@^@city^@^@^@^@sex^@^@^Afì<91>D^@^@^@^Af??,^@^@^@^@^R{"type":"concise"}{"container":{},"aggregators":[{"type":"longSum","name":"gmv","fieldName":"gmv","expression":null}],"timestampSpec":{"column":"time","format":"auto","missingValue":null},"queryGranularity":{"type":"none"},"rollup":true}
smooth文件中的binary數(shù)據(jù)經(jīng)過LZ4或Bitmap壓縮奴烙,所以無法看到數(shù)據(jù)原始內(nèi)容助被。
在smooth文件最后還包含了兩部分?jǐn)?shù)據(jù),分別是index.drd和metadata.drd切诀。其中index.drd中包含了Segment中包含哪些度量揩环、維度、時(shí)間范圍幅虑、以及使用哪種bitmap丰滑。metadata.drd中存儲(chǔ)了指標(biāo)聚合函數(shù)、查詢粒度倒庵、時(shí)間戳配置等(上面內(nèi)容的最后部分)褒墨。
下圖是物理存儲(chǔ)結(jié)構(gòu)圖,存儲(chǔ)未壓縮和編碼的數(shù)據(jù)就是最右邊的內(nèi)容擎宝。
Segment創(chuàng)建
Segment都是在MiddleManager節(jié)點(diǎn)中創(chuàng)建的郁妈,并且處在MiddleManager中的Segment在狀態(tài)上都是可變的并且未提交的(提交到DeepStorage之后,數(shù)據(jù)就不可改變)绍申。
Segment從在MiddleManager中創(chuàng)建到傳播到Historical中噩咪,會(huì)經(jīng)歷以下幾個(gè)步驟:
- MiddleManager中創(chuàng)建Segment文件,并將其發(fā)布到Deep Storage极阅。
- Segment相關(guān)的元數(shù)據(jù)信息被存儲(chǔ)到MetaStore中胃碾。
- Coordinator進(jìn)程根據(jù)MetaStore中得知Segment相關(guān)的元數(shù)據(jù)信息后,根據(jù)規(guī)則的設(shè)置分配給復(fù)合條件的Historical節(jié)點(diǎn)涂屁。
- Historical節(jié)點(diǎn)得到Coordinator指令后书在,自動(dòng)從DeepStorage中拉取Segment數(shù)據(jù)文件灰伟,并通過Zookeeper向集群聲明負(fù)責(zé)提供該Segment數(shù)據(jù)相關(guān)的查詢服務(wù)拆又。
- MiddleManager在得知Historical負(fù)責(zé)該Segment后儒旬,會(huì)丟棄該Segment文件,并向集群聲明不在負(fù)責(zé)該Segment相關(guān)的查詢帖族。
如何配置分區(qū)
可以通過granularitySpec中的segmentGranularity設(shè)置segment的時(shí)間間隔(http://druid.io/docs/latest/ingestion/ingestion-spec.html#granularityspec)栈源。為了保證Druid的查詢效率,每個(gè)Segment文件的大小建議在300MB~700MB之間竖般。如果超過這個(gè)范圍甚垦,可以修改時(shí)間間隔或者使用分區(qū)來進(jìn)行優(yōu)化(配置partitioningSpec中的targetPartitionSize,官方建議設(shè)置500萬行以上涣雕;http://druid.io/docs/latest/ingestion/hadoop.html#partitioning-specification)艰亮。
系統(tǒng)架構(gòu)詳解
我們知道Druid節(jié)點(diǎn)類型有五種:Overload、MiddleManager挣郭、Coordinator迄埃、Historical和Broker。
Overload和MiddleManager主要負(fù)責(zé)數(shù)據(jù)攝入(對(duì)于沒有發(fā)布的Segment兑障,MiddleManager也提供查詢服務(wù))侄非;Coordinator和Historical主要負(fù)責(zé)歷史數(shù)據(jù)的查詢;Broker節(jié)點(diǎn)主要負(fù)責(zé)接收Client查詢請(qǐng)求流译,拆分子查詢給MiddleManager和Historical節(jié)點(diǎn)逞怨,然后合并查詢結(jié)果返回給Client。其中Overload是MiddleManager的master節(jié)點(diǎn)福澡,Coordinator是Historical的master節(jié)點(diǎn)叠赦。
索引服務(wù)
Druid提供一組支持索引服務(wù)(Indexing Service)的組件,也就是Overload和MiddleManager節(jié)點(diǎn)竞漾。索引服務(wù)是一種高可用的分布式服務(wù)眯搭,用于運(yùn)行跟索引相關(guān)的任務(wù),索引服務(wù)是數(shù)據(jù)攝入創(chuàng)建和銷毀Segment的主要方式(還有一種是采用實(shí)時(shí)節(jié)點(diǎn)的方式业岁,但是現(xiàn)在已經(jīng)廢棄了)鳞仙。索引服務(wù)支持以pull或push的方式攝入外部數(shù)據(jù)。
索引服務(wù)采用的是主從架構(gòu)笔时,Overload為主節(jié)點(diǎn)棍好,MiddleManager是從節(jié)點(diǎn)。索引服務(wù)架構(gòu)圖如下圖所示:
索引服務(wù)由三部分組件組成:用于執(zhí)行任務(wù)的Peon(勞工)組件允耿、用于管理Peon的MiddleManager組件和分配任務(wù)給MiddleManager的Overload組件借笙。MiddleManager和Overload組件可以部署在相同節(jié)點(diǎn)也可以跨節(jié)點(diǎn)部署,但是Peon和MiddleManager是部署在同一個(gè)節(jié)點(diǎn)上的较锡。
索引服務(wù)架構(gòu)和Yarn的架構(gòu)很像:
- Overlaod節(jié)點(diǎn)相當(dāng)于Yarn的ResourceManager业稼,負(fù)責(zé)集群資源管理和任務(wù)分配。
- MiddleManager節(jié)點(diǎn)相當(dāng)于Yarn的NodeManager蚂蕴,負(fù)責(zé)接受任務(wù)和管理本節(jié)點(diǎn)的資源低散。
- Peon節(jié)點(diǎn)相當(dāng)于Yarn的Container俯邓,執(zhí)行節(jié)點(diǎn)上具體的任務(wù)。
Overload節(jié)點(diǎn)
Overload作為索引服務(wù)的主節(jié)點(diǎn)熔号,對(duì)外負(fù)責(zé)接受索引任務(wù)稽鞭,對(duì)內(nèi)負(fù)責(zé)將任務(wù)分解并下發(fā)給MiddleManager。Overload有兩種運(yùn)行模式:
- 本地模式(Local Mode):默認(rèn)模式引镊。本地模式下的Overload不僅負(fù)責(zé)任務(wù)協(xié)調(diào)工作朦蕴,還會(huì)負(fù)責(zé)啟動(dòng)一些peon來完成具體的任務(wù)。
- 遠(yuǎn)程模式(Remote Mode):該模式下弟头,Overload和MiddleManager運(yùn)行在不同的節(jié)點(diǎn)上吩抓,它僅負(fù)責(zé)任務(wù)的協(xié)調(diào)工作,不負(fù)責(zé)完成具體的任務(wù)赴恨。
Overload提供了一個(gè)UI客戶端琴拧,可以用于查看任務(wù)、運(yùn)行任務(wù)和終止任務(wù)等嘱支。
http://<OVERLORD_IP>:<port>/console.html
Overload提供了RESETful的訪問形式蚓胸,所以客戶端可以通過HTTP POST形式向請(qǐng)求節(jié)點(diǎn)提交任務(wù)。
http://<OVERLORD_IP>:<port>/druid/indexer/v1/task //提交任務(wù)
http://<OVERLORD_IP>:<port>/druid/indexer/v1/task/{task_id}/shutdown //殺死任務(wù)
MiddleManager節(jié)點(diǎn)
MiddleManager是執(zhí)行任務(wù)的工作節(jié)點(diǎn)除师,MiddleManager會(huì)將任務(wù)單獨(dú)發(fā)給每個(gè)單獨(dú)JVM運(yùn)行的Peon(因?yàn)橐奄Y源和日志進(jìn)行隔離)沛膳,每個(gè)Peon一次只能運(yùn)行一個(gè)任務(wù)。
Peon節(jié)點(diǎn)
Peon在單個(gè)JVM中運(yùn)行單個(gè)任務(wù)汛聚,MiddleManager負(fù)責(zé)為任務(wù)創(chuàng)建Peon锹安。
Coordinator節(jié)點(diǎn)
Coordinator是Historical的mater節(jié)點(diǎn),它主要負(fù)責(zé)管理和分發(fā)Segment倚舀。具體工作就是:告知Historical加載或刪除Segment叹哭、管理Segment副本以及負(fù)載Segment在Historical上的均衡。
Coordinator是定期運(yùn)行的痕貌,并且運(yùn)行間隔可以通過配置參數(shù)配置风罩。每次Coordinator運(yùn)行都會(huì)通過Zookeeper獲取當(dāng)前集群狀態(tài),通過評(píng)估集群狀態(tài)來采取適當(dāng)?shù)牟僮?比如均衡負(fù)載Segment)舵稠。Coordinator會(huì)連接數(shù)據(jù)庫(kù)(MetaStore)超升,數(shù)據(jù)庫(kù)中存儲(chǔ)了Segment信息和規(guī)則(Rule)。Segment表中列出了需要加載到集群中的所有Segment哺徊,Coordinator每次運(yùn)行都會(huì)從Segment表來拉取Segment列表并與當(dāng)前集群的Segment對(duì)比室琢,如果發(fā)現(xiàn)數(shù)據(jù)庫(kù)中不存在的Segment,但是在集群中還有落追,就會(huì)把它從集群刪掉盈滴;規(guī)則表定義了如何處理Segment,規(guī)則的作用就是我們可以通過配置一組規(guī)則轿钠,來操作集群加載Segment或刪除Segment巢钓。關(guān)于如何配置規(guī)則宽菜,可以查看:http://druid.io/docs/latest/operations/rule-configuration.html。
Historical節(jié)點(diǎn)加載Segment前竿报,會(huì)進(jìn)行容量排序,哪個(gè)Historical節(jié)點(diǎn)的Segment最少继谚,則它就具有最高的加載權(quán)烈菌。Coordinator不會(huì)直接Historical節(jié)點(diǎn)通信,而是將Segment信息放到一個(gè)隊(duì)列中花履,Historical節(jié)點(diǎn)去隊(duì)列取Segment描述信息芽世,并且加載該Segment到本節(jié)點(diǎn)。
Coordinator提供了一UI界面诡壁,用于顯示集群信息和規(guī)則配置:
http://<COORDINATOR_IP>:<COORDINATOR_PORT>
Historical節(jié)點(diǎn)
Historical節(jié)點(diǎn)負(fù)責(zé)管理歷史Segment济瓢,Historical節(jié)點(diǎn)通過Zookeeper監(jiān)聽指定的路徑來發(fā)現(xiàn)是否有新的Segment需要加載(Coordinator通過分配算法指定具體的Historical)。
上面通過Coordinator知道妹卿,當(dāng)有新的Segment需要加載的時(shí)候旺矾,Coordinator會(huì)將其放到一個(gè)隊(duì)列中。當(dāng)Historical節(jié)點(diǎn)收到有新的Segment時(shí)候夺克,就會(huì)檢測(cè)本地cache和磁盤箕宙,查看是否有該Segment信息。如果沒有Historical節(jié)點(diǎn)會(huì)從Zookeeper中拉取該Segment相關(guān)的信息铺纽,然后進(jìn)行下載柬帕。
Broker
Broker節(jié)點(diǎn)是負(fù)責(zé)轉(zhuǎn)發(fā)Client查詢請(qǐng)求的,Broker通過zookeeper能夠知道哪個(gè)Segment在哪些節(jié)點(diǎn)上狡门,Broker會(huì)將查詢轉(zhuǎn)發(fā)給相應(yīng)節(jié)點(diǎn)陷寝。所有節(jié)點(diǎn)返回?cái)?shù)據(jù)后,Broker會(huì)將所有節(jié)點(diǎn)的數(shù)據(jù)進(jìn)行合并其馏,然后返回給Client凤跑。
Broker會(huì)有一個(gè)LRU(高速緩存失效策略),來緩存每Segment的結(jié)果叛复。這個(gè)緩存可以是本地緩存饶火,也可以借助外部緩存系統(tǒng)(比如memcached),第三方緩存可以在所有broker中共享Segment結(jié)果致扯。當(dāng)Borker接收到查詢請(qǐng)求后肤寝,會(huì)首先查看本地是否有對(duì)應(yīng)的查詢數(shù)據(jù),對(duì)于不存在的Segment數(shù)據(jù)抖僵,會(huì)將請(qǐng)求轉(zhuǎn)發(fā)給Historical節(jié)點(diǎn)鲤看。