大數據

騰訊看點基於 Flink 的實時數倉及多維實時數據分析實踐

當業務發展到一定規模,實時數據倉庫是一個必要的基礎服務。從數據驅動方面考慮,多維實時數據分析系統的重要性也不言而喻。但是當數據量巨大的情況下,拿騰訊看點來說,一天上報的數據量達到萬億級的規模,要實現極低延遲的實時計算和亞秒級的多維實時查詢是有技術挑戰的。

本文將介紹信息流場景下,騰訊看點的實時數據倉庫和多維實時數據分析系統的技術架構。

1、可解決的痛點

可以先看一下,多維實時數據分析系統可以解決哪些痛點。比如:

  • 推薦同學 10 分鐘前上了一個推薦策略,想知道在不同人群的推薦效果怎麼樣?
  • 運營同學想知道,在廣東省的用戶中,最火的廣東地域內容是哪些,方便做地域 Push。
  • 審核同學想知道,過去 5 分鐘,遊戲類被舉報最多的內容和賬號是哪些?
  • 老闆可能想了解,過去 10 分鐘有多少用戶在看點消費了內容,對消費人群有一個宏觀瞭解。

2、調研

1.jpg

在進行開發之前,我們做了這些調研。

  1. 離線數據分析平臺能否滿足這些需求,結論是不能滿足。離線數據分析平臺不行的原因如下。
  • C 側數據上報過來,需要經過 Spark 的多層離線計算,最終結果出庫到 MySQL 或者 ES 提供給離線分析平臺查詢。這個過程的延時最少 3-6 個小時,目前比較常見的都是提供隔天的查詢,所以很多實時性要求高的業務場景都是不能滿足的。
  • 另一個問題是,騰訊看點的數據量太大,帶來的不穩定性也比較大,經常會有預料不到的延遲。所以,離線分析平臺是無法滿足很多需求的。
  1. 實時數據分析平臺的話,事業群內部提供了準實時數據查詢的功能,底層技術用的是 Kudu+Impala,Impala 雖然是 MPP 架構的大數據計算引擎,並且訪問以列式存儲數據的 Kudu。但是對於實時數據分析場景來說,查詢響應的速度和數據的延遲都還是比較高,查詢一次實時 DAU,返回結果耗時至少幾分鐘,無法提供良好的交互式用戶體驗。所以(Kudu+Impala)這種通用大數據處理框架的速度優勢更多的是相比(Spark+Hdfs)這種離線分析框架來說的,對於我們這個實時性要求更高的場景,是無法滿足的。

3、項目背景

1-2.jpg

經過剛才的介紹,再來看下我們這個項目的背景。

作者發文的內容被內容中心引入,經過內容審核鏈路,啟用或者下架。啟用的內容給到推薦系統和運營系統,然後推薦系統和運營系統將內容進行 C 側分發。內容分發給 C 側用戶之後,用戶會產生各種行為,曝光、點擊、舉報等,通過埋點上報實時接入到消息隊列中。

接下來我們做了兩部分工作,就是圖中有顏色的這兩部分。

  • 第一部分構建了一個騰訊看點的實時數據倉庫。
  • 第二部分就是基於 OLAP 存儲引擎,開發了多維實時數據分析系統。

我們為什麼要構建實時數倉,因為原始的上報數據量非常大,一天上報峰值就有上萬億條。而且上報格式混亂。缺乏內容維度信息、用戶畫像信息,下游沒辦法直接使用。而我們提供的實時數倉,是根據騰訊看點信息流的業務場景,進行了內容維度的關聯,用戶畫像的關聯,各種粒度的聚合,下游可以非常方便的使用實時數據。

4、方案選型

1-3.jpg

