Druid架構概覽
什麼是Druid
Druid是一個高效的資料查詢系統,主要解決的是對於大量的基於時序的資料進行聚合查詢。資料可以實時攝入,進入到Druid後立即可查,同時資料是幾乎是不可變。通常是基於時序的事實事件,事實發生後進入Druid,外部系統就可以對該事實進行查詢。
Druid系統架構
Druid是一組系統,按照職責分成不同的角色。目前存在五種節點型別:
- Historical: 歷史節點的職責主要是對歷史的資料進行儲存和查詢,歷史節點從Deep Storage下載Segment,然後響應Broker對於Segment的查詢將查詢結果返回給Broker節點,它們通過Zookeeper來宣告自己儲存的節點,同時也通過zookeeper來監聽載入或刪除Segment的訊號。
- Coordinator:協調節點監測一組歷史節點來保證資料的可用和冗餘。協調節點讀取元資料儲存來確定哪些Segment需要load到叢集中,通過zk來感知Historical節點的存在,通過在Zookeeper上建立entry來和Historical節點通訊來告訴他們載入或者刪除Segment
- Broker:節點接收外部客戶端的查詢,並且將查詢路由到歷史節點和實時節點。當Broker收到返回的結果的時候,它將結果merge起來然後返回給呼叫者。Broker通過Zook來感知實時節點和歷史節點的存在。
- Indexing Service: 索引服務是一些worker用來從實時獲取資料或者批量插入資料。
- Realtime:獲取實時資料
下面這張圖片展示了一次查詢資料在整個架構中的流向
Druid資料流向除了上述五個節點,Druid還有三個外部依賴:
- Zookeeper叢集
- 元資料儲存例項:Mysql
- Deep Storage:HDFS
Segments
Druid 把它的索引儲存到一個Segment檔案中,Segment檔案是通過時間來分割的。
Segment資料結構
對於攝入到Druid的資料的列,主要分三種類型,時間列,指標列和維度列。如下圖
示例資料對於時間列和指標列處理比較簡單,直接用LZ4壓縮存起來就ok,一旦查詢知道去找哪幾行,只需要將它們解壓,然後用相應的操作符來操作它們就可以了。維度列就沒那麼簡單了,因為它們需要被過濾和聚合,因此每個維度需要下面三個資料結構。
- 一個map,Key是維度的值,值是一個整型的id
- 一個儲存列的值得列表,用1中的map編碼的list
- 對於列中的每個值對應一個bitmap,這個bitmap用來指示哪些行包含這個個值。
對於上圖的Page列,它的儲存是這樣的
1: 字典
{
"Justin BIeber": 0,
"Ke$ha": 1
}
2. 值的列表
[0,
0,
1,
1]
3. bitMap
value="Justin Bieber": [1, 1, 0, 0]
value="Ke$ha": [0, 0, 1, 1]
歷史節點
每個歷史節點維持一個和Zookeeper的長連線監測一組path來獲取新的Segment資訊。歷史節點互相不進行通訊,他們依靠zk來等待協調節點來協調。
協調節點負責把新的Segment分發給歷史節點,協調節點通過在zk的指定路徑下建立一個entry來向歷史節點做分發。
當歷史節點發現一個新的entry出現在path中,它首先會檢查本地檔案快取看有有沒Segment資訊,如果沒有Segment資訊,歷史節點會從zk上下載新的Segment的元資訊。Segment的元資訊包括Segment存在Deep Storage的位置和如何解壓和處理Segment。一旦一個歷史節點完成對一個Segment的處理,這個歷史節點會在zk上的一個路徑宣告對這個Segment提供查詢服務,此刻這個Segment就可以查詢了。
查詢節點
Broker節點負責將查詢路由到歷史節點和實時節點,Broker節點通過zk來知道哪些Segment存在哪個節點上。Broker也會把查詢的結果進行Merge
大多數Druid查詢包含一個區間物件,這個物件用來指定查詢所要查的區間段。Druid的Segment也通過時間段進行分割散落在整個叢集中。假設有一個簡單的資料來源,這個資料來源有七個Segment,每個Segment包含一週中的某一天的資料。任何一個時間範圍超過一天的查詢都會落到不止一個Segment上。這些Segment可能分佈在叢集中不同的節點上。因此這種查詢就會涉及到多個節點。
為了確定傳送到哪個節點上,Broker會從Historial和RealTime的節點來獲取他們提供查詢的Segment的資訊,然後構建一個時間軸,當收到特定的時間區間的查詢時,Broker通過時間軸來選擇節點。
Broker節點會維護一個LRU快取,快取存著每個Segment的結果,快取可以是一個本地的快取或者多個節點共用的外部的快取如 memcached。當Broker收到查詢時候,它首先將查詢對映成一堆Segment的查詢,其中的一個子集的結果可能已經存在快取中,他們可以直接從快取中拉出來,那些沒在快取中的將被髮送到相應節點。
協調節點
協調節點負責Segment的管理和分發,協調節點指揮歷史節點來載入或者刪除Segment,以及Segment的冗餘和平衡Segment。協調節點會週期性的進行掃描,每次掃描會根據叢集當前的狀態來決定進一步的動作。和歷史節點和Broker一樣,協調節點通過zk來獲取Segment資訊,同時協調節點還通過資料庫來獲取可用的Segment資訊和規則。在一個Segment提供查詢之前,可用的歷史節點會按照容量去排序,容量最小的具有最高的優先順序,協調節點就會讓它去載入這個Segment然後提供服務。
- 清理Segment,Druid會將叢集中的Segment和資料庫中的Segment進行對比,如果叢集有的的資料庫中沒有的會被清理掉。同事那些老的被新的替換的Segment也會被清理掉。
- Segment可用性, 歷史節點可能因為某種原因不可用,協調節點會發現節點不可用了,會將這個節點上的Segment轉移到其他的節點。Segment不會立即被轉移,如果在配置的時間段內節點恢復了,歷史節點會從本地快取載入Segment。恢復服務
- Segment負載均衡,協調節點會找到Segment最多的節點和Segment最少的節點,當他們的比例超過一個設定的值的時候,協調節點會從Segment最多的節點轉移到Segment最少的節點。
索引服務
索引服務是一個高可用的,分散式的服務來執行索引相關的Task。索引服務會建立或者銷燬Segment。索引服務是一個Master/Slave架構。索引服務是三個元件的集合
- peon元件用來跑索引任務。
- Middle Manager元件用來管理peons
- Overlord向MiddleManager分發任務。
索引服務
索引服務Overlord節點負責接受任務,協調任務分發,建立鎖,和返回狀態給呼叫者。Overlord節點可以以本地模式或者遠端模式執行。本地模式會直接建立Peon,遠端模式會通過Middle Manager建立任務。
實時節點
實時節點提供實時索引服務,通過實時節點索引的資料立即可查。實時節點會週期性的構建Segment,並且把這些Segment推到歷史節點並修改元資料。
實時節點