(2)Druid架構詳解

Druid的架構中包括以下4類節點:
  • 實時節點(realtime node)  實時攝入數據,並生成Segment數據文件
  • 歷史節點(historical node) 加載已經生成的數據文件,以供數據查詢
  • 查詢節點(Broker node)對外提供數據查詢服務,並同時從實時節點與歷史節點查詢數據,合併後返回給調用方。
  • 協調節點(coordinator  node)負責歷史節點的數據負載均衡 ,以及通過規則Rule管理數據的生命週期。
同時,集羣還包括以下三類依賴:
  • 元數據庫,存儲Druid集羣的元數據信息,一般用mysql
  • 分佈式協調服務,爲druid集羣提供一致性的協調服務,通常爲zookeeper
  • 數據文件存儲庫(deepStorage)存放生成的Segment數據文件,並供歷史節點下載,對於分佈式集羣一般是HDFS

Druid架構設計思想:
1  索引對樹結構的選擇
對於大多數Druid的使用場景,Druid本質上是一個分佈式的時序數據庫,爲了加速對數據庫的訪問,大多數傳統的數據庫都會用一個數據結構叫做索引index,索引一般不使用hash算法,而使用Tree結構,
二叉樹 --》 平衡二叉樹 --》 B+樹  --》 LSM-Tree  日誌結構合併樹
LSM-Tree最大的特點是同時使用了兩種分類樹的數據結構來存儲數據,並同時提供查詢,其中一部分數據結構C0樹存在於內存緩存(memtable)中,負責接受新的數據插入更新以及讀請求,並直接在內存中對數據進行排序,另一部分數據結構C1存在於硬盤上(sstable)它們是由存在於內存緩存中的C0樹寫到磁盤而成的,主要負責提供讀操作,特點是有序且不可更改。
LSM-Tree的另一大特點是有日誌文件(commit log)來爲數據恢復做保障,這三類數據結構的協作順序是:所有的新插入與更新都首先被記錄到commit log中,該操作叫做WAL 然後再寫到memtable,當達到一定條件時數據會從memtable寫到sstable,並拋棄相關的log數據,memtable和sstable可同時提供查詢,當memtable出問題時,可以從commit log與 sstable中將memtable的數據恢復。
LSM-Tree的這種結構非常有利於數據快讀寫入,但不利於讀,因爲理論上讀的時候可能需要同時從memtable和所有硬盤上的sstable中查詢數據,這樣顯然會對性能造成較大的影響。所以LSM-Tree採取以下措施:
1  定期將硬盤上小的sstable合併,較少sstable的數量,刪除操作也在合併的時候進行
2  對每個sstable使用布隆過濾,減少數據總的查詢時間

Druid對LSM-Tree的應用
LSM-Tree顯然比較適合那些數據插入操作遠多於更新刪除和讀的場景,同時Druid在一開始就是爲時序數據場景設計的,正好符合LSM-Tree的優勢特點。
Druid的類LSM-Tree架構中的實時節點(Realtime node)負責消費實時數據,與經典LSM-Tree架構不同的是,Druid不提供日誌和WAL原則,實時數據首先會被直接加載進實時節點內存中的堆結構緩存區(相當於memtable),當條件滿足時,緩存區裏的數據會被寫入到硬盤上形成一個數據塊(Segment Split),同時實時節點又會立即將新生成的數據塊加載到內存中的非堆區,因此無論是堆結構緩存還是非堆區裏的數據,都能被查詢節點(Broker node)查詢。
同時,實時節點會週期性的將磁盤上同一個時間段內生成的所有數據塊合併爲一個大的數據塊(Segment),這個過程在實時節點中叫segment Merge操作,合併好的segment會立即被實時節點上傳到數據文件存儲庫(deepStorage)中,隨後協調節點(coordinator node)會指導一個歷史節點(historical node)去文件存儲庫,將新生成的segment下載到其本地磁盤中,當歷史節點成功加載到segment胡,會通過協調服務zk在集羣中聲明其從此刻負責提供該segment的查詢,當實時節點收到該聲明後也會立即聲明其不再提供該segment的查詢,接下來查詢節點會轉從該歷史節點查詢此segment的數據,而對於全局數據來說,查詢節點會同時從實時節點(少量當前數據)和歷史節點(大量歷史數據)分別查詢,然後做個結果的整合,最後再返回給用戶。