那就看下我們多維實時數據分析系統的方案選型,選型我們對比了行業內的領先方案,選擇了最符合我們業務場景的方案。

  • 第一塊是實時數倉的選型,我們選擇的是業界比較成熟的 Lambda 架構,他的優點是靈活性高、容錯性高、成熟度高和遷移成本低;缺點是實時、離線數據用兩套代碼,可能會存在一個口徑修改了,另一個沒改的問題,我們每天都有做數據對賬的工作,如果有異常會進行告警。
  • 第二塊是實時計算引擎選型,因為 Flink 設計之初就是為了流處理,SparkStreaming 嚴格來說還是微批處理,Strom 用的已經不多了。再看 Flink 具有 Exactly-once 的準確性、輕量級 Checkpoint 容錯機制、低延時高吞吐和易用性高的特點,我們選擇了 Flink 作為實時計算引擎。
  • 第三塊是實時存儲引擎,我們的要求就是需要有維度索引、支持高併發、預聚合、高性能實時多維 OLAP 查詢。可以看到,Hbase、Tdsql 和 ES 都不能滿足要求,Druid 有一個缺陷,它是按照時序劃分 Segment,無法將同一個內容,存放在同一個 Segment上,計算全局 TopN 只能是近似值,所以我們選擇了最近兩年大火的 MPP 數據庫引擎 ClickHouse。

5、設計目標與設計難點

1-4.jpg

我們多維實時數據分析系統分為三大模塊

  1. 實時計算引擎
  2. 實時存儲引擎
  3. App層

難點主要在前兩個模塊:實時計算引擎和實時存儲引擎。

  1. 千萬級/s 的海量數據如何實時接入,並且進行極低延遲維表關聯。
  2. 實時存儲引擎如何支持高併發寫入、高可用分佈式和高性能索引查詢,是比較難的。

這幾個模塊的具體實現,看一下我們系統的架構設計。

6、架構設計

1-5.jpg

前端採用的是開源組件 Ant Design,利用了 Nginx 服務器,部署靜態頁面,並反向代理了瀏覽器的請求到後臺服務器上。

後臺服務是基於騰訊自研的 RPC 後臺服務框架寫的,並且會進行一些二級緩存。

實時數倉部分,分為了接入層、實時計算層和實時數倉存儲層。

  • 接入層主要是從千萬級/s 的原始消息隊列中,拆分出不同行為數據的微隊列,拿看點的視頻來說,拆分過後,數據就只有百萬級/s 了;
  • 實時計算層主要負責,多行行為流水數據進行行轉列,實時關聯用戶畫像數據和內容維度數據;
  • 實時數倉存儲層主要是設計出符合看點業務的,下游好用的實時消息隊列。我們暫時提供了兩個消息隊列,作為實時數倉的兩層。一層 DWM 層是內容 ID-用戶ID 粒度聚合的,就是一條數據包含內容 ID-用戶ID 還有 B 側內容數據、C 側用戶數據和用戶畫像數據;另一層是 DWS 層,是內容ID粒度聚合的,一條數據包含內容 ID,B 側數據和 C 側數據。可以看到內容 ID-用戶ID 粒度的消息隊列流量進一步減小到十萬級/s,內容 ID 粒度的更是萬級/s,並且格式更加清晰,維度信息更加豐富。

實時存儲部分分為實時寫入層、OLAP 存儲層和後臺接口層。

  • 實時寫入層主要是負責 Hash 路由將數據寫入;
  • OLAP 存儲層利用 MPP 存儲引擎,設計符合業務的索引和物化視圖,高效存儲海量數據;
  • 後臺接口層提供高效的多維實時查詢接口。

7、實時計算

1-7.jpg

這個系統最複雜的兩塊,實時計算和實時存儲。

先介紹實時計算部分:分為實時關聯和實時數倉。

7.1 實時高性能維表關聯

1-8.jpg

實時維表關聯這一塊難度在於。百萬級/s的實時數據流,如果直接去關聯 HBase,1 分鐘的數據,關聯完 HBase 耗時是小時級的,會導致數據延遲嚴重。

我們提出了幾個解決方案:

  • 第一個是,在 Flink 實時計算環節,先按照 1 分鐘進行了窗口聚合,將窗口內多行行為數據轉一行多列的數據格式,經過這一步操作,原本小時級的關聯耗時下降到了十幾分鍾,但是還是不夠的。
  • 第二個是,在訪問 HBase 內容之前設置一層 Redis 緩存,因為 1000 條數據訪問 HBase 是秒級的,而訪問 Redis 是毫秒級的,訪問 Redis 的速度基本是訪問 HBase 的 1000 倍。為了防止過期的數據浪費緩存,緩存過期時間設置成 24 小時,同時通過監聽寫 HBase Proxy 來保證緩存的一致性。這樣將訪問時間從十幾分鍾變成了秒級。
  • 第三個是,上報過程中會上報不少非常規內容 ID,這些內容 ID 在內容 HBase中是不存儲的,會造成緩存穿透的問題。所以在實時計算的時候,我們直接過濾掉這些內容 ID,防止緩存穿透,又減少一些時間。
  • 第四個是,因為設置了定時緩存,會引入一個緩存雪崩的問題。為了防止雪崩,我們在實時計算中,進行了削峰填谷的操作,錯開設置緩存的時間。

