Druid在有讚的實踐

有贊技術發表於2019-02-25

一、Druid介紹

Druid 是 MetaMarket 公司研發,專為海量資料集上的做高效能 OLAP (OnLine Analysis Processing)而設計的資料儲存和分析系統,目前Druid 已經在Apache基金會下孵化。Druid的主要特性:

  • 互動式查詢( Interactive Query ): Druid 的低延遲資料攝取架構允許事件在它們建立後毫秒內查詢,因為 Druid 的查詢延時通過只讀取和掃描有必要的元素被優化。Druid 是列式儲存,查詢時讀取必要的資料,查詢的響應是亞秒級響應。
  • 高可用性( High Available ):Druid 使用 HDFS/S3 作為 Deep Storage,Segment 會在2個 Historical 節點上進行載入;攝取資料時也可以多副本攝取,保證資料可用性和容錯性。
  • 可伸縮( Horizontal Scalable ):Druid 部署架構都可以水平擴充套件,增加大量伺服器來加快資料攝取,以及保證亞秒級的查詢服務
  • 並行處理( Parallel Processing ): Druid 可以在整個叢集中並行處理查詢
  • 豐富的查詢能力( Rich Query ):Druid支援 Scan、 TopN、 GroupBy、 Approximate 等查詢,同時提供了2種查詢方式:API 和 SQL

Druid常見應用的領域:

  • 網頁點選流分析
  • 網路流量分析
  • 監控系統、APM
  • 資料運營和營銷
  • BI分析/OLAP

二、為什麼我們需要用 Druid

有贊作為一家 SaaS 公司,有很多的業務的場景和非常大量的實時資料和離線資料。在沒有是使用 Druid 之前,一些 OLAP 場景的場景分析,開發的同學都是使用 SparkStreaming 或者 Storm 做的。用這類方案會除了需要寫實時任務之外,還需要為了查詢精心設計儲存。帶來問題是:開發的週期長;初期的儲存設計很難滿足需求的迭代發展;不可擴充套件。
在使用 Druid 之後,開發人員只需要填寫一個資料攝取的配置,指定維度和指標,就可以完成資料的攝入;從上面描述的 Druid 特性中我們知道,Druid 支援 SQL,應用 APP 可以像使用普通 JDBC 一樣來查詢資料。通過有贊自研OLAP平臺的幫助,資料的攝取配置變得更加簡單方便,一個實時任務建立僅僅需要10來分鐘,大大的提高了開發效率。

2.1、Druid 在有贊使用場景

  • 系統監控和APM:有讚的監控系統(天網)和大量的APM系統都使用了 Druid 做資料分析
  • 資料產品和BI分析:有贊 SaaS 服務為商家提供了有很多資料產品,例如:商家營銷工具,各類 BI 報表
  • 實時OLAP服務:Druid 為風控、資料產品等C端業務提供了實時 OLAP 服務

三、Druid的架構

Druid在有讚的實踐

Druid 的架構是 Lambda 架構,分成實時層( Overlord、 MiddleManager )和批處理層( Broker 和 Historical )。主要的節點包括(PS: Druid 的所有功能都在同一個軟體包中,通過不同的命令啟動):

  • Coordinator 節點:負責叢集 Segment 的管理和釋出,並確保 Segment 在 Historical 叢集中的負載均衡
  • Overlord 節點:Overlord 負責接受任務、協調任務的分配、建立任務鎖以及收集、返回任務執行狀態給客戶端;在Coordinator 節點配置 asOverlord,讓 Coordinator 具備 Overlord 功能,這樣減少了一個元件的部署和運維
  • MiddleManager 節點:負責接收 Overlord 分配的索引任務,建立新啟動Peon例項來執行索引任務,一個MiddleManager可以執行多個 Peon 例項
  • Broker 節點:負責從客戶端接收查詢請求,並將查詢請求轉發給 Historical 節點和 MiddleManager 節點。Broker 節點需要感知 Segment 資訊在叢集上的分佈
  • Historical 節點:負責按照規則載入非實時視窗的Segment
  • Router 節點:可選節點,在 Broker 叢集之上的API閘道器,有了 Router 節點 Broker 不在是單點服務了,提高了併發查詢的能力