基於 DataSource 與 Segment的數據結構
1 DataSource結構
與傳統的RDBMS比較,Druid的DataSource可以理解爲RDBMS的table
DataSource的結構包含以下幾個方面:
  • 時間列(timestamp)表明每行數據的時間值,精確到毫秒
  • 維度列(dimension)
  • 指標列(metric)用於聚合和計算的列
無論是實時數據消費還是批量數據處理,Druid在基於DataSource結構存儲數據時即可選擇對任意的指標列進行聚合操作,該聚合操作主要基於維度列和時間範圍
  1. 同維度列的值做聚合:所有維度的值都相同時,這一類行數據符合聚合操作
  2. 對指定時間粒度內的值做聚合
相對於其他時序數據庫,Druid在數據存儲時便可對數據進行聚合操作是其一大特點,該特點使得druid不僅能夠節省存儲空間,而且提高聚合查詢效率。
2 segment結構
DataSource是個邏輯概念,Segment卻是數據的實際物理存儲格式,Druid正是通過Segment實現了對數據的橫縱向切割操作,從數據按時間分佈的角度看,通過參數segmentGranularity的設置,Druid將不同時間範圍內的數據存儲在不同的segment數據塊中,這便是所謂的數據橫向切割,這種設計爲Druid帶來了一個優點,按時間範圍查詢數據時,僅需要訪問對應時間段內的這個segment數據塊,而不需要進行全表數據範圍的查詢,這使得效率得到了極大的提高。
同時,在segment中也面向列進行數據壓縮存儲,這便是所謂的數據縱向切割,而且在segment中使用了Bitmap等技術對數據的訪問進行優化。


3  擴展系統
Druid可以添加擴展系統,通過兩種方法:
1)將擴展加載到Druid Service的classpath,Druid便能加載到相關擴展
2)在common.runtime.properties文件中通過druid.extensions.directory指定擴展目錄