可以看到,優化前後,數據量從百億級減少到了十億級,耗時從小時級減少到了數十秒,減少 99%。

7.2 下游提供服務

1-9.jpg

實時數倉的難度在於:它處於比較新的領域,並且各個公司各個業務差距比較大,怎麼能設計出方便,好用,符合看點業務場景的實時數倉是有難度的。

先看一下實時數倉做了什麼,實時數倉對外就是幾個消息隊列,不同的消息隊列裡面存放的就是不同聚合粒度的實時數據,包括內容 ID、用戶ID、C 側行為數據、B 側內容維度數據和用戶畫像數據等。

我們是怎麼搭建實時數倉的,就是上面介紹的實時計算引擎的輸出,放到消息隊列中保存,可以提供給下游多用戶複用。

我們可以看下,在我們建設實時數據倉庫前後,開發一個實時應用的區別。沒有數倉的時候,我們需要消費千萬級/s 的原始隊列,進行復雜的數據清洗,然後再進行用戶畫像關聯、內容維度關聯,才能拿到符合要求格式的實時數據,開發和擴展的成本都會比較高,如果想開發一個新的應用,又要走一遍這個流程。有了數倉之後,如果想開發內容 ID 粒度的實時應用,就直接申請 TPS 萬級/s 的 DWS 層的消息隊列。開發成本變低很多,資源消耗小很多,可擴展性也強很多。

看個實際例子,開發我們系統的實時數據大屏,原本需要進行如上所有操作,才能拿到數據。現在只需要消費 DWS 層消息隊列,寫一條 Flink SQL 即可,僅消耗 2 個 CPU 核心,1G 內存。

可以看到,以 50 個消費者為例,建立實時數倉前後,下游開發一個實時應用,可以減少 98%的資源消耗。包括計算資源,存儲資源,人力成本和開發人員學習接入成本等等。並且消費者越多,節省越多。就拿 Redis 存儲這一部分來說,一個月就能省下上百萬人民幣。

8、實時存儲

1-10.jpg

介紹完實時計算,再來介紹實時存儲。

這塊分為三個部分來介紹:

  • 第一是 分佈式-高可用
  • 第二是 海量數據-寫入
  • 第三是 高性能-查詢

8.1 分佈式-高可用

1-11.jpg

我們這裡聽取的是 Clickhouse 官方的建議,藉助 ZK 實現高可用的方案。數據寫入一個分片,僅寫入一個副本,然後再寫 ZK,通過 ZK 告訴同一個分片的其他副本,其他副本再過來拉取數據,保證數據一致性。

這裡沒有選用消息隊列進行數據同步,是因為 ZK 更加輕量級。而且寫的時候,任意寫一個副本,其它副本都能夠通過 ZK 獲得一致的數據。而且就算其它節點第一次來獲取數據失敗了,後面只要發現它跟 ZK 上記錄的數據不一致,就會再次嘗試獲取數據,保證一致性。

8.2 海量數據-寫入

1-12.jpg

數據寫入遇到的第一個問題是,海量數據直接寫入 Clickhouse 的話,會導致 ZK 的 QPS 太高,解決方案是改用 Batch 方式寫入。Batch 設置多大呢,Batch 太小的話緩解不了 ZK 的壓力,Batch 也不能太大,不然上游內存壓力太大,通過實驗,最終我們選用了大小幾十萬的 Batch。

第二個問題是,隨著數據量的增長,單 QQ 看點的視頻內容每天可能寫入百億級的數據,默認方案是寫一張分佈式表,這就會造成單臺機器出現磁盤的瓶頸,尤其是 Clickhouse 底層運用的是 Mergetree,原理類似於 HBase、RocketsDB 的底層 LSM-Tree。在合併的過程中會存在寫放大的問題,加重磁盤壓力。峰值每分鐘幾千萬條數據,寫完耗時幾十秒,如果正在做 Merge,就會阻塞寫入請求,查詢也會非常慢。我們做的兩個優化方案:一是對磁盤做 Raid,提升磁盤的 IO;二是在寫入之前進行分表,直接分開寫入到不同的分片上,磁盤壓力直接變為 1/N。