四、有贊 OLAP 平臺的架構和功能解析

4.1 有贊 OLAP 平臺的主要目標:

  • 最大程度的降低實時任務開發成本:從開發實時任務需要寫實時任務、設計儲存,到只需填寫配置即可完成實時任務的建立
  • 提供資料補償服務,保證資料的安全:解決因為實時視窗關閉,遲到資料的丟失問題
  • 提供穩定可靠的監控服務:OLAP 平臺為每一個 DataSource 提供了從資料攝入、Segment 落盤,到資料查詢的全方位的監控服務

4.2 有贊 OLAP 平臺架構

Druid在有讚的實踐

有贊 OLAP 平臺是用來管理 Druid 和周圍元件管理系統,OLAP平臺主要的功能:

  • Datasource 管理
  • Tranquility 配置和例項管理:OLAP 平臺可以通過配置管理各個機器上 Tranquility 例項,擴容和縮容
  • 資料補償管理:為了解決資料遲延的問題,OLAP 平臺可以手動觸發和自動觸發補償任務
  • Druid SQL查詢: 為了幫助開發的同學除錯 SQL,OLAP 平臺整合了 SQL 查詢功能
  • 監控報警

4.2 Tranquility 例項管理

OLAP 平臺採用的資料攝取方式是 Tranquility工具,根據流量大小對每個 DataSource 分配不同 Tranquility 例項數量; DataSource 的配置會被推送到 Agent-Master 上,Agent-Master 會收集每臺伺服器的資源使用情況,選擇資源豐富的機器啟動 Tranquility 例項,目前只要考慮伺服器的記憶體資源。同時 OLAP 平臺還支援 Tranquility 例項的啟停,擴容和縮容等功能。

Druid在有讚的實踐

4.3 解決資料遲延問題——離線資料補償功能

流式資料處理框架都會有時間視窗,遲於視窗期到達的資料會被丟棄。如何保證遲到的資料能被構建到 Segment 中,又避免實時任務視窗長期不能關閉。我們研發了 Druid 資料補償功能,通過 OLAP 平臺配置流式 ETL 將原始的資料儲存在 HDFS 上,基於 Flume 的流式 ETL 可以保證按照 Event 的時間,同一小時的資料都在同一個檔案路徑下。再通過 OLAP 平臺手動或者自動觸發 Hadoop-Batch 任務,從離線構建 Segment。

Druid在有讚的實踐

基於 Flume 的 ETL 採用了 HDFS Sink 同步資料,實現了 Timestamp 的 Interceptor,按照 Event 的時間戳欄位來建立檔案(每小時建立一個資料夾),延遲的資料能正確歸檔到相應小時的檔案中。

4.4 冷熱資料分離

隨著接入的業務增加和長期的執行時間,資料規模也越來越大。Historical 節點載入了大量 Segment 資料,觀察發現大部分查詢都集中在最近幾天,換句話說最近幾天的熱資料很容易被查詢到,因此資料冷熱分離對提高查詢效率很重要。Druid 提供了Historical 的 Tier 分組機制與資料載入 Rule 機制,通過配置能很好的將資料進行冷熱分離。 首先將 Historical 群進行分組,預設的分組是"_default_tier",規劃少量的 Historical 節點,使用 SATA 盤;把大量的 Historical 節點規劃到 "hot" 分組,使用 SSD 盤。然後為每個 DataSource 配置載入 Rule :

  • rule1: 載入1份最近30天的 Segment 到 "hot" 分組;
  • rule2: 載入2份最近6個月的 Segment 到 "_default_tier" 分組;
  • rule3: Drop 掉之前的所有 Segment(注:Drop 隻影響 Historical 載入 Segment,Drop 掉的 Segment 在 HDFS 上仍有備份)
{"type":"loadByPeriod","tieredReplicants":{"hot":1}, "period":"P30D"} 
{"type":"loadByPeriod","tieredReplicants":{"_default_tier":2}, "period":"P6M"} 
{"type":"dropForever"}
複製程式碼

