導讀:本文將描述 Apache Druid 的基本集羣架構,說明架構中各進程的做用。並從數據寫入和數據查詢兩個角度來講明 Druid 架構的工做流程。html
關注公衆號 MageByte,設置星標點「在看」是咱們創造好文的動力。公衆號後臺回覆 「加羣」 進入技術交流羣獲更多技術成長。算法
Druid 是多進程架構,每種進程類型均可以獨立配置,獨立擴展。這樣能夠爲集羣提供最大的靈活度。這種設計還提供了強失效容忍:一個失效的組件不會當即影響另外的組件。sql
下面咱們來深刻了解 Druid 有哪些進程類型,每種進程又在整個集羣中扮演什麼角色。數據庫
Druid 有多種進程類型,以下:apache
你能夠以任何方式來部署上面的進程。可是爲了易於運維,官方建議如下面三種服務類型來組織進程:Master、Query 和 Data。服務器
除了內置的進程類型,Druid 還有三個外部依賴項。網絡
共享文件存儲,只要配置成容許 Druid 訪問便可。在集羣部署中,一般使用分佈式存儲(如 S3 或 HDFS)或掛載網絡文件系統。在單機部署中,一般使用本地磁盤。Druid 使用 Deep Storage 存儲寫入集羣的數據。架構
Druid 僅將 Deep Storage 用做數據的備份,並做爲 Druid進程間在後臺的數據傳輸方式。要響應查詢,Historical 進程並不從 Deep Storage 上讀取數據,在任何查詢以前,先從本地磁盤查詢已經存在的數據。這意味着,Druid 在查詢時並不須要訪問 Deep Storage,這樣就能夠獲得最優的查詢延遲。這也意味着,在 Deep Storage 和 Historical 進程間你必須有足夠的磁盤空間來存儲你計劃加載的數據。app
Deep Storage 是 Druid 彈性、容錯設計的重要組成部分。若是 Druid 單機進程本地數據丟失,能夠從 Deep Storage 恢復數據。運維
元數據存儲,存儲各類共享的系統元數據,如 segment 可用性信息和 task 信息。在集羣部署中,一般使用傳統的 RDBMS,如 PostgreSQL 或 MySQL。在單機部署中,一般使用本地存儲,如 Apache Derby 數據庫。
用來進行內部服務發現,協調,和主選舉。
下圖能夠看出使用官方建議的 Master/Query/Data 服務部署方式,查詢和寫入數據是如何進行的:
Druid 數據存儲在"datasources"中,它就像 RDBMS 中的 table。每個 datasources 經過時間分區,或經過其餘屬性進行分區。每個時間範圍稱之爲"chunk"(好比,一天一個,若是你的 datasource 使用 day 分區)。在 chunk 中,數據被分區進一個或多個"segments"中。每個 segment 是一個單獨的文件,一般包含數百萬行數據。一旦 segment 被存儲進 chunks,其組織方式將如如下時間線所示:
一個 datasource 也許只有一個,也可能有數十萬甚至上百萬個 segment。每一個 segment 生命週期開始於 MiddleManager 建立時,剛被建立時,segment 是可變和未提交的。segment 構建過程包含如下幾步,旨在生成結構緊湊並支持快速查詢的數據文件。
segment 定時提交和發佈。此時,數據被寫入 Deep Storage,而且再不可變,並從 MiddleManagers 進程遷移至 Historical 進程中。一個關於 segment 的 entry 將寫入 metadata storage。這個 entry 是關於 segment 的元數據的自描述信息,包含如 segment 的數據模式,大小,Deep Storage 地址等信息。這些信息讓 Coordinator 知道集羣中哪些數據是可用的。
indexing 是每一個 segment 建立的機制。handoff 是數據被髮布並開始能夠被 Historical 進程處理的機制。這機制在 indexing 側的工做順序以下:
這機制在 Coordinator/Historical 側的工做以下:
數據寫入(indexing)和移交(handoff):
Segment 標識由下面四部分組成:
segmentGranularity
指定參數)。例如,這是 datasource 爲clarity-cloud0
,時間段爲2018-05-21T16:00:00.000Z/2018-05-21T17:00:00.000Z
,版本號爲2018-05-21T15:56:09.909Z
,分區號爲 1 的標識符:
clarity-cloud0_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T15:56:09.909Z_1
分區號爲 0(塊中的第一個分區)的 segment 省略了分區號,如如下示例所示,它是與前一個分區在同一時間塊中的 segment,但分區號爲 0 而不是 1:
clarity-cloud0_2018-05-21T16:00:00.000Z_2018-05-21T17:00:00.000Z_2018-05-21T15:56:09.909Z
你可能想知道上一節中描述的「版本號」是什麼。
Druid 支持批處理模式覆寫。在 Driud 中,若是你要作的只是追加數據,那麼每一個時間塊只有一個版本。可是,當你覆蓋數據時,在幕後發生的事情是使用相同的數據源,相同的時間間隔,但版本號更高的方式建立了一組新的 segment。這向 Druid 系統的其他部分發出信號,代表應從羣集中刪除較舊的版本,而應使用新版本替換它。
對於用戶而言,切換彷佛是瞬間發生的,由於 Druid 經過先加載新數據(但不容許對其進行查詢)來處理此問題,而後在全部新數據加載完畢後,當即將新查詢切換到新 segment。而後,它在幾分鐘後刪除舊 segment。
每一個 segment 的生命週期都涉及如下三個主要領域:
元數據存儲區
中。將 segmnet 的記錄插入元數據存儲的操做稱爲發佈。而後將元數據中的use
布爾值設置成可用
。由實時任務建立的 segment 將在發佈以前可用,由於它們僅在 segment 完成時才發佈,而且不接受任何其餘數據。你可使用 Druid SQL sys.segments
表檢查當前 segment 的狀態 。它包括如下標誌:
is_published
:若是 segment 元數據已發佈到存儲的元數據中,used
則爲 true,此值也爲 true。is_available
:若是該 segment 當前可用於實時任務或Historical查詢,則爲 True。is_realtime
:若是 segment 在實時任務上可用,則爲 true 。對於使用實時寫入的數據源,一般會先設置成true
,而後隨着 segment 的發佈和移交而變成false
。is_overshadowed
:若是該 segment 已發佈(used
設置爲 true)而且被其餘一些已發佈的 segment 徹底覆蓋,則爲 true。一般,這是一個過渡狀態,處於此狀態的 segment 很快就會將其used
標誌自動設置爲 false。查詢首先進入Broker
進程,Broker
將得出哪些 segment 具備與該查詢有關的數據(segment 列表始終按時間規劃,也能夠根據其餘屬性來規劃,這取決於數據源的分區方式),而後,Broker
將肯定哪些 Historical
和 MiddleManager
正在爲這些 segment 提供服務,並將重寫的子查詢發送給每一個進程。Historical
/ MiddleManager
進程將接受查詢,對其進行處理並返回結果。Broker
接收結果並將它們合併在一塊兒以獲得最終答案,並將其返回給客戶端。
Broker
會分析每一個請求,優化查詢,儘量的減小每一個查詢必須掃描的數據量。相比於 Broker 過濾器作的優化,每一個 segment 內的索引結構容許 Druid 在查看任何數據行以前先找出哪些行(若是有)與過濾器集匹配。一旦 Druid 知道哪些行與特定查詢匹配,它就只會訪問該查詢所需的特定列。在這些列中,Druid 能夠在行與行之間跳過,從而避免讀取與查詢過濾器不匹配的數據。
所以,Druid 使用三種不一樣的技術來優化查詢性能:
檢索每一個查詢需訪問的 segment。
在每一個 segment 中,使用索引來標識查詢的行。
其餘系列文章連接:
想了解更多數據存儲,時間序列,Druid 的知識,可關注個人公衆號。點「在看」是咱們創造好文的動力