實時節點
實時節點主要負責即時攝入實時數據,以及生成segment數據文件。
實時節點通過Firehose來消費實時數據,Firehose是Druid中消費實時數據的模型,可以有不同的具體實現,比如Druid自帶的基於Kafka High Level API實現的用於消費Kafka數據的druid-kafka-eight Firehose,還有低級的kafkaAPI,
同時,實時節點會通過另一個用於生成segment數據文件的模塊plumber,按照指定的週期,按時將本週期內生產出的所有數據塊合併成一個大的segment數據文件。
segment數據文件從製造到傳播要經歷一個完整的流程,步驟如下:
1) 實時節點生產出segment數據文件,並將其上傳到DeepStorage中
2) segment數據文件的相關元數據信息被存放到metaStore(mysql)中
3) master節點(也就是coordinator節點)從metaStore裏得知Segment數據文件的相關元數據信息後,將其根據規則的設置分配給符合條件的歷史節點。
4) 歷史節點得到指令後會主動從deepStorage中拉取指定的segment數據文件,並通過zookeeper向集羣聲明其負責提供該segment數據文件的查詢服務。
5) 實時節點丟棄該segment數據文件,並向集羣聲明其不再提供該segment數據文件的查詢服務。
從設計上看,實時節點擁有很好的可擴展性和高可用性,對於Druid版本來說,其消費kafka數據時一般使用Druid自帶的用於消費kafka數據的druid-kafka-eight Firehose,使用pull的方式從kafka獲取數據,而該Firehose能夠讓Druid有較好的可擴展性和高可用性,使用一組實時節點組成一個kafka consumer group來共同消費同一個kafka topic的數據,各個節點會負責獨立消費一個或多個該topic,所包含的partition的數據,並且保證同一個partition不會被多於一個的實時節點消費,當每一個實時節點完成部分數據的消費後,會主動將數據消費進度offset提交到zookeeper集羣,這樣,當這個節點不可用時,該kafka consumer group會立即在組內對所有可用節點進行partition的重新分配,接着所有節點將會根據記錄在zookeeper集羣裏的每一個partition的offset來繼續消費爲曾被消費的數據,從而保證所有數據在任何時候都會被Druid集羣至少消費一次,進而實現了這個角度上的高可用性,同樣的道理,當集羣中添加新的實時節點時,也會觸發相同的事件,從而保證了實時節點能夠輕鬆實現線性擴展。
但是,這樣的方式有個問題,當一個kafka consumer group內的實時節點不可用時,該方法雖然能夠保證它所負責的partition裏未曾被消費的數據能夠被其他存活的實時節點分配且被消費,但是該不可用實時節點已經消費,但是沒有傳到deepStorage上且被其他歷史節點下載的segment數據卻會被集羣所遺漏,從而形成了這個基於druid-kafka-eight Firehose的消費方案的高可用性的一個缺陷,解決這個問題有兩種方式:
    1   想辦法讓不可用的實時節點重新貨到集羣可用的節點,那麼當它重新啓動時會將之前已經生成但尚未被上傳的segment數據文件統統加載回來,並最終將其合併並上傳到deepstorage,保證完整
    2   使用tranquility與索引服務對具體的kafka topic partition進行精確消費和備份,由於tranquility可以通過push的方式將指定的數據推向druid集羣,因此它可以同時對同一個partition製造多個副本,所以當某個數據消費的任務失敗,系統依然可以準確地選擇使用另一個相同任務所創建的segment數據塊。

歷史節點
歷史節點在啓動的時候,首先會檢查自己的本地緩存中已經存在的segment數據文件,然後從deepstorage中下載屬於自己但目前不再自己本地磁盤上的segment數據文件,無論是何種查詢,歷史節點都會首先將相關segment數據文件從磁盤加載到內存,然後在提供查詢服務。
不難看出,歷史節點的查詢效率受內存空間富裕程度的影響很大,內存空間富餘,查詢時需要從磁盤加載數據的次數就少,查詢速度就快。
原則上歷史節點的查詢速度與其內存空間大小和所負責的segment數據文件大小之比成正比關係。

對於一個分佈式系統來說,在規劃存儲時需要同時考慮硬件的異構性和數據溫度,異構性是指集羣中不同節點的硬件性能不同,數據溫度則是用來形容數據被訪問的頻繁程度。
熱數據: 經常被訪問,特點是總數據量不大,但要求響應速度最快
溫數據:不經常被訪問,特點是總的數據量一般,且要求響應速度儘量快
冷數據:偶爾被訪問,特點是總的數據量佔比最大,但響應速度不用很快
druid也考慮了這個問題,提出了層的概念 tier 將集羣中所有的歷史節點根據性能容量等指標分爲不同的層,並且可以讓不同性質的dataSource使用不同的層來存儲segment數據文件。
歷史節點有很高的可擴展性和高可用性,新的歷史節點被添加後,會通過zookeeper被協調節點發現,然後協調節點將會自動分配相關的segment給它,原有的歷史節點被移出集羣后,同樣會被協調節點發現,然後協調節點會將原本分配給它的segment重新分配給其他處於工作狀態的歷史節點。

查詢節點
同時從實時節點和歷史節點查詢數據,合併後返回給調用方。druid集羣中直接對外提供查詢的節點只有查詢節點,因此查詢節點是整個集羣的查詢中樞點。

很多數據庫都會利用緩存來存儲之前的查詢結果,當相似的查詢再次發生時,可以直接利用之前存儲在cache中的數據作爲部分或全部的結果,而不用再次訪問庫中的相關數據,這樣,在cache中的數據的命中率較高時,查詢效率也會得到明顯的提高。
Druid也是cache機制來提高自己的查詢效率,Druid提供了兩類介質以供選擇:
  • 外部cache ,比如mencache
  • 本地cache 例如查詢節點或者歷史節點的內存作爲cache