提高 "hot"分組叢集的 druid.server.priority 值(預設是0),熱資料的查詢都會落到 "hot" 分組。

Druid在有讚的實踐

4.5 監控與報警

Druid 架構中的各個元件都有很好的容錯性,單點故障時叢集依然能對外提供服務:Coordinator 和 Overlord 有 HA 保障;Segment 是多副本儲存在HDFS/S3上;同時 Historical 載入的 Segment 和 Peon 節點攝取的實時部分資料可以設定多副本提供服務。同時為了能在節點/叢集進入不良狀態或者達到容量極限時,儘快的發出報警資訊。和其他的大資料框架一樣,我們也對 Druid 做了詳細的監控和報警項,分成了2個級別:

  • 基礎監控
    包括各個元件的服務監控、叢集水位和狀態監控、機器資訊監控
  • 業務監控
    業務監控包括:實時任務建立、資料攝取 TPS、消費遲延、持久化相關、查詢 RT/QPS 等的關鍵指標,有單個 DataSource 和全域性的2種不同檢視;同時這些監控項都有設定報警項,超過閾值觸發報警提醒。業務指標的採集是大部分是通過 Druid 框架自身提供的 Metrics 和 Alerts 資訊,然後流入到 Kafka / OpenTSDB 等元件,通過流資料分析獲得我們想要的指標。

4.6 部署架構

Druid在有讚的實踐

Historical 叢集的部署和4.4節中描述的資料冷熱分離相對應,用 SSD 叢集儲存最近的N天的熱資料(可調節 Load 的天數),用相對廉價的 Sata 機型儲存更長時間的歷史冷資料,同時充分利用 Sata 的 IO 能力,把 Segment Load到不同磁碟上;在有贊有很多的收費業務,我們在硬體層面做隔離,保證這些業務在查詢端有足夠的資源;在接入層,使用 Router 做路由,避免了 Broker 單點問題,也能很大的程度叢集查詢吞吐量;在 MiddleManager 叢集,除了部署有 Index 任務(記憶體型任務)外,我們還混合部署了部分流量高 Tranquility 任務(CPU型任務),提高了 MiddleManager 叢集的資源利用率。

4.7 貢獻開源社群

在有贊業務查詢方式一般是 SQL On Broker/Router,我們發現一旦有少量慢查詢的情況,客戶端會出現查詢不響應的情況,而且連線越來越難獲取到。登入到Broker 的服務端後發現,可用連線數量急劇減少至被耗盡,同時出現了大量的 TCP Close_Wait。用 jstack 工具排查之後發現有 deadlock 的情況,具體的 Stack 請檢視 ISSUE-6867

經過原始碼排查之後發現,DruidConnection為每個 Statement 註冊了回撥。在正常的情況下 Statement 結束之後,執行回撥函式從 DruidConnection 的 statements 中 remove 掉自己的狀態;如果有慢查詢的情況(超過最長連線時間或者來自客戶端的Kill),connection 會被強制關閉,同時關閉其下的所有 statements ,2個執行緒(關閉connection的執行緒和正在退出 statement 的執行緒)各自擁有一把鎖,等待對方釋放鎖,就會產生死鎖現象,連線就會被馬上耗盡。

