druid.io剖析
簡介
druid做爲如今最有潛力的海量數據實時分析系統,在優酷廣告團隊中扮演者很是重要的角色node
總體架構
![總體架構](http://static.javashuo.com/static/loading.gif)
如今已經用tranquility+indexing service替換realtimemysql
實時數據經由tranquility被推送到Indexing Service,而後生成索引(Segment),同時提供來自用戶的 查詢請求。當索引所在的時間段過去之後,Indexing Service會將索引推送到deep storage。 索引信息會被註冊到metadata中。協調節點定時同步metadata,感知新生成的Segment, 並經過Zookeeper去通知歷史節點加載索引。git
除了實時加載數據,Druid也支持批量導入數據。導入的數據生成索引segment,寫入deep storage, 經與上述一樣的步驟,經過修改metadata信息將segment加載到歷史節點。github
Lambda架構思想
Lamda架構用來同時處理離線和實時數據算法
![lambda架構](http://static.javashuo.com/static/loading.gif)
druid藉助lambda思想,很好的解決了實時處理邏輯會丟棄時間窗口之外的數據的問題。sql
- 實時:經過tranquility拉取數據並導入druid
- 離線:經過druid提供的Hadoop Indexer(其實是mr任務)獲取hdfs數據,生成segment並導入hdfs,並將索引元信息導入mysql。druid定時輪詢mysql並獲取最新索引數據,最後經過指定負載均衡算法分配給工做節點
依賴
kafka(16c32g480gx8)
TT日誌-->storm etl-->kafkajson
kafka做爲storm和druid之間的緩衝緩存
tranquility(4c8g120gx9)
/home/admin/druid-tranquility/default架構
config目錄存儲了不少json文件,定義了數據源的schema,也就是druid表結構,全部schema中配置的segmentGranularity都是hour級別, queryGranularity有hour也有minute級別併發
重要參數
segmentGranularity不等於queryGranularity
- segmentGranularity:索引粒度,也就是一個segment文件包含的數據時間範圍
- queryGranularity:查詢粒度,也就是最小聚合粒度,表明數據存儲的時候,在維度相同的狀況下,同一查詢粒度範圍內的數據會自動被聚合,致使查詢的時候只能查到該粒度級別的數據
- intermediatePersistPeriod:定時持久化增量索引的週期,目前大可能是5min
- windowPeriod:時間窗口,表示若是數據時間比當前時間老或者比當前時間新,超過該窗口範圍以外的所有被丟棄,目前大可能是10min,也有5min
推薦配置:intermediatePersistPeriod ≤ windowPeriod < segmentGranularity,queryGranularity<= segmentGranularity
tranquility工做流程:
![tranquility](http://static.javashuo.com/static/loading.gif)
簡而言之,tranquility會作兩件事:
- 創建索引任務併發給overlord:tranquility發送的task會被overlord接受,最後會佔用middle-manager的一個空閒slot。爲防止太多task,tranquility會爲同一個segmentGranularity範圍以內的task分配同一個id,這個全部發送過去的task都會被合併。還有兩個配置項影響task數量,tranquility能夠在schema中爲每一個數據源配置partitions和replicants,一個小時之內請求的task數量= partitions * replicants。目前middle-manager的全部slot數量均可以在overlord UI查看,能夠根據剩餘slot數量來修改配置中的partitions和replicants參數。創建的task主要目的是明確每一個tranquility該往哪一個peon發送實時數據,即實時數據在衆多peon中負載均衡的策略(稍後討論handoff階段)
- 將實時數據發送給peon進程:peon會經過EventReceiverFirehose服務暴露一個http接口,tranquility經過zookeeper獲取task的分配信息,明確實時數據該往哪一個peon發,並將peon暴露的接口發起post請求,提交實時數據
內部組件
indexing service
indexing service分爲三個組件(工做進程),用相似storm的nimbus->supervisor->worker的方式工做
- overlord(4c8g120gx2):接收tranquility請求的實時索引task,選擇slot空閒最多的middle-manager,經過zk將task分配給middle-manager,填滿爲止。目前overlord兩臺機器,master-slave結構
- middle-manager(4c8g120gx51):經過zk獲取task,啓動本地進程peon執行task
- peon:獲取實時數據,執行task,完成索引創建。peon自己還負責索引查詢服務
index service接收tranquility請求並處理的整個流程:
![indexing service](http://static.javashuo.com/static/loading.gif)
peon完成了索引build,merge,handoff的整個生命週期
![peon-flow](http://static.javashuo.com/static/loading.gif)
每一個middle-manger有N個slot,對應N個peon,每次分配一個索引task就會建立一個peon進程,這個小時之內peon會佔據這個slot,等完成handoff以後才釋放
這個小時之內,peon會不斷生成增量索引,定時持久化索引,合併索引生成segment,最後handoff segment
handoff流程:
![handoff](http://static.javashuo.com/static/loading.gif)
historical(16c32g480gx10)
historical提供索引加載和查詢服務
![historical](http://static.javashuo.com/static/loading.gif)
歷史節點在從下載segment前,會從本地緩存檢查是否存在,若是不存在才從hdfs下載。下載完成以後,會根據zk獲取到的壓縮信息進行解壓處理並加載到內存,這時就能提供查詢服務。
能夠經過配置給歷史節點劃分不一樣的層,而後在coordinator配置規則來加載指定數據源到某個層。這樣能夠實現冷熱數據劃分處理,熱數據查詢多存量小,採用更好的cpu和內存機型配置,冷數據查詢少存量大,採用更大的硬盤機型配置
broker(16c32g480gx2)
broker負責查詢索引,目前是master-master結構
broker是查詢節點,負責接受查詢請求,解析查詢對象中的時間範圍,根據時間範圍將實時索引請求(當前小時)路由到peon節點,將歷史索引請求(1小時以前)路由到historical節點。接收peon和historical查詢返回的數據,在作一次合併,最後返回結果
爲了提升查詢效率,broker會將查詢結果緩存(LRU),目前提供了兩種方式:
- heap memory(目前使用)
- kv存儲,如memcached
只會緩存歷史節點返回的數據,由於peon返回的實時數據常常改變,沒有緩存的價值
coordinator(4c8g120gx2)
coordinator會協調歷史節點中segment的分配
- rules:每分鐘從mysql拉取druid_rules和druid_segments,rules用來告知historical將如何load和drop索引文件,coordinator會讀取這些rules,而後修改zk,通知historical加載刪除指定的segment,這些均可以在coordinator的UI配置
- load balance:根據zk中每一個historical node負責的segment量,作負載均衡
- replication:在coordinator的UI中配置rules時,能夠同時配置加載segment的備份數量,這些備份數量會以load balance的形式,分配到多個historical上面。這個備份數量與hdfs的segment備份數量不同,hdfs那個保證深度存儲的數據不會丟失,historical上面備份是爲了保證當某個historical掛掉的時候,其餘存儲了備份segment的節點能接着提供查詢服務
外部依賴
zookeeper
druid依賴zk實現集羣之間的交互
druid採用shard-nothing架構,每一個節點之間不直接和其餘打交道,而是採用zk來溝通。這樣保證了druid自己的HA特性
peon和historical發佈索引
- /druid/announcements:聲明全部peon和historical的host
- /druid/segments:記錄全部peon和historical的host,以及他們負責的索引
提供indexing service相關數據(overlord頁面數據來源)
- /druid/indexer
- leaderLatchPath:overlord選主
- tasks:運行的peon任務
- status:peon任務狀態
- announcements:聲明middle-manager的capacity
coordinator用來通知historical加載卸載索引
- /druid/loadQueue/_historical_host/_segement_id:記錄歷史節點所負責的segment
coordinator選主
- coordinator:記錄coordinator信息
集羣通訊
附屬功能
- /druid/listeners:存儲lookup數據
deep storage —— hdfs
存儲索引文件
metadata storage —— mysql
存儲元數據
- druid_segments:索引元數據,數據源、是否可用、大小、維度、指標
- druid_rules:通知historical該如何加載、卸載索引的規則,能夠在coordinator配置
- druid_config:存放運行時配置信息
- druid_audit:記錄配置、規則的變化
- druid_task(相關的幾張表):overlord用來存放索引task數據,防止overlord掛掉致使task丟失
索引文件
segment就是壓縮後的索引文件,命名方式爲datasource_intervalStart_intervalEnd_version_partitionNum。如dsp_report_2011-01-01T01:00:00Z_2011-01-01T02:00:00Z_v1_0,表明dsp_report數據源,從2011-01-01那天1點到2點的數據,版本號爲v1,分區數爲0
深刻剖析segment存儲結構
- version.bin:4字節,記錄segment version
- XXXXX.smoosh:該文件存放多個邏輯意義上的子文件,經過記錄offset來管理這些子文件。有的子文件存放了column信息,有的存放了索引元信息。column信息也就是真實存儲的數據
- meta.smoosh:上面這些子文件名稱以及他們出現的offset都記錄在meta.smoosh中
XXXXX.smoosh中存放的column是最重要的,能夠分爲Timestamp, Dimensions, Metrics三部分。
timestamp |
domain |
advertiser |
device |
city |
click |
cost |
2015-11-25T10:00:00Z |
youku.com |
BMW |
Android |
Peking |
9 |
0.9 |
2015-11-25T10:00:00Z |
youku.com |
BMW |
Iphone |
HongKong |
3 |
0.3 |
2015-11-25T10:00:00Z |
tudou.com |
PANDORA |
Iphone |
HongKong |
2 |
0.2 |
2015-11-25T10:00:00Z |
tudou.com |
PANDORA |
Iphone |
Peking |
1 |
0.1 |
- Timestamp:用時間戳來表示時間,能夠用一系列時間戳表示該segment全部Timestamp列信息,採用LZ4算法壓縮
- Metrics:也是數字,存放方法同上,壓縮算法同上
- Dimensions:因爲Dimensions大可能是字符串,採用上面的存放方式沒法很好壓縮。目前Dimensions拆分紅多個結構進行存儲。
Dimensions結構:
- 將字符串映射爲整數id的字典
- 記錄該dimension每一行的值,值用上述字典編碼
- 爲每一個不一樣dimension的值,定義一個bitmap,存儲該值出現的行號,採用roaring壓縮算法
上述結構中,1能夠有效減小索引文件的大小,2的基礎上作排序能夠很方便的作groupby合併處理,3是快速完成where條件查詢的利器。
內存管理
druid使用了三種不一樣類型的內存:
- 堆內存:broker用來緩存查詢結果、簡單計算
- 直接內存:通常用來存儲聚合操做中所產生的臨時數據
- MMap:歷史節點用來加載segment,快速,減小一次系統複製操做。memory_for_segments = total_memory - heap - direct_memory - jvm_overhead,segment可用的內存越小,mmap操做就會致使更多的內存換頁操做
參考資料