Druid是一個高效的數(shù)據(jù)查詢系統(tǒng),主要解決的是對于大量的基于時序的數(shù)據(jù)進(jìn)行聚合查詢。數(shù)據(jù)可以實時攝入,進(jìn)入到Druid后立即可查,同時數(shù)據(jù)是幾乎是不可變。通常是基于時序的事實事件,事實發(fā)生后進(jìn)入Druid,外部系統(tǒng)就可以對該事實進(jìn)行查詢。
Druid采用的架構(gòu):
shared-nothing架構(gòu)與lambda架構(gòu)
Druid設(shè)計三個原則:
1.快速查詢(Fast Query) : 部分?jǐn)?shù)據(jù)聚合(Partial Aggregate) + 內(nèi)存華(In-Memory) + 索引(Index)
2.水平拓展能力(Horizontal Scalability):分布式數(shù)據(jù)(Distributed data)+并行化查詢(Parallelizable Query)
3.實時分析(Realtime Analytics):Immutable Past , Append-Only Future
數(shù)據(jù)吞吐量大
支持流式數(shù)據(jù)攝入和實時
查詢靈活且快速
Druid在數(shù)據(jù)攝入之前,首先要定義一個數(shù)據(jù)源(DataSource,類似于數(shù)據(jù)庫中表的概念)
Druid是一個分布式數(shù)據(jù)分析平臺,也是一個時序數(shù)據(jù)庫
1.數(shù)據(jù)格式
數(shù)據(jù)集合(時間列,維度列,指標(biāo)列)
數(shù)據(jù)結(jié)構(gòu):
基于DataSource與Segment的數(shù)據(jù)結(jié)構(gòu),DataSource相當(dāng)于關(guān)系型數(shù)據(jù)庫中的表。
DataSource包含:
時間列(TimeStamp):標(biāo)識每行數(shù)據(jù)的時間值
維度列(Dimension):標(biāo)識數(shù)據(jù)行的各個類別信息
指標(biāo)列(Metric):用于聚合和計算的列
Segment結(jié)構(gòu):
DataSource是邏輯結(jié)構(gòu),而Segment是數(shù)據(jù)實際存儲的物理結(jié)構(gòu),Druid通過Segment實現(xiàn)對數(shù)據(jù)的橫縱切割操作。
橫向切割:通過設(shè)置在segmentGranularity參數(shù),Druid將不同時間范圍內(nèi)的數(shù)據(jù)存儲在不同Segment數(shù)據(jù)塊中。
縱向切割:在Segment中面向列進(jìn)行數(shù)據(jù)壓縮處理
設(shè)置合理的Granularity
segmentGranularity:segment的組成粒度。
queryGranularity :segment的聚合粒度。
queryGranularity 小于等于 segmentGranularity
若segmentGranularity = day,那么Druid會按照天把不同天的數(shù)據(jù)存儲在不同的Segment中。
若queryGranularity =none,可以查詢所有粒度,queryGranularity = hour只能查詢>=hour粒度的數(shù)據(jù)
2.數(shù)據(jù)攝入
實時數(shù)據(jù)攝入
批處理數(shù)據(jù)攝入
3.數(shù)據(jù)查詢
原生查詢,采用JSON格式,通過http傳送
1.OpenTSDB
開源的時序數(shù)據(jù)庫,支持?jǐn)?shù)千億的數(shù)據(jù)點,并提供精確的數(shù)據(jù)查詢功能
采用java編寫,通過基于Hbase的存儲實現(xiàn)橫向拓展
設(shè)計思路:利用Hbase的key存儲一些tag信息,將同一小時的數(shù)據(jù)放在一行存儲,提高了查詢速度
架構(gòu)示意圖:
集群還依賴三類外部依賴
元數(shù)據(jù)庫(Metastore):存儲Druid集群的原數(shù)據(jù)信息,比如Segment的相關(guān)信息,一般用MySql或PostgreSQL
分布式協(xié)調(diào)服務(wù)(Coordination):為Druid集群提供一致性協(xié)調(diào)服務(wù)的組件,通常為Zookeeper
數(shù)據(jù)文件存儲系統(tǒng)(DeepStorage):存放生成的Segment文件,并供歷史節(jié)點下載。對于單節(jié)點集群可以是本地磁盤,而對于分布式集群一般是HDFS或NFS
實時節(jié)點數(shù)據(jù)塊的生成示意圖:
數(shù)據(jù)塊的流向:
Historical Node歷史節(jié)點
Broker Node節(jié)點:
Druid提供兩類介質(zhì)作為Cache以供選擇
外部Cache,比如Memcached
本地Cache,比如查詢節(jié)點或歷史節(jié)點的內(nèi)存作為cache
高可用性:
通過Nginx來負(fù)載均衡查詢節(jié)點(Broker Node)
協(xié)調(diào)節(jié)點:
協(xié)調(diào)節(jié)點(Coordinator Node)負(fù)責(zé)歷史節(jié)點的數(shù)據(jù)負(fù)載均衡,以及通過規(guī)則管理數(shù)據(jù)的生命周期
4.索引服務(wù)
2.從節(jié)點:Middle Manager
索引服務(wù)的工作節(jié)點,負(fù)責(zé)接收主節(jié)點的分配的任務(wù),然后啟動相關(guān)的苦工即獨立的JVM來完成具體的任務(wù)
這樣的架構(gòu)與Hadoop YARN相似
主節(jié)點相當(dāng)于Yarn的ResourceManager,負(fù)責(zé)集群資源管理,與任務(wù)分配
從節(jié)點相當(dāng)于Yarn的NodeManager,負(fù)責(zé)管理獨立節(jié)點的資源并接受任務(wù)
Peon(苦工)相當(dāng)于Yarn的Container,啟動在具體節(jié)點上負(fù)責(zé)具體任務(wù)的執(zhí)行
由于老版本的Druid使用pull方式消費kafka數(shù)據(jù),使用kafka consumer group來共同消費一個kafka topic的數(shù)據(jù),各個節(jié)點會負(fù)責(zé)獨立消費一個或多個該topic所包含的Partition數(shù)據(jù),并保證同一個Partition不會被多于一個的實時節(jié)點消費。每當(dāng)一個實時節(jié)點完成部分?jǐn)?shù)據(jù)的消費后,會主動將消費進(jìn)度(kafka topic offset)提交到Zookeeper集群。
當(dāng)節(jié)點不可用時,該kafka consumer group 會立即在組內(nèi)對所有可用的節(jié)點進(jìn)行partition重新分配,接著所有節(jié)點將會根據(jù)記錄在zk集群中每一個partition的offset來繼續(xù)消費未曾消費的數(shù)據(jù),從而保證所有數(shù)據(jù)在任何時候都會被Druid集群至少消費一次。
這樣雖然能保證不可用節(jié)點未消費的partition會被其余工作的節(jié)點消費掉,但是不可用節(jié)點上已經(jīng)消費的數(shù)據(jù),尚未被傳送到DeepStoreage上且未被歷史節(jié)點下載的Segment數(shù)據(jù)卻會被集群遺漏,這是基于kafka-eight Firehose消費方式的一種缺陷。
解決方案:
1.讓不可用節(jié)點恢復(fù)重新回到集群成為可用節(jié)點,重啟后會將之前已經(jīng)生成但未上傳的Segment數(shù)據(jù)文件統(tǒng)統(tǒng)加載回來,并最終合并傳送到DeepStoreage,保證數(shù)據(jù)完整性
2.使用Tranquility與Indexing Service,對kafka的數(shù)據(jù)進(jìn)行精確的消費與備份。
由于Tranquility可以通過push的方式將指定數(shù)據(jù)推向Druid集群,因此它可以同時對同一個partition制造多個副本。所以當(dāng)某個數(shù)據(jù)消費失敗時候,系統(tǒng)依然可以準(zhǔn)確的選擇使用另外一個相同的任務(wù)所創(chuàng)建的Segment數(shù)據(jù)庫