// statement執行緒退出時執行的回撥函式
final DruidStatement statement = new DruidStatement(
	connectionId,
	statementId,
	ImmutableSortedMap.copyOf(sanitizedContext),
	() -> {
	    // onClose function for the statement
	    synchronized (statements) {
	       log.debug("Connection[%s] closed statement[%s].", connectionId, statementId);
	       statements.remove(statementId);
	   }
	}
);
複製程式碼
// 超過最長連線時間的自動kill
return connection.sync(
    exec.schedule(
        () -> {
          log.debug("Connection[%s] timed out.", connectionId);
          closeConnection(new ConnectionHandle(connectionId));
        },
        new Interval(DateTimes.nowUtc(), config.getConnectionIdleTimeout()).toDurationMillis(),
        TimeUnit.MILLISECONDS
    )
);
複製程式碼

在排查清楚問題之後,我們也向社群提了 PR-6868 。目前已經成功合併到 Master 分支中,將會 0.14.0 版本中釋出。如果讀者們也遇到這個問題,可以直接把該PR cherry-pick 到自己的分支中進行修復。

五、挑戰和未來的展望

5.1 資料攝取系統

目前比較常用的資料攝取方案是:KafkaIndex 和 Tranquility 。我們採用的是 Tranquility 的方案,目前 Tranquility 支援了 Kafka 和 Http 方式攝取資料,攝取方式並不豐富;Tranquility 也是 MetaMarket 公司開源的專案,更新速度比較緩慢,不少功能缺失,最關鍵的是監控功能缺失,我們不能監控到例項的執行狀態,攝取速率、積壓、丟失等資訊。 目前我們對 Tranquility 的例項管理支援啟停,擴容縮容等操作,實現的方式和 Druid 的 MiddleManager 管理 Peon 節點是一樣的。把 Tranquility 或者自研攝取工具轉換成 Yarn 應用或者 Docker 應用,就能把資源排程和例項管理交給更可靠的排程器來做。

5.2 Druid 的維表 JOIN 查詢

Druid 目前並不沒有支援 JOIN查詢,所有的聚合查詢都被限制在單 DataSource 內進行。但是實際的使用場景中,我們經常需要幾個 DataSource 做 JOIN 查詢才能得到所需的結果。這是我們面臨的難題,也是 Druid 開發團隊遇到的難題。

5.3 整點查詢RT毛刺問題

對於 C 端的 OLAP 查詢場景,RT 要求比較高。由於 Druid 會在整點建立當前小時的 Index 任務,如果查詢正好落到新建的 Index 任務上,查詢的毛刺很大,如下圖所示:

Druid在有讚的實踐

我們已經進行了一些優化和調整,首先調整 warmingPeriod 引數,整點前啟動 Druid 的 Index 任務;對於一些 TPS 低,但是 QPS 很高的 DataSource ,調大 SegmentGranularity,大部分 Query 都是查詢最近24小時的資料,保證查詢的資料都在記憶體中,減少新建 Index 任務的,查詢毛刺有了很大的改善。儘管如此,離我們想要的目標還是一定的差距,接下去我們去優化一下原始碼。

5.4 歷史資料自動Rull-Up

現在大部分 DataSource 的 Segment 粒度( SegmentGranularity )都是小時級的,儲存在 HDFS 上就是每小時一個Segment。當需要查詢時間跨度比較大的時候,會導致Query很慢,佔用大量的 Historical 資源,甚至出現 Broker OOM 的情況。如果建立一個 Hadoop-Batch 任務,把一週前(舉例)的資料按照天粒度 Rull-Up 並且 重新構建 Index,應該會在壓縮儲存和提升查詢效能方面有很好的效果。關於歷史資料 Rull-Up 我們已經處於實踐階段了,之後會專門博文來介紹。

最後打個小廣告,有贊大資料團隊基礎設施團隊,主要負責有讚的資料平臺 (DP), 實時計算 (Storm, Spark Streaming, Flink),離線計算 (HDFS, YARN, HIVE, SPARK SQL),線上儲存(HBase),實時 OLAP (Druid) 等數個技術產品,歡迎感興趣的小夥伴聯絡 zhaojiandong@youzan.com

相關文章