第三個問題是,雖然我們寫入按照分片進行了劃分,但是這裡引入了一個分佈式系統常見的問題,就是局部的 Top 並非全局 Top 的問題。比如同一個內容 ID 的數據落在了不同的分片上,計算全局 Top100 閱讀的內容 ID,有一個內容 ID 在分片 1 上是 Top100,但是在其它分片上不是 Top100,導致彙總的時候,會丟失一部分數據,影響最終結果。我們做的優化是在寫入之前加上一層路由,將同一個內容 ID 的記錄,全部路由到同一個分片上,解決了該問題。

介紹完寫入,下一步介紹 Clickhouse 的高性能存儲和查詢。

8.3 高性能-存儲-查詢

Clickhouse 高性能查詢的一個關鍵點是稀疏索引。稀疏索引這個設計就很有講究,設計得好可以加速查詢,設計不好反而會影響查詢效率。我根據我們的業務場景,因為我們的查詢大部分都是時間和內容 ID 相關的,比如說,某個內容,過去 N 分鐘在各個人群表現如何?我按照日期,分鐘粒度時間和內容 ID 建立了稀疏索引。針對某個內容的查詢,建立稀疏索引之後,可以減少 99%的文件掃描。

還有一個問題就是,我們現在數據量太大,維度太多。拿 QQ 看點的視頻內容來說,一天流水有上百億條,有些維度有幾百個類別。如果一次性把所有維度進行預聚合,數據量會指數膨脹,查詢反而變慢,並且會佔用大量內存空間。我們的優化,針對不同的維度,建立對應的預聚合物化視圖,用空間換時間,這樣可以縮短查詢的時間。

1-13.jpg

分佈式表查詢還會有一個問題,查詢單個內容 ID 的信息,分佈式表會將查詢下發到所有的分片上,然後再返回查詢結果進行彙總。實際上,因為做過路由,一個內容 ID 只存在於一個分片上,剩下的分片都在空跑。針對這類查詢,我們的優化是後臺按照同樣的規則先進行路由,直接查詢目標分片,這樣減少了 N-1/N 的負載,可以大量縮短查詢時間。而且由於我們是提供的 OLAP 查詢,數據滿足最終一致性即可,通過主從副本讀寫分離,可以進一步提升性能。

我們在後臺還做了一個 1 分鐘的數據緩存,針對相同條件查詢,後臺就直接返回了。

8.4 擴容

這裡再介紹一下我們的擴容的方案,調研了業內的一些常見方案。

比如 HBase,原始數據都存放在 HDFS 上,擴容只是 Region Server 擴容,不涉及原始數據的遷移。但是 Clickhouse 的每個分片數據都是在本地,是一個比較底層存儲引擎,不能像 HBase 那樣方便擴容。

Redis 是哈希槽這種類似一致性哈希的方式,是比較經典分佈式緩存的方案。Redis slot 在 Rehash 的過程中雖然存在短暫的 ask 讀不可用,但是總體來說遷移是比較方便的,從原 h[0]遷移到 h[1],最後再刪除 h[0]。但是 Clickhouse 大部分都是 OLAP 批量查詢,不是點查,而且由於列式存儲,不支持刪除的特性,一致性哈希的方案不是很適合。

目前擴容的方案是,另外消費一份數據,寫入新 Clickhouse 集群,兩個集群一起跑一段時間,因為實時數據就保存 3 天,等 3 天之後,後臺服務直接訪問新集群。

9、成果

騰訊看點實時數據倉庫:DWM 層和 DWS 層,數據延遲 1 分鐘。

遠見多維實時數據分析系統:亞秒級響應多維條件查詢請求,在未命中緩存情況下,過去 30 分鐘的查詢,99%的請求耗時在 1 秒內;過去 24 小時的查詢,90%的請求耗時在 5 秒內,99%的請求耗時在 10 秒內。

更多 Flink 技術交流可加入 Flink 社區釘釘大群。

最新釘群二維碼.jpeg

Leave a Reply

Your email address will not be published. Required fields are marked *