如果用查詢節點的內存做cache,查詢的時候會首先訪問其cache,只有當不命中的時候纔會去訪問歷史節點與實時節點以查詢數據。

一般一個druid集羣中只需要一個查詢節點即可,但是爲了防止出現單個節點失敗導致無查詢節點可用,通常會多加一個查詢節點到集羣中,無論訪問哪個查詢節點都能得到同樣的查詢結果,因此,在實踐中也常常會通過nginx作爲客戶端查詢的gateway來完成多個查詢節點的負載均衡,達到集羣高可用的效果。


協調節點
負責歷史節點數據的負載均衡,已經通過rule管理數據的生命週期。
對於整個druid集羣來說,其實並沒有真正意義上的master節點,因爲實時節點與查詢節點能自行管理並不受其他節點管理,但是對於歷史節點來說,協調節點便是他們的master,因爲協調節點將會給歷史節點分配數據,完成數據分佈在歷史節點間的負載均衡,當協調節點不可訪問時,歷史節點依然可以提供查詢服務,但是不能再接收新的segment數據了。
Druid利用針對每個DataSource設置的規則Rule來加載load或丟棄Drop具體的數據文件,以管理數據生命週期。可以對一個DataSource按順序添加多條規則,對於一個segment數據文件來說,協調節點會逐條檢查規則,當碰到當前segment數據文件符合某條規則的情況下,協調節點會立即命令歷史節點對該segment數據文件執行這條規則--加載或丟棄,並停止檢查餘下的規則,否則繼續檢查下一條設置好的規則。
副本:Druid允許用戶對某個dataSource定義其segment數據文件在歷史節點中的副本數量,副本數量默認爲1,即僅有一個歷史節點提供某segment數據文件的查詢,存在單點問題,如果用戶社會中更多的副本數量,則意味着某segment數據文件在集羣中存在多個歷史節點中,當某個歷史節點不可用的時候,還能從其他同樣擁有該segment數據文件副本的歷史節點中查詢到相關數據--segment數據文件的單點問題就解決了。同樣在集羣做升級的情況下也能保證集羣的查詢服務在次過程中不間斷。
高可用:只要在集羣中添加若干個協調節點即可,當某個協調節點退出服務,集羣中的其他協調節點依然自動完成相關工作。
索引服務
除了實時節點可以生產segment數據文件之外,Druid還提供了一組名爲索引服務的組件也可以生產segment數據文件,相比實時節點生產的segment,索引服務的優點是對數據能用pull的方式外,也可以支持push方式,不同於手工編寫數據消費配置文件的方式,可以通過API編程的方式來靈活定義任務配置,可以更靈活的管理與使用系統資源,可以完成segment副本數量的控制,能夠靈活完成跟segment數據文件相關的所有操作,如合併,刪除segment數據文件。
主從結構的架構:索引服務實際包括一組組件,以master-slave結構作爲架構,統治節點overlord node爲主節點,中間管理者middle manager爲從節點。

統治節點作爲索引服務的主節點,對外負責接受任務請求,對內負責將任務分解並下發到從節點(中間管理者),統治節點有以下兩種運行模式
  • 本地模式    默認的,在該模式下,統治節點不僅負責集羣的任務協調分配工作,也能負責啓動一些苦力(peon)來完成一部分具體的任務
  • 遠程模式 在該模式下,統治節點與中間管理節點分別運行在不同節點上,它不僅負責集羣的任務協調分配工作,不負責具體的任務,統治節點提供restful 的訪問方式,因此客戶端可以通過HTTP POST請求向統治節點提交任務。
客戶端可以發出查看任務狀態,接受segment等任務,同時統治節點也有個控制檯console

中間管理者是索引服務的工作節點,負責接受統治節點分配的任務,然後啓動祥光的苦力(獨立的JVM),類似於yarn



任務



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章