我們知道Druid能夠同時提供對大數(shù)據(jù)集的實時攝入和高效復(fù)雜查詢的性能步势,主要原因就是它獨到的架構(gòu)設(shè)計和基于Datasource與Segment的數(shù)據(jù)存儲結(jié)構(gòu)。接下來我們會分別從數(shù)據(jù)存儲和系統(tǒng)節(jié)點架構(gòu)兩方面來深入了解一下Druid的架構(gòu)妈嘹。
數(shù)據(jù)存儲
Druid將數(shù)據(jù)組織成Read-Optimized的結(jié)構(gòu)柳琢,而這也是Druid能夠支持交互式查詢的關(guān)鍵。Druid中的數(shù)據(jù)存儲在被稱為datasource中润脸,類似RDMS中的table柬脸。每個datasource按照時間劃分,如果你有需求也可以進(jìn)一步按其它屬性劃分毙驯。每個時間范圍稱為一個chunk(比如你按天分區(qū)倒堕,則一個chunk為一天)。在chunk中數(shù)據(jù)由被分為一個或多個segment(segment是數(shù)據(jù)實際存儲結(jié)構(gòu)爆价,Datasource垦巴、Chunk只是一個邏輯概念),每個segment都是一個單獨的文件铭段,通常包含幾百萬行數(shù)據(jù)骤宣,這些segment是按照時間組織成的chunk,所以在按照時間查詢數(shù)據(jù)時序愚,效率非常高憔披。
數(shù)據(jù)分區(qū)
任何分布式存儲/計算系統(tǒng),都需要對數(shù)據(jù)進(jìn)行合理的分區(qū),從而實現(xiàn)存儲和計算的均衡芬膝,以及數(shù)據(jù)并行化望门。而Druid本身處理的是事件數(shù)據(jù),每條數(shù)據(jù)都會帶有一個時間戳蔗候,所以很自然的就可以使用時間進(jìn)行分區(qū)怒允。比如上圖埂软,我們指定了分區(qū)粒度為為天锈遥,那么每天的數(shù)據(jù)都會被單獨存儲和查詢(一個分區(qū)下有多個Segment的原因往下看)。
使用時間分區(qū)我們很容易會想到一個問題勘畔,就是很可能每個時間段的數(shù)據(jù)量是不均衡的(想一想我們的業(yè)務(wù)場景)所灸,而Duid為了解決這種問題,提供了“二級分區(qū)”炫七,每一個二級分區(qū)稱為一個Shard(這才是物理分區(qū))爬立。通過設(shè)置每個Shard的所能存儲的目標(biāo)值和Shard策略浑劳,來完成shard的分區(qū)塘慕。Druid目前支持兩種Shard策略:Hash(基于維值的Hash)和Range(基于某個維度的取值范圍)蒋院。上圖中坊夫,2000-01-01和2000-01-03的每個分區(qū)都是一個Shard鼎姊,由于2000-01-02的數(shù)據(jù)量比較多化漆,所以有兩個Shard棠绘。
Segment
Shard經(jīng)過持久化之后就稱為了Segment苟翻,Segment是數(shù)據(jù)存儲的止、復(fù)制檩坚、均衡(Historical的負(fù)載均衡)和計算的基本單元了。Segment具有不可變性诅福,一個Segment一旦創(chuàng)建完成后(MiddleManager節(jié)點發(fā)布后)就無法被修改匾委,只能通過生成一個新的Segment來代替舊版本的Segment。
Segment內(nèi)部存儲結(jié)構(gòu)
接下來我們可以看下Segment文件的內(nèi)部存儲結(jié)構(gòu)氓润。因為Druid采用列式存儲赂乐,所以每列數(shù)據(jù)都是在獨立的結(jié)構(gòu)中存儲(并不是獨立的文件,是獨立的數(shù)據(jù)結(jié)構(gòu)咖气,因為所有列都會存儲在一個文件中)挨措。Segment中的數(shù)據(jù)類型主要分為三種:時間戳、維度列和指標(biāo)列采章。
對于時間戳列和指標(biāo)列运嗜,實際存儲是一個數(shù)組,Druid采用LZ4壓縮每列的整數(shù)或浮點數(shù)悯舟。當(dāng)收到查詢請求后担租,會拉出所需的行數(shù)據(jù)(對于不需要的列不會拉出來),并且對其進(jìn)行解壓縮抵怎。解壓縮完之后奋救,在應(yīng)用具體的聚合函數(shù)岭参。
對于維度列不會像指標(biāo)列和時間戳這么簡單,因為它需要支持filter和group by尝艘,所以Druid使用了字典編碼(Dictionary Encoding)和位圖索引(Bitmap Index)來存儲每個維度列演侯。每個維度列需要三個數(shù)據(jù)結(jié)構(gòu):
- 需要一個字典數(shù)據(jù)結(jié)構(gòu),將維值(維度列值都會被認(rèn)為是字符串類型)映射成一個整數(shù)ID背亥。
- 使用上面的字典編碼秒际,將該列所有維值放在一個列表中。
- 對于列中不同的值狡汉,使用bitmap數(shù)據(jù)結(jié)構(gòu)標(biāo)識哪些行包含這些值娄徊。
Druid針對維度列之所以使用這三個數(shù)據(jù)結(jié)構(gòu),是因為:
- 使用字典將字符串映射成整數(shù)ID盾戴,可以緊湊的表示結(jié)構(gòu)2和結(jié)構(gòu)3中的值寄锐。
- 使用Bitmap位圖索引可以執(zhí)行快速過濾操作(找到符合條件的行號,以減少讀取的數(shù)據(jù)量)尖啡,因為Bitmap可以快速執(zhí)行AND和OR操作橄仆。
- 對于group by和TopN操作需要使用結(jié)構(gòu)2中的列值列表。
我們以上面"Page"維度列為例衅斩,可以具體看下Druid是如何使用這三種數(shù)據(jù)結(jié)構(gòu)存儲維度列:
1. 使用字典將列值映射為整數(shù)
{
"Justin Bieher":0,
"ke$ha":1
}
2. 使用1中的編碼盆顾,將列值放到一個列表中
[0,0,1,1]
3. 使用bitmap來標(biāo)識不同列值
value = 0: [1,1,0,0] //1代表該行含有該值,0標(biāo)識不含有
value = 1: [0,0,1,1]
下圖是以advertiser列為例矛渴,描述了advertiser列的實際存儲結(jié)構(gòu):
前兩種存儲結(jié)構(gòu)在最壞情況下會根據(jù)數(shù)據(jù)量增長而成線性增長(列數(shù)據(jù)中的每行都不相同)椎扬,而第三種由于使用Bitmap存儲(本身是一個稀疏矩陣),所以對它進(jìn)行壓縮具温,可以得到非巢系樱客觀的壓縮比。Druid而且運用了Roaring Bitmap(http://roaringbitmap.org/)能夠?qū)嚎s后的位圖直接進(jìn)行布爾運算铣猩,可以大大提高查詢效率和存儲效率(不需要解壓縮)揖铜。
Segment命名
高效的數(shù)據(jù)查詢,不僅僅體現(xiàn)在文件內(nèi)容的存儲結(jié)構(gòu)上达皿,還有一點很重要天吓,就是文件的命名上。試想一下峦椰,如果一個Datasource下有幾百萬個Segment文件龄寞,我們又如何快速找出我們所需要的文件呢?答案就是通過文件名稱快速索引查找汤功。
Segment的命名包含四部分:數(shù)據(jù)源(Datasource)物邑、時間間隔(包含開始時間和結(jié)束時間兩部分)、版本號和分區(qū)(Segment有分片的情況下才會有)。
test-datasource_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T16:00:00.000Z_1
數(shù)據(jù)源名稱_開始時間_結(jié)束時間_版本號_分區(qū)
分片號是從0開始色解,如果分區(qū)號為0茂嗓,則可以省略:test-datasource_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T16:00:00.000Z
還需要注意如果一個時間間隔segment由多個分片組成,則在查詢該segment的時候科阎,需要等到所有分片都被加載完成后述吸,才能夠查詢(除非使用線性分片規(guī)范(linear shard spec),允許在未加載完成時查詢)锣笨。
字段 | 是否必須 | 描述 |
---|---|---|
datasource | 是 | segment所在的Datasource |
開始時間 | 是 | 該Segment所存儲最早的數(shù)據(jù)蝌矛,時間格式是ISO 8601。開始時間和結(jié)束時間是通過segmentGranularity設(shè)置的時間間隔 |
結(jié)束時間 | 是 | 該segment所存儲最晚的數(shù)據(jù)票唆,時間格式是ISO 8601 |
版本號 | 是 | 因為Druid支持批量覆蓋操作朴读,當(dāng)批量攝入與之前相同數(shù)據(jù)源屹徘、相同時間間隔數(shù)據(jù)時走趋,數(shù)據(jù)就會被覆蓋,這時候版本號就會被更新噪伊。Druid系統(tǒng)的其它部分感知到這個信號后簿煌,就會把就舊數(shù)據(jù)刪除,使用新版本的數(shù)據(jù)(這個切換很快)鉴吹。版本號也是是用的ISO 8601時間戳姨伟,但是這個時間戳代表首次啟動的時間 |
分區(qū)號 | 否 | segment如果采用分區(qū),才會有該標(biāo)識 |
Segment物理存儲實例
下面我們以一個實例來看下Segment到底以什么形式存儲的豆励,我們以本地導(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}
我們以單機形式運行Druid,這樣Druid生成的Segment文件都在${DRUID_HOME}/var/druid/segments 目錄下良蒸。
segment通過datasource_beginTime_endTime_version_shard用于唯一標(biāo)識技扼,在實際存儲中是以目錄的形式表現(xiàn)的。
可以看到Segment中包含了Segment描述文件(descriptor.json)和壓縮后的索引數(shù)據(jù)文件(index.zip)嫩痰,我們主要看的也是index.zip這個文件剿吻,對其進(jìn)行解壓縮。
首先看下factory.json這個文件串纺,這個文件并不是segment具體存儲段數(shù)據(jù)的文件丽旅。因為Druid通過使用MMap(一種內(nèi)存映射文件的方式)的方式訪問Segment文件,通過查看這個文件內(nèi)容來看纺棺,貌似是用于MMap讀取文件所使用的(不太了解MMap)?
#factory.json文件內(nèi)容
{"type":"mMapSegmentFactory"}
Druid實際存儲Segment數(shù)據(jù)文件是:version.bin榄笙、meta.smoosh和xxxxx.smoosh這三個文件,下面分別看下這三個文件的內(nèi)容祷蝌。
version.bin是一個存儲了4個字節(jié)的二進(jìn)制文件茅撞,它是Segment內(nèi)部版本號(隨著Druid發(fā)展,Segment的格式也在發(fā)展),目前是V9乡翅,以Sublime打開該文件可以看到:
0000 0009
meta.smoosh里面存儲了關(guān)于其它smoosh文件(xxxxx.smoosh)的元數(shù)據(jù)鳞疲,里面記錄了每一列對應(yīng)文件和在文件的偏移量。除了列信息外蠕蚜,smoosh文件還包含了index.drd和metadata.drd尚洽,這部分是關(guān)于Segment的一些額外元數(shù)據(jù)信息。
#版本號,該文件所能存儲的最大值(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文件前靶累,我們先想一下為什么這個文件被命名為這種樣式腺毫?因為Druid為了最小化減少打開文件的句柄數(shù),它會將一個Segment的所有列數(shù)據(jù)都存儲在一個smoosh文件中挣柬,也就是xxxxx.smoosh這個文件潮酒。但是由于Druid使用MMap來讀取Segment文件,而MMap需要保證每個文件大小不能超過2G(Java中的MMapByteBuffer限制)邪蛔,所以當(dāng)一個smoosh文件大于2G時急黎,Druid會將新數(shù)據(jù)寫入到下一個smoosh文件中。這也就是為什么這些文件命名是這樣的侧到,這里也對應(yīng)上了meta文件中為什么還要標(biāo)識列所在的文件名勃教。
通過meta.smoosh的偏移量也能看出,00000.smoosh文件中數(shù)據(jù)是按列進(jìn)行存儲的匠抗,從上到下分別存儲的是時間列故源、指標(biāo)列、維度列汞贸。對于每列主要包會含兩部分信息:ColumnDescriptor和binary數(shù)據(jù)绳军。columnDescriptor是一個使用Jackson序列化的對象,它包含了該列的一些元數(shù)據(jù)信息矢腻,比如數(shù)據(jù)類型门驾、是否是多值等。而binary則是根據(jù)不同數(shù)據(jù)類型進(jìn)行壓縮存儲的二進(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中包含哪些度量、維度帐偎、時間范圍逐纬、以及使用哪種bitmap。metadata.drd中存儲了指標(biāo)聚合函數(shù)削樊、查詢粒度豁生、時間戳配置等(上面內(nèi)容的最后部分)兔毒。
下圖是物理存儲結(jié)構(gòu)圖,存儲未壓縮和編碼的數(shù)據(jù)就是最右邊的內(nèi)容甸箱。
Segment創(chuàng)建
Segment都是在MiddleManager節(jié)點中創(chuàng)建的育叁,并且處在MiddleManager中的Segment在狀態(tài)上都是可變的并且未提交的(提交到DeepStorage之后,數(shù)據(jù)就不可改變)芍殖。
Segment從在MiddleManager中創(chuàng)建到傳播到Historical中豪嗽,會經(jīng)歷以下幾個步驟:
- MiddleManager中創(chuàng)建Segment文件,并將其發(fā)布到Deep Storage豌骏。
- Segment相關(guān)的元數(shù)據(jù)信息被存儲到MetaStore中龟梦。
- Coordinator進(jìn)程根據(jù)MetaStore中得知Segment相關(guān)的元數(shù)據(jù)信息后,根據(jù)規(guī)則的設(shè)置分配給復(fù)合條件的Historical節(jié)點窃躲。
- Historical節(jié)點得到Coordinator指令后计贰,自動從DeepStorage中拉取Segment數(shù)據(jù)文件,并通過Zookeeper向集群聲明負(fù)責(zé)提供該Segment數(shù)據(jù)相關(guān)的查詢服務(wù)蒂窒。
- MiddleManager在得知Historical負(fù)責(zé)該Segment后躁倒,會丟棄該Segment文件,并向集群聲明不在負(fù)責(zé)該Segment相關(guān)的查詢刘绣。
如何配置分區(qū)
可以通過granularitySpec中的segmentGranularity設(shè)置segment的時間間隔(http://druid.io/docs/latest/ingestion/ingestion-spec.html#granularityspec)樱溉。為了保證Druid的查詢效率,每個Segment文件的大小建議在300MB~700MB之間纬凤。如果超過這個范圍,可以修改時間間隔或者使用分區(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é)點類型有五種:Overload完丽、MiddleManager恋技、Coordinator、Historical和Broker逻族。
Overload和MiddleManager主要負(fù)責(zé)數(shù)據(jù)攝入(對于沒有發(fā)布的Segment蜻底,MiddleManager也提供查詢服務(wù));Coordinator和Historical主要負(fù)責(zé)歷史數(shù)據(jù)的查詢聘鳞;Broker節(jié)點主要負(fù)責(zé)接收Client查詢請求薄辅,拆分子查詢給MiddleManager和Historical節(jié)點,然后合并查詢結(jié)果返回給Client抠璃。其中Overload是MiddleManager的master節(jié)點站楚,Coordinator是Historical的master節(jié)點。
索引服務(wù)
Druid提供一組支持索引服務(wù)(Indexing Service)的組件搏嗡,也就是Overload和MiddleManager節(jié)點窿春。索引服務(wù)是一種高可用的分布式服務(wù)拉一,用于運行跟索引相關(guān)的任務(wù),索引服務(wù)是數(shù)據(jù)攝入創(chuàng)建和銷毀Segment的主要方式(還有一種是采用實時節(jié)點的方式旧乞,但是現(xiàn)在已經(jīng)廢棄了)蔚润。索引服務(wù)支持以pull或push的方式攝入外部數(shù)據(jù)。
索引服務(wù)采用的是主從架構(gòu)尺栖,Overload為主節(jié)點抽碌,MiddleManager是從節(jié)點。索引服務(wù)架構(gòu)圖如下圖所示:
索引服務(wù)由三部分組件組成:用于執(zhí)行任務(wù)的Peon(勞工)組件决瞳、用于管理Peon的MiddleManager組件和分配任務(wù)給MiddleManager的Overload組件货徙。MiddleManager和Overload組件可以部署在相同節(jié)點也可以跨節(jié)點部署,但是Peon和MiddleManager是部署在同一個節(jié)點上的皮胡。
索引服務(wù)架構(gòu)和Yarn的架構(gòu)很像:
- Overlaod節(jié)點相當(dāng)于Yarn的ResourceManager痴颊,負(fù)責(zé)集群資源管理和任務(wù)分配。
- MiddleManager節(jié)點相當(dāng)于Yarn的NodeManager屡贺,負(fù)責(zé)接受任務(wù)和管理本節(jié)點的資源蠢棱。
- Peon節(jié)點相當(dāng)于Yarn的Container,執(zhí)行節(jié)點上具體的任務(wù)甩栈。
Overload節(jié)點
Overload作為索引服務(wù)的主節(jié)點泻仙,對外負(fù)責(zé)接受索引任務(wù),對內(nèi)負(fù)責(zé)將任務(wù)分解并下發(fā)給MiddleManager量没。Overload有兩種運行模式:
- 本地模式(Local Mode):默認(rèn)模式玉转。本地模式下的Overload不僅負(fù)責(zé)任務(wù)協(xié)調(diào)工作,還會負(fù)責(zé)啟動一些peon來完成具體的任務(wù)殴蹄。
- 遠(yuǎn)程模式(Remote Mode):該模式下究抓,Overload和MiddleManager運行在不同的節(jié)點上,它僅負(fù)責(zé)任務(wù)的協(xié)調(diào)工作袭灯,不負(fù)責(zé)完成具體的任務(wù)刺下。
Overload提供了一個UI客戶端,可以用于查看任務(wù)稽荧、運行任務(wù)和終止任務(wù)等橘茉。
http://<OVERLORD_IP>:<port>/console.html
Overload提供了RESETful的訪問形式,所以客戶端可以通過HTTP POST形式向請求節(jié)點提交任務(wù)姨丈。
http://<OVERLORD_IP>:<port>/druid/indexer/v1/task //提交任務(wù)
http://<OVERLORD_IP>:<port>/druid/indexer/v1/task/{task_id}/shutdown //殺死任務(wù)
MiddleManager節(jié)點
MiddleManager是執(zhí)行任務(wù)的工作節(jié)點畅卓,MiddleManager會將任務(wù)單獨發(fā)給每個單獨JVM運行的Peon(因為要把資源和日志進(jìn)行隔離),每個Peon一次只能運行一個任務(wù)构挤。
Peon節(jié)點
Peon在單個JVM中運行單個任務(wù)髓介,MiddleManager負(fù)責(zé)為任務(wù)創(chuàng)建Peon。
Coordinator節(jié)點
Coordinator是Historical的mater節(jié)點筋现,它主要負(fù)責(zé)管理和分發(fā)Segment唐础。具體工作就是:告知Historical加載或刪除Segment箱歧、管理Segment副本以及負(fù)載Segment在Historical上的均衡。
Coordinator是定期運行的一膨,并且運行間隔可以通過配置參數(shù)配置呀邢。每次Coordinator運行都會通過Zookeeper獲取當(dāng)前集群狀態(tài),通過評估集群狀態(tài)來采取適當(dāng)?shù)牟僮?比如均衡負(fù)載Segment)豹绪。Coordinator會連接數(shù)據(jù)庫(MetaStore)价淌,數(shù)據(jù)庫中存儲了Segment信息和規(guī)則(Rule)。Segment表中列出了需要加載到集群中的所有Segment瞒津,Coordinator每次運行都會從Segment表來拉取Segment列表并與當(dāng)前集群的Segment對比蝉衣,如果發(fā)現(xiàn)數(shù)據(jù)庫中不存在的Segment,但是在集群中還有巷蚪,就會把它從集群刪掉病毡;規(guī)則表定義了如何處理Segment,規(guī)則的作用就是我們可以通過配置一組規(guī)則屁柏,來操作集群加載Segment或刪除Segment啦膜。關(guān)于如何配置規(guī)則,可以查看:http://druid.io/docs/latest/operations/rule-configuration.html淌喻。
Historical節(jié)點加載Segment前僧家,會進(jìn)行容量排序,哪個Historical節(jié)點的Segment最少裸删,則它就具有最高的加載權(quán)八拱。Coordinator不會直接Historical節(jié)點通信,而是將Segment信息放到一個隊列中烁落,Historical節(jié)點去隊列取Segment描述信息乘粒,并且加載該Segment到本節(jié)點。
Coordinator提供了一UI界面伤塌,用于顯示集群信息和規(guī)則配置:
http://<COORDINATOR_IP>:<COORDINATOR_PORT>
Historical節(jié)點
Historical節(jié)點負(fù)責(zé)管理歷史Segment,Historical節(jié)點通過Zookeeper監(jiān)聽指定的路徑來發(fā)現(xiàn)是否有新的Segment需要加載(Coordinator通過分配算法指定具體的Historical)轧铁。
上面通過Coordinator知道每聪,當(dāng)有新的Segment需要加載的時候,Coordinator會將其放到一個隊列中齿风。當(dāng)Historical節(jié)點收到有新的Segment時候药薯,就會檢測本地cache和磁盤,查看是否有該Segment信息救斑。如果沒有Historical節(jié)點會從Zookeeper中拉取該Segment相關(guān)的信息童本,然后進(jìn)行下載。
Broker
Broker節(jié)點是負(fù)責(zé)轉(zhuǎn)發(fā)Client查詢請求的脸候,Broker通過zookeeper能夠知道哪個Segment在哪些節(jié)點上穷娱,Broker會將查詢轉(zhuǎn)發(fā)給相應(yīng)節(jié)點绑蔫。所有節(jié)點返回數(shù)據(jù)后,Broker會將所有節(jié)點的數(shù)據(jù)進(jìn)行合并泵额,然后返回給Client配深。
Broker會有一個LRU(高速緩存失效策略),來緩存每Segment的結(jié)果嫁盲。這個緩存可以是本地緩存篓叶,也可以借助外部緩存系統(tǒng)(比如memcached),第三方緩存可以在所有broker中共享Segment結(jié)果羞秤。當(dāng)Borker接收到查詢請求后缸托,會首先查看本地是否有對應(yīng)的查詢數(shù)據(jù),對于不存在的Segment數(shù)據(jù)瘾蛋,會將請求轉(zhuǎn)發(fā)給Historical節(jié)點俐镐。
Broker不會緩存實時數(shù)據(jù),因為實時數(shù)據(jù)處于不可靠狀態(tài)瘦黑。