理“Druid 元數(shù)據(jù)”之亂
作者 | vivo 互聯(lián)網(wǎng)大數(shù)據(jù)團(tuán)隊(duì)-Zheng Xiaofeng
一、背景
Druid 是一個(gè)專(zhuān)為大型數(shù)據(jù)集上的高性能切片和 OLAP 分析而設(shè)計(jì)的數(shù)據(jù)存儲(chǔ)系統(tǒng)。
由于Druid 能夠同時(shí)提供離線和實(shí)時(shí)數(shù)據(jù)的查詢(xún),因此Druid最常用作為GUI分析、業(yè)務(wù)監(jiān)控、實(shí)時(shí)數(shù)倉(cāng)的數(shù)據(jù)存儲(chǔ)系統(tǒng)。
此外Druid擁有一個(gè)多進(jìn)程,分布式架構(gòu),每個(gè)Druid組件類(lèi)型都可以獨(dú)立配置和擴(kuò)展,為集群提供最大的靈活性。
由于Druid架構(gòu)設(shè)計(jì)和數(shù)據(jù)(離線,實(shí)時(shí))的特殊性,導(dǎo)致Druid元數(shù)據(jù)管理邏輯比較復(fù)雜,主要體現(xiàn)在Druid具有眾多的元數(shù)據(jù)存儲(chǔ)介質(zhì)以及眾多不同類(lèi)型組件之間元數(shù)據(jù)傳輸邏輯上。
本文的目的是通過(guò)梳理 Druid 元數(shù)據(jù)管理這個(gè)側(cè)面從而進(jìn)一步了解 Druid 內(nèi)部的運(yùn)行機(jī)制。
二、 Druid 元數(shù)據(jù)相關(guān)概念
2.1 Segment
Segment 是Druid管理數(shù)據(jù)的最基本單元,一個(gè)Datasource包含多個(gè)Segment,每個(gè)Segment保存著Datasource某個(gè)時(shí)間段的數(shù)據(jù),這個(gè)特定時(shí)間段的數(shù)據(jù)組織方式是通過(guò)Segment的payload(json)來(lái)定義的,payload內(nèi)部定義了某個(gè)Segment的維度,指標(biāo)等信息。
同一個(gè)Datasource的不同Segment的payload信息(維度、指標(biāo))可以不相同,Segment信息主要包含下面幾部分:
【時(shí)間段(Interval)】:用于描述數(shù)據(jù)的開(kāi)始時(shí)間和結(jié)束時(shí)間。
【DataSource】: 用字符串表示,指定segment隸屬于哪個(gè)Datasource。
【版本(Version)】:用一個(gè)時(shí)間表示,時(shí)間段(Interval)相同的Segment,版本高的Segment數(shù)據(jù)可見(jiàn),版本低的Segment會(huì)被刪除掉。
【Payload 信息】:主要包含了此Segment的維度和指標(biāo)信息,以及Segment數(shù)據(jù)存在DeepStorage 位置信息等等。
segment主要組成部分
segment 內(nèi)部數(shù)據(jù)樣例
2.2 Datasource
Datasource相當(dāng)于關(guān)系型數(shù)據(jù)庫(kù)的表,Datasource的Schema是根據(jù)其可用的Segment動(dòng)態(tài)變化的,如果某個(gè)Datasource沒(méi)有可用的Segment(used=1),在druid-web的Datasource列表界面和查詢(xún)界面看不到這個(gè)Datasource。
元數(shù)據(jù)庫(kù)中druid_dataSource表并沒(méi)有保存Schema信息,只保存了該Datasource對(duì)應(yīng) 實(shí)時(shí)任務(wù)消費(fèi)數(shù)據(jù)的偏移量信息,都說(shuō)Druid的Datasource相當(dāng)于關(guān)系型數(shù)據(jù)庫(kù)的表,但是Druid中表(Datasource)Schema信息,并不是定義在druid_dataSource元數(shù)據(jù)表里。
那么在druid-web 頁(yè)面上看到的Datasource 的Schema信息是怎么來(lái)的呢?
其實(shí)它是實(shí)時(shí)根據(jù)該Datasource下所有Segment元數(shù)據(jù)信息合并而來(lái),所以DataSource的Schema是實(shí)時(shí)變化的,
這樣設(shè)計(jì)的好處是很好的適應(yīng)了Datasource維度不斷變化的需求在 :
Schema的合并過(guò)程
2.3 Rule
Rule定義了Datasource的Segment留存規(guī)則,主要分兩大類(lèi):Load和Drop。
- Load 表示Segment 保留策略。
- Drop 表示 Segment 刪除策略。
Load/Drop規(guī)則均有三個(gè)子類(lèi),分別是Forever Load/Drop,Interval Load/Drop以及Period Load/Drop,一個(gè)Datasource包含1個(gè)或多個(gè)Rule規(guī)則,如果沒(méi)有定義Rule規(guī)則就使用集群的Default Rule規(guī)則。
Datasource Rule規(guī)則列表是有序的(自定義規(guī)則在前面,集群默認(rèn)規(guī)則在后面),在運(yùn)行Run規(guī)則時(shí),會(huì)對(duì)該Datasource下所有可用的Segment信息,按照Run規(guī)則的先后順序進(jìn)行判斷,只要Segment滿足某個(gè)Rule規(guī)則,后面的規(guī)則Rule就不再運(yùn)行(如圖:Rule處理邏輯案例)。Rule規(guī)則主要包含下面幾部分信息:
【類(lèi)型】:類(lèi)型有刪除規(guī)則和加載規(guī)則。
【Tier和副本信息】:如果是Load規(guī)則,需要定義在不同Tier的Historical機(jī)器副本數(shù)。
【時(shí)間信息】:刪除或加載某個(gè)時(shí)間段的Segment。
Rule 樣例如下:
[
{
"period": "P7D",
"includeFuture": true,
"tieredReplicants": {
"_default_tier": 1,
"vStream":1
},
"type": "loadByPeriod"
},
{
"type": "dropForever"
}
]
Rule處理邏輯案例
2.4 Task
Task主要用于數(shù)據(jù)的攝入(本文主要討論實(shí)時(shí)攝入kafka數(shù)據(jù)的任務(wù)),在Task的運(yùn)行過(guò)程中,它會(huì)根據(jù)數(shù)據(jù)時(shí)間列產(chǎn)生一個(gè)或者多個(gè)Segment,Task分為實(shí)時(shí)和離線任務(wù)。
實(shí)時(shí)任務(wù)(kafka)是Overload進(jìn)程根據(jù)Supervisor定義自動(dòng)生成;
離線任務(wù)(類(lèi)型:index_hadoop,index_parallel)則需要外部系統(tǒng)通過(guò)訪問(wèn)接口方式提交。
每個(gè)任務(wù)主要包含下面幾部分信息:
【dataSchema】:定義了該任務(wù)生成的Segment中有哪些維度(dimensionsSpec),指標(biāo)(metricsSpec),時(shí)間列(timestampSpec),Segment粒度(segmentGranularity),數(shù)據(jù)聚合粒度(queryGranularity)。
【tuningConfig】:任務(wù)在攝入數(shù)據(jù)過(guò)程中的優(yōu)化參數(shù)(包括Segment生成策略,索引類(lèi)型,數(shù)據(jù)丟棄策略等等),不同的任務(wù)類(lèi)型有不同的參數(shù)設(shè)置。
【ioConfig】:定義了數(shù)據(jù)輸入的源頭信息,不同的數(shù)據(jù)源配置項(xiàng)有所不同。
【context】:關(guān)于任務(wù)全局性質(zhì)的配置,如任務(wù)Java進(jìn)程的option信息。
【datasource】:表示該任務(wù)為那個(gè)Datasource 構(gòu)造Segment。
實(shí)時(shí)任務(wù)生成Segment案例
2.5 Supervisor
Supervisor 用于管理實(shí)時(shí)任務(wù),離線任務(wù)沒(méi)有對(duì)應(yīng)的Supervisor,Supervisor與Datasource是一對(duì)一的關(guān)系,在集群運(yùn)行過(guò)程中Supervisor對(duì)象由Overlord進(jìn)程創(chuàng)建,通過(guò)Overlord接口提交Supervisor信息后,會(huì)在元數(shù)據(jù)庫(kù)(MySQL)中持久化,Supervisor內(nèi)容與Task相似,可以認(rèn)為實(shí)時(shí)Task是由Supervisor 克隆出來(lái)的。
三、Druid 整體架構(gòu)
前面籠統(tǒng)地介紹了Druid元數(shù)據(jù)相關(guān)概念,為了深入的了解Druid元數(shù)據(jù),先從宏觀的角度認(rèn)識(shí)一下Druid的整體架構(gòu)。
可以形象地把Druid集群類(lèi)比為一家公司,以Druid不同組件類(lèi)比這家公司中不同類(lèi)型員工來(lái)介紹Druid集群,Druid組件大體可以分為三類(lèi)員工:領(lǐng)導(dǎo)層,車(chē)間員工和銷(xiāo)售員工,如下圖:
Druid組件分類(lèi)
領(lǐng)導(dǎo)層: 領(lǐng)導(dǎo)根據(jù)外部市場(chǎng)需求(Overlord接收外部攝入任務(wù)請(qǐng)求),然后把生產(chǎn)任務(wù)下發(fā)到對(duì)應(yīng)的職業(yè)經(jīng)理人(MiddleManager),職業(yè)經(jīng)理人管理團(tuán)隊(duì)(MiddleManager 啟動(dòng)Peon進(jìn)程),下發(fā)具體生產(chǎn)任務(wù)給不同類(lèi)型的員工(Peon進(jìn)程)。
車(chē)間員工: 生產(chǎn)員工(Peon) 負(fù)責(zé)生產(chǎn)產(chǎn)品(segment),倉(cāng)庫(kù)管理員(Coordinator)負(fù)責(zé)把生產(chǎn)出來(lái)的產(chǎn)品(segment)分配到倉(cāng)庫(kù)(Historical)中去。
銷(xiāo)售員工: 銷(xiāo)售員(Broker)從生產(chǎn)員工(Peon)獲取最新的產(chǎn)品(segment),從倉(cāng)庫(kù)中獲取原來(lái)生產(chǎn)的產(chǎn)品(segment),然后把產(chǎn)品整理打包(數(shù)據(jù)進(jìn)一步合并聚合)之后交給顧客(查詢(xún)用戶(hù))。
上面通過(guò)類(lèi)比公司的方式,對(duì)Druid集群有了初步的整體印象。
下面具體介紹 Druid 集群架構(gòu),Druid 擁有一個(gè)多進(jìn)程,分布式架構(gòu),每個(gè)Druid組件類(lèi)型都可以獨(dú)立配置和擴(kuò)展,為集群提供最大的靈活性。
一個(gè)組件的中斷不會(huì)立即影響其他組件。
下面我們簡(jiǎn)要介紹Druid各個(gè)組件在集群中起到的作用。
Druid架構(gòu)
Overlord
Overlord負(fù)責(zé)接受任務(wù)、協(xié)調(diào)任務(wù)的分配、創(chuàng)建任務(wù)鎖以及收集、返回任務(wù)運(yùn)行狀態(tài)給調(diào)用者。當(dāng)集群中有多個(gè)Overlord時(shí),則通過(guò)選舉算法產(chǎn)生Leader,其他Follower作為備份。
MiddleManager
MiddleManager負(fù)責(zé)接收Overlord分配的實(shí)時(shí)任務(wù),同時(shí)創(chuàng)建新的進(jìn)程用于啟動(dòng)Peon來(lái)執(zhí)行實(shí)時(shí)任務(wù),每一個(gè)MiddleManager可以運(yùn)行多個(gè)Peon實(shí)例,每個(gè)實(shí)時(shí)Peon既提供實(shí)時(shí)數(shù)據(jù)查詢(xún)也負(fù)責(zé)實(shí)時(shí)數(shù)據(jù)的攝入工作。
Coordinator
Coordinator 主要負(fù)責(zé)Druid集群中Segment的管理與發(fā)布(主要是管理歷史Segment),包括加載新Segment、丟棄不符合規(guī)則的Segment、管理Segment副本以及Segment負(fù)載均衡等。如果集群中存在多個(gè)Coordinator Node,則通過(guò)選舉算法產(chǎn)生Leader,其他Follower作為備份。
Historical
Historical 的職責(zé)是負(fù)責(zé)加載Druid中非實(shí)時(shí)窗口內(nèi)且滿足加載規(guī)則的所有歷史數(shù)據(jù)的Segment。每一個(gè)Historical Node只與Zookeeper保持同步,會(huì)把加載完成的Segment同步到Zookeeper。
Broker
Broker Node 是整個(gè)集群查詢(xún)的入口,Broker 實(shí)時(shí)同步Zookeeper上保存的集群內(nèi)所有已發(fā)布的Segment的元信息,即每個(gè)Segment保存在哪些存儲(chǔ)節(jié)點(diǎn)上,Broker 為Zookeeper中每個(gè)dataSource創(chuàng)建一個(gè)timeline,timeline按照時(shí)間順序描述了每個(gè)Segment的存放位置。
每個(gè)查詢(xún)請(qǐng)求都會(huì)包含dataSource以及interval信息,Broker 根據(jù)這兩項(xiàng)信息去查找timeline中所有滿足條件的Segment所對(duì)應(yīng)的存儲(chǔ)節(jié)點(diǎn),并將查詢(xún)請(qǐng)求發(fā)往對(duì)應(yīng)的節(jié)點(diǎn)。
四、 Druid元數(shù)據(jù)存儲(chǔ)介質(zhì)
Druid 根據(jù)自身不同的業(yè)務(wù)需要,把元數(shù)據(jù)存儲(chǔ)在不同的存儲(chǔ)介質(zhì)中,為了提升查詢(xún)性能,同時(shí)也會(huì)將所有元數(shù)據(jù)信息緩存在內(nèi)存中。把歷史數(shù)據(jù)的元數(shù)據(jù)信息保存到元數(shù)據(jù)庫(kù)(MySQL),以便集群重啟時(shí)恢復(fù)。
由于Druid 擁有一個(gè)多進(jìn)程,分布式架構(gòu),需要使用Zookeeper進(jìn)行元數(shù)據(jù)傳輸,服務(wù)發(fā)現(xiàn),主從選舉等功能,并且歷史節(jié)點(diǎn)會(huì)把Segment元數(shù)據(jù)信息存儲(chǔ)在本地文件。
那么歷史節(jié)點(diǎn)(Historical)為什么會(huì)把該節(jié)點(diǎn)加載的Segment元數(shù)據(jù)信息緩存在自己節(jié)點(diǎn)的本地呢?
是因?yàn)樵跉v史節(jié)點(diǎn)發(fā)生重啟之后,讀取Segment的元數(shù)據(jù)信息不用去Mysql等其他元數(shù)據(jù)存儲(chǔ)介質(zhì)進(jìn)行跨節(jié)點(diǎn)讀取而是本地讀取, 這樣就極大地提升了歷史節(jié)點(diǎn)數(shù)據(jù)的恢復(fù)效率。
下面分別介紹這些存儲(chǔ)介質(zhì)(內(nèi)存、元數(shù)據(jù)庫(kù)、Zookeeper、本地文件)里的數(shù)據(jù)和作用。
4.1 元數(shù)據(jù)庫(kù)(MySQL)
MySQL 數(shù)據(jù)庫(kù)主要用于長(zhǎng)期持久化 Druid 元數(shù)據(jù)信息,比如segment部分元數(shù)據(jù)信息存在druid_segments表中,歷史的Task信息存在druid_tasks,Supervisor信息存儲(chǔ)在druid_supervisors等等。
Druid部分服務(wù)進(jìn)程在啟動(dòng)時(shí)會(huì)加載元數(shù)據(jù)庫(kù)持久化的數(shù)據(jù),如:Coordinator進(jìn)程會(huì)定時(shí)加載表druid_segments 中used字段等于1的segment列表,Overlord 啟動(dòng)時(shí)會(huì)自動(dòng)加載druid_supervisors表信息,以恢復(fù)原來(lái)實(shí)時(shí)攝入任務(wù)等等。
MySQL 元數(shù)據(jù)庫(kù)表
4.2 Zookeeper
Zookeeper 主要存儲(chǔ) Druid 集群運(yùn)行過(guò)程中實(shí)時(shí)產(chǎn)生的元數(shù)據(jù),Zookeeper 數(shù)據(jù)目錄大概可以分為Master節(jié)點(diǎn)高可用、數(shù)據(jù)攝入、數(shù)據(jù)查詢(xún)3類(lèi)目錄。
下面介紹Druid相關(guān)Zookeeper目錄元數(shù)據(jù)內(nèi)容。
Zookeeper 元數(shù)據(jù)節(jié)點(diǎn)分類(lèi)
4.2.1 Master 節(jié)點(diǎn)高可用相關(guān)目錄
${druid.zk.paths.base}/coordinator: coordinator 主從高可用目錄,有多個(gè)臨時(shí)有序節(jié)點(diǎn) 編號(hào)小的是leader。
${druid.zk.paths.base}/overlord: overlord 主從高可用目錄,有多個(gè)臨時(shí)有序節(jié)點(diǎn) 編號(hào)小的是leader。
4.2.2 數(shù)據(jù)查詢(xún)相關(guān)目錄
${druid.zk.paths.base}/announcements:只存儲(chǔ)historical,peon進(jìn)程的host:port,沒(méi)有MiddleManager,broker,coodinator等進(jìn)程信息,用于查詢(xún)相關(guān)節(jié)點(diǎn)服務(wù)發(fā)現(xiàn)。
${druid.zk.paths.base}/segments:當(dāng)前集群中能被查詢(xún)到的segment列表。目錄結(jié)構(gòu):historical或peon的host:port/${segmentId},Broker節(jié)點(diǎn)會(huì)實(shí)時(shí)同步這些Segment信息,作為數(shù)據(jù)查詢(xún)的重要依據(jù)。
4.2.3 數(shù)據(jù)攝入相關(guān)目錄
${druid.zk.paths.base}/loadQueue: Historical需要加載和刪除的segment信息列表(不止只有加載),Historical進(jìn)程會(huì)監(jiān)聽(tīng)這個(gè)目錄下自己需要處理的事件(加載或刪除),事件完成之后會(huì)主動(dòng)刪除這個(gè)目錄下的事件。
${druid.zk.paths.indexer.base}=${druid.zk.paths.base}/indexer:關(guān)于攝入任務(wù)數(shù)據(jù)的base目錄。
${druid.zk.paths.indexer.base}/announcements:保存當(dāng)前存活MiddleManager列表,注意historical,peon 列表不在這里,這里只存儲(chǔ)攝入相關(guān)的服務(wù)信息,用于數(shù)據(jù)攝入相關(guān)節(jié)點(diǎn)服務(wù)發(fā)現(xiàn)。
${druid.zk.paths.indexer.base}/tasks Overlord 分配的任務(wù)信息放在這個(gè)目錄(MiddleManager的host:port/taskInfo),等任務(wù)在MiddleManager上運(yùn)行起來(lái)了,任務(wù)節(jié)點(diǎn)信息將被刪除。
${druid.zk.paths.indexer.base}/status:保存任務(wù)運(yùn)行的狀態(tài)信息,Overlord通過(guò)監(jiān)聽(tīng)這個(gè)目錄獲取任務(wù)的最新運(yùn)行狀態(tài)。
4.3 內(nèi)存
Druid為了提升元數(shù)據(jù)訪問(wèn)的效率會(huì)把元數(shù)據(jù)同步到內(nèi)存,主要通過(guò)定時(shí)SQL 查詢(xún)?cè)L問(wèn)方式同步MySQL元數(shù)據(jù)或者使用Apache Curator Recipes實(shí)時(shí)同步Zookeeper上的元數(shù)據(jù)到內(nèi)存如下圖。
每個(gè)進(jìn)程中的元數(shù)據(jù)不一樣,下面一一介紹一下各個(gè)角色進(jìn)程緩存了哪些數(shù)據(jù)。
Druid進(jìn)程元數(shù)據(jù)同步方式
4.3.1 Overlord
實(shí)時(shí)同步Zookeeper目錄(${druid.zk.paths.indexer.base}/announcements)下的數(shù)據(jù),使用變量RemoteTaskRunner::zkWorkers(類(lèi)型:Map)存儲(chǔ),每ZkWorker對(duì)應(yīng)一個(gè)MM進(jìn)程,在ZkW orker對(duì)象中會(huì)實(shí)時(shí)同步Zookeeper目錄(${druid.zk.paths.indexer.base}/status/${mm_host:port})任務(wù)信息,使用RemoteTaskRunner::runningTasks變量存儲(chǔ)。
默認(rèn)每分鐘同步數(shù)據(jù)庫(kù)中druid_tasks active = 1的數(shù)據(jù),使用變量TaskQueue::tasks(類(lèi)型:List )存儲(chǔ),在同步時(shí)會(huì)把內(nèi)存中的Task列表與最新元數(shù)據(jù)里的Task列表進(jìn)行比較,得到新增的task列表和刪除的task列表,把新增的Task加到內(nèi)存變量TaskQueue::tasks,清理掉將要被刪除的task
4.3.2 Coordinator
默認(rèn)每1分鐘同步元數(shù)據(jù)庫(kù)中druid_segemtns 中列 used=1的segment列表到變量SQLMetadataSegmentManager::dataSourcesSnapshot。
默認(rèn)每1分鐘同步元數(shù)據(jù)庫(kù)druid_rules表信 SQLMetadataRuleManager::rules變量使用CoordinatorServerView類(lèi)(后面會(huì)介紹)實(shí)時(shí)同步${druid.zk.paths.base}/announcements,${druid.zk.paths.base}/segments的數(shù)據(jù),用于與元數(shù)據(jù)庫(kù)中的segment對(duì)比,用來(lái)判斷哪些segment應(yīng)該加載或刪除。
4.3.3 Historical
會(huì)實(shí)時(shí)同步${druid.zk.paths.base}/loadQueue/${historical_host:port} 下的數(shù)據(jù),進(jìn)行segment的加載與刪除操作,操作完成之后會(huì)主動(dòng)刪除對(duì)應(yīng)的節(jié)點(diǎn)。
Historical通過(guò)上報(bào)segment信息到${druid.zk.paths.base}/segments來(lái)暴露segment。
4.3.4 MiddleManager
會(huì)實(shí)時(shí)同步${druid.zk.paths.indexer.base}/tasks/${mm_host:port}的數(shù)據(jù),進(jìn)行任務(wù)(peon)進(jìn)程的啟動(dòng),啟動(dòng)完成之后會(huì)主動(dòng)刪除對(duì)應(yīng)的節(jié)點(diǎn)。
MiddleManager上報(bào)segment信息到${druid.zk.paths.base}/segments來(lái)暴露segment。
4.3.5 Broker
使用BrokerServerView類(lèi)實(shí)時(shí)同步${druid.zk.paths.base}/announcements,${druid.zk.paths.base}/segments的數(shù)據(jù),構(gòu)建出整個(gè)系統(tǒng)的時(shí)間軸對(duì)象(BrokerServerView::timelines) 作為數(shù)據(jù)查詢(xún)的基本依據(jù)。同步過(guò)程中類(lèi)的依賴(lài)關(guān)系如下圖。
下層的類(lèi)對(duì)象使用監(jiān)聽(tīng)上層類(lèi)對(duì)象的方式感知sement的增刪改,并做相應(yīng)的邏輯處理, 會(huì)同時(shí)監(jiān)聽(tīng)${druid.zk.paths.base}/announcements和${druid.zk.paths.base}/segments的數(shù)據(jù)的數(shù)據(jù)變化,通過(guò)回調(diào)監(jiān)聽(tīng)器的方式通知到下層類(lèi)對(duì)象。
zk中segment同步到Druid進(jìn)程過(guò)程中對(duì)象之間的監(jiān)聽(tīng)關(guān)系
4.4 本地文件
本地文件的元數(shù)據(jù)主要用于恢復(fù)單個(gè)節(jié)點(diǎn)時(shí)讀取并加載。
例如:Historical節(jié)點(diǎn)第一個(gè)數(shù)據(jù)目錄下的info_dir目錄(如:/data1/druid/segment-cache/info_dir),保存了該節(jié)點(diǎn)加載的所有segment信息,在Historical進(jìn)程重啟時(shí)會(huì)讀取該目錄下的segment元數(shù)據(jù)信息,判斷本地是否有該segment的數(shù)據(jù),如果沒(méi)有就去深度存儲(chǔ)系統(tǒng)(hdfs)下載,數(shù)據(jù)下載完成后會(huì)上報(bào)segment信息到Zookeeper(路徑:${druid.zk.paths.base}/segments)。
五、Druid 元數(shù)據(jù)相關(guān)業(yè)務(wù)邏輯
由于Druid組件類(lèi)型比較多,業(yè)務(wù)邏輯比較復(fù)雜,從整體到局部方式,從宏觀到細(xì)節(jié),循序漸進(jìn)地了解Druid的業(yè)務(wù)邏輯,以便了解Druid元數(shù)據(jù)在業(yè)務(wù)邏輯中發(fā)揮的作用。
5.1 Druid 元數(shù)據(jù)整體業(yè)務(wù)邏輯
前面從整體了解了 Druid 集群各個(gè)組件的協(xié)作關(guān)系,下面分別從攝入任務(wù)管理、數(shù)據(jù)攝入、數(shù)據(jù)查詢(xún)?nèi)齻€(gè)方面的業(yè)務(wù)邏輯來(lái)梳理元數(shù)據(jù)在 Druid 集群所起的作用。
5.1.1 攝入任務(wù)管理
攝入數(shù)據(jù)之前需要用戶(hù)提交攝入任務(wù),Overlord根據(jù)任務(wù)的配置會(huì)相應(yīng)命令MiddlerManager啟動(dòng)該任務(wù)的相關(guān)進(jìn)程(peon進(jìn)程)用于攝入數(shù)據(jù),具體流程如下圖中數(shù)據(jù)序號(hào)順序執(zhí)行。
任務(wù)提交與管理
下面分別按照上圖中數(shù)字序號(hào)順序介紹 Druid 內(nèi)部關(guān)于任務(wù)管理的業(yè)務(wù)邏輯:
① Overlord進(jìn)程收到任務(wù)提交請(qǐng)求之后,會(huì)把任務(wù)信息寫(xiě)入druid_tasks表,此時(shí)字段active等于1。
② Overlord分配任務(wù)給特定的MiddleManager節(jié)點(diǎn),并把task信息寫(xiě)入Zookeeper目錄(${druid.zk.paths.indexer.base}/tasks )下。
③ MiddleManager進(jìn)程監(jiān)聽(tīng)當(dāng)前節(jié)點(diǎn)在Zookeeper目錄
(${ruid.zk.paths.indexer.base}/task)需要啟動(dòng)的task信息。
④ MiddleManager會(huì)以fork的方式啟動(dòng)Peon進(jìn)程(task)此時(shí)Peon進(jìn)程開(kāi)始攝入數(shù)據(jù),并把任務(wù)Running狀態(tài)寫(xiě)入Zookeeper目錄
(${ruid.zk.paths.indexer.base}/status)。
⑤ Overlord會(huì)實(shí)時(shí)監(jiān)聽(tīng)Zookeeper目錄
(${ruid.zk.paths.indexer.base}/status)獲取任務(wù)運(yùn)行最新?tīng)顟B(tài)。
⑥ 任務(wù)完成后Overlord會(huì)把task狀態(tài)信息更新到數(shù)據(jù)庫(kù)表druid_tasks,此時(shí)字段active=0。
5.1.2 數(shù)據(jù)攝入邏輯
Druid數(shù)據(jù)攝入邏輯
下面分別按照上圖中數(shù)字序號(hào)順序介紹Druid內(nèi)部關(guān)于數(shù)據(jù)攝入的業(yè)務(wù)邏輯:
① Peon進(jìn)程在本地生產(chǎn)segment之后,會(huì)上傳segment數(shù)據(jù)到深度存儲(chǔ)Hdfs。
② 插入一條segment元數(shù)據(jù)信息到元數(shù)據(jù)druid_segments表中,包括segment數(shù)據(jù)hdfs地址,Interval信息,注意此時(shí)used字段為1。
③ Coordinator進(jìn)程定時(shí)拉取druid_segments表中used為1的數(shù)據(jù)。
④ Coordinator進(jìn)程把segment分配信息寫(xiě)入Zookeeper目錄:
${druid.zk.paths.base}/loadQueue。
⑤ HIstorical進(jìn)程監(jiān)聽(tīng)當(dāng)前節(jié)點(diǎn)在Zookeeper目錄
(${druid.zk.paths.base}/loadQueue)獲取需要加載的segment信息。
⑥ 從Hdfs下載segment數(shù)據(jù),加載segment。
⑦把已加載的segment的元數(shù)據(jù)信息同步到Zookeeper目錄(${druid.zk.paths.base}/segments)。
5.1.3 數(shù)據(jù)查詢(xún)邏輯
數(shù)據(jù)查詢(xún)主要涉及到Peon、Historical,Broker三個(gè)角色,Broker會(huì)根據(jù)client的查詢(xún)請(qǐng)求中包含的dataSource和interval信息,篩選出需要查詢(xún)的segment,然后Broker作為客戶(hù)端從Peon獲取實(shí)時(shí)數(shù)據(jù),從Historical獲取歷史數(shù)據(jù),再根據(jù)查詢(xún)要求,將兩部分?jǐn)?shù)據(jù)進(jìn)一步聚合,如下圖:
Druid數(shù)據(jù)查詢(xún)邏輯
5.2 Druid 元數(shù)據(jù)具體業(yè)務(wù)邏輯
有了前面對(duì)Druid集群整體認(rèn)識(shí)之后,下面更為細(xì)致的探討Druid元數(shù)據(jù)在各個(gè)組件之間發(fā)揮的作用。
如下圖虛線箭頭表示元數(shù)據(jù)的傳輸,下面按照?qǐng)D中數(shù)字序號(hào)介紹每個(gè)虛線箭頭兩端組件與元數(shù)據(jù)存儲(chǔ)介質(zhì)(MySQL、Zookeeper)之間的元數(shù)據(jù),每條具體從組件對(duì)元數(shù)據(jù)存儲(chǔ)介質(zhì)包含讀和寫(xiě)兩方面來(lái)介紹,如下:
Druid元數(shù)據(jù)業(yè)務(wù)邏輯
① 寫(xiě):啟動(dòng)任務(wù)時(shí)寫(xiě)入task信息,提交實(shí)時(shí)任務(wù)時(shí)寫(xiě)入supervisor信息。讀:broker調(diào)用overlord接口時(shí)會(huì)查詢(xún)不同狀態(tài)下的task信息,進(jìn)程重啟時(shí)恢復(fù)supervisor信息。
② 寫(xiě):分配任務(wù)到MiddleManager時(shí),寫(xiě)入任務(wù)信息。讀:同步正在運(yùn)行任務(wù)的狀態(tài)信息。
③ 寫(xiě):寫(xiě)入當(dāng)前節(jié)點(diǎn)任務(wù)狀態(tài)信息到Zookeeper,讀:讀取帶啟動(dòng)或終止任務(wù)信息。
④ 寫(xiě):任務(wù)啟動(dòng)后上報(bào)實(shí)時(shí)segment信息。
⑤ 讀:coordinator定時(shí)讀取字段used=1的segment列表信息。
⑥ 寫(xiě):coordinator分配的segment信息,讀:已分配的segment列表信息。
⑦ 寫(xiě):已加載完成的segment信息,讀:需要加載的segment信息。
⑧ 讀:加載完成的segment信息,作為數(shù)據(jù)查詢(xún)的依據(jù)。
六、總結(jié)
前面以整體到局部、抽象到細(xì)節(jié)的方式從四個(gè)方面(Druid元數(shù)據(jù)基本概念、Druid整體架構(gòu)、Druid元數(shù)據(jù)存儲(chǔ)介質(zhì)Druid元數(shù)據(jù)相關(guān)業(yè)務(wù)邏輯)介紹了Druid元數(shù)據(jù)在Druid集群中扮演的角色。
Druid 擁有一個(gè)多進(jìn)程,分布式架構(gòu),每個(gè)組件只關(guān)注自己的業(yè)務(wù)邏輯和元數(shù)據(jù),通過(guò)RPC通信或Zookeeper 進(jìn)行組件之間的解耦,每個(gè) Druid 組件類(lèi)型都可以獨(dú)立配置和擴(kuò)展,極大提供集群的靈活性,以至于一個(gè)組件的中斷不會(huì)立即影響其他組件,下面對(duì)Druid元數(shù)據(jù)做一個(gè)總結(jié):
- Druid元數(shù)據(jù)存儲(chǔ)介質(zhì)有內(nèi)存、元數(shù)據(jù)庫(kù)(MySQL)、Zookeeper、本地文件。
- 元數(shù)據(jù)庫(kù)(MySQL)和本地的元數(shù)據(jù)起到備份、持久化的作用。Zookeeper主要起到元數(shù)據(jù)傳輸橋梁,實(shí)時(shí)保存元數(shù)據(jù)的作用,同時(shí)把元數(shù)據(jù)同步到內(nèi)存,極大提升了Druid數(shù)據(jù)查詢(xún)和數(shù)據(jù)攝入的性能,而本地文件的元數(shù)據(jù)主要用于恢復(fù)單個(gè)節(jié)點(diǎn)時(shí)快速讀取并加載到內(nèi)存。
- 在Druid組件進(jìn)程中會(huì)把Zookeeper和元數(shù)據(jù)庫(kù)(MySQL)里的元數(shù)據(jù)分別通過(guò)實(shí)時(shí)同步和定時(shí)拉取的方式同步到進(jìn)程的內(nèi)存中,以提高訪問(wèn)效率。
- 保存在各個(gè)組件進(jìn)程中內(nèi)存的元數(shù)據(jù)才是當(dāng)前集群中最新最全的元數(shù)據(jù)。