導讀:今天給大家帶來的分享主題是流批一體的實時多維分析。分享主要分為 4 個部分,首先是大資料架構演進介紹,從傳統的經典離線架構到 Lambda 架構, Kappa 架構,分析一下各種架構的優缺點;接下來介紹我們的業務痛點,以及流批一體的解決方案;第三部分會重點介紹我們的流批一體方案在落地過程中遇到的問題,以及我們的解決方法;最後是總結以及未來規劃。
主要內容如下:
分享嘉賓|鄭德來 百度 資深研發工程師
編輯整理|Brandon OPPO
出品社群|DataFun
在經典離線數倉架構中,最底層是資料來源,包括日誌打點、DB 儲存的資料,以及第三方回傳資料等等;ODS 層為資料操作層,主要用來做簡單的清洗,儲存一些基礎資料;在 ODS 層之上是 DWD 層,在該層會構建出最細粒度的明細事實表;DWD 層之上是 DWS 彙總資料層,在這一層會按主題對明細資料進行彙總;最上層是 ADS 應用資料層,存放業務的個性化統計指標。
經典離線數倉優點是架構簡單,開發成本與資源成本低,資料容易管理,同時因為沒有實時資料,也不會存在離線實時資料 diff 的問題。經典離線數倉最大的問題首先是資料延遲的問題,隨著業務的複雜性越來越高,數倉複雜度也會不斷的提升,需要關聯的資料來源也越來越多,資料的整體產出時效,就會有各種問題,包括輸入資料流斷流,資料延遲等在傳統數倉常見的問題。第二個問題是缺失實時資料,通常資料的產出時效都是小時或者天級別,有些資料量比較大可能會是周級別甚至月級別產出,無法支撐起業務對資料時效性的訴求。第三個問題是表的數量太多,因為一些複雜的業務場景 ODS 表可能有幾百張,產出資料時需要做大量的關聯,數倉使用體驗變差,同時關聯查詢也會導致資料產出的時效變差。
Lambda 架構出現的初衷是為了彌補經典數倉時效性差的問題。整個 Lambda 架構可以分為 3 層,首先圖中最左邊的是 Batch Layer 層,也就是批處理層,在批處理層複用了經典離線數倉的分層架構,在技術棧上也是與經典數倉一致,使用 MR, Hive, Spark 等離線計算框架。第二層是右邊的 Speed Layer 加速資料層,用於產出時效性高的一些資料。Speed Layer 層對資料準確性和完整性會有一些降級。Speed Layer 層採用 Kafka 等訊息中介軟體來進行資料的傳輸和儲存,使用 Storm,Spark Streaming,Flink 進行資料的計算。Lambda 架構的第三層就是 Server 層服務層,在服務層中會把 Speed 層和 Batch 層的資料進行合併,或者替換輸出到資料庫中來支援資料應用。
Lambda 層中的 Speed 層讓資料的產出時效大大提前了,同時具有 Speed 層和Batch 層讓 Lambda 架構兼顧了資料的準確性和時效性。另外Lambda架構中的 Batch 層沿用經典離線數倉的架構,在經典離線數倉遷移到 Lambda 架構時,Lambda 架構是可以相容經典數倉架構的。Lambda 架構的缺點就是一個需求會有兩套程式碼,一套離線,一套實時,造成了開發成本和運維成本的浪費。第二就是資源的浪費,一份離線資源一份實時資源,兩份資源導致整體資源佔用比較多。第三個問題就是資料的 diff 問題,因為它是實時和離線兩條流,它的資料邏輯本質是不可能完全對齊的,這也是 Lambda 架構最大的一個痛點。
Lambda 架構本身具有 Batch 和 Speed 兩條流,Speed 層的資料準確性是不被信任的,主要需要 Batch 層來保證資料的準確性,隨著流式計算框架的發展,不重不丟語義的支援,最終演變出了 Kappa 架構。
Kappa 架構的核心思想就是去掉 Lambda 架構的 Batch 層,實時和離線使用同一套程式碼,透過一套程式碼來滿足業務對資料的準確性和實時性的要求。Kappa 架構也不是完美的,比如我們正常的業務會經常遇到一些口徑的變更,這些口徑的變更會帶來資料回溯的問題,因為它是沒有離線這條資料流的,資料回溯就需要靠資料重新消費來解決。這種回溯成本非常高,對整個系統的吞吐壓力挑戰比較大。同時,隨著業務的複雜度不斷增加,它的資料來源的複雜度也會增加,隨著資料的增加,關聯場景比較多,會面臨各種複雜關聯場景的挑戰。關聯越來越複雜,開發和維護的成本也會變得非常高。第三個就是對於一些新業務,它的數倉基本是從零開始搭建,這個時候搭建一個 Kappa 架構會相對簡單,但是我們的數倉建設其實都是有歷史包袱的,並非一蹴而就,而是經過一定的演變過來的,這樣在改造時就需要對一些存量邏輯做實時的改造,Kappa架構是沒有 Batch 這條流的,需要完全替換調離線,這樣的改造往往成本是非常高的,同時收益比較小,這也是 Kappa 架構很難大規範鋪開的一個很關鍵原因。
首先看一下我們的舊架構,其實也一個 Lambda 架構,左邊深藍色是離線資料流,右邊淺藍這部分是實時資料流。離線資料流最底層是我們的資料來源層,資料來源主要有兩類,一類是日誌的打點資料,比如展現的打點,點選的打點,活動的打點。另一類是來源於業務的 DB 資料庫,比如 MySQL,主要是一些訂單資料或者物料資料。對於這些資料我們會進行採集,像打點資料一般會採用 Flume 這種日誌採集工具,小時級或者天級採集到檔案系統 HDFS 上。對於業務的資料庫資料,一般會採用類似與 Sqoop 工具採集到檔案系統上去。這兩部資料採集到檔案系統上之後,就是經過離線資料的清洗和處理,來構建我們的資料倉儲,資料倉儲也是採用經典的離線資料倉儲架構,再在資料倉儲上層承接多維分析以及資料包表需求。右邊實時流的日誌打點資料採集,我們是把它寫到訊息佇列裡面去,並不是所有日誌都採集到訊息佇列裡,因為訊息佇列成本比檔案系統高很多,所以有實時需求時我們才會把它採集到訊息佇列。對於業務 DB 資料也一樣,也是根據業務時效性需要,將 Binlog 日誌採集到訊息佇列裡。資料寫入到訊息佇列中之後就是流式計算環節,在流式計算環節中,主要會按照需求分別對資料進行加工,最終滿足策略訊號,實時報表,實時應用的一些需求。
第一個問題就是,表的數量非常的多,尤其是比較複雜的業務,ODS 表可能有幾百張,這時業務在使用起來就非常困難,尤其是一些人員的流動比較大,新人剛來對數倉沒有那麼熟悉,完全學習起來的成本很高。第二個問題就是表的關聯場景比較多,一次查詢經常要關聯幾十張表,查詢時效慢,這是業務比較敏感的一點。第三個問題是實時分析能力比較弱,因為只有一些定製的實時報表,只面向了某一個場景,缺少多維的實時資料分析能力。
整體上我們的流批一體方案其實是採用了一種 Lambda 和 Kappa 的混合架構。首先最底層的資料來源,資料採集和資料儲存這一塊,其實沒有大的變化,最關鍵的變化主要是資料清洗和數倉這兩個環節。首先,每個欄位我們會根據它的使用場景的時效性要求,來確定這個欄位是走實時流還是走離線流,比如某個欄位時效性要求在分鐘級別,這個時候就需要走實時流,而如果只是天級別,周級別那就沒必要走實時。但是如果一個欄位既有實時又有離線需求,這個時候實時和離線它不再是一個補充關係,而是一個替換關係,這樣來避免 Lambda 架構中最經典的問題,離線實時兩套程式碼導致的資料不一致問題。對於沒有時效性需求的欄位繼續沿用離線的處理邏輯,如果強行切換到實時流,訊息佇列和流式計算的成本比較高,同時也會增加開發和維護的成本。資料倉儲也由之前的分層建模的方式,變成了寬表建模,實時欄位和離線欄位透過分鐘級 Merge 成一張寬表。整體的建模思路也是不再面向資料來源建模,而是面向最終業務側的視角,業務使用去建模,那最關鍵的保證業務方在使用表的時候,表儘量少,儘量不做關聯,降低使用和學習成本,同時最主要是查詢效能,這是業務最敏感的一個點。然後對資料應用層,我們升級了一些資料查詢引擎。目前這個架構整個時效性基本是分鐘級別。端到端的耗時,根據資料量,關聯邏輯的複雜程度不一樣,它的時效性在 5 分鐘到 20 分鐘不等,滿足了我們業務對於實時多維分析的訴求。總結起來,業務的訴求是匯入時效要求是分鐘級別,查詢時效是秒級別。偶爾一些秒級別的延遲需求場景,比如類似大屏或者榜單,這部分我們還是會透過定製的實時報表實現。
在方案落地過程中,第一個問題是 DB 更新資料的問題。因為典型的實時數倉,比較簡單的一個場景就是純日誌場景,所有資料來源都來自於日誌的。一個典型的解決方案是,原始日誌經過實時採集,寫入訊息佇列中,在流式計算環節,透過固定的時間視窗,把資料寫入檔案系統,然後經過上層的查詢引擎去實時查詢。日誌資料有個特點就是它是不會變化的,日誌在列印的那一刻資料就固定下來了,但是 DB 資料不一樣,它是會更新的,比如訂單的一些狀態,收款退款,或者是一些物料屬性,它都會隨時發生變化。但因為我們的分散式檔案系統,往往它是不支援更新的,隨著計算視窗的變大,儲存能力和可維護性都會變差。
我們的解決方案,首先 DB 資料會採集變更資訊 Binlog 日誌,寫入到訊息佇列中。然後在流式計算框架對變更的日誌資料,比如透過分鐘級視窗,寫成一些 Delta 檔案,來儲存 DB 資料的變更過程。在初次上線過程中,我們會把 DB 的全量資料進行歸檔,產生 Base 檔案,然後會增加一個 Copy On Write 機制,透過滾動 5min 的合併將 Base 檔案和 Delta 檔案不斷進行合併,不斷產生最新的一個可用的版本。這裡我們沒有采用 Merge On Read 的這個方案,關鍵原因是在我們的業務訴求,業務對我們的查詢時效敏感,要求在秒級,如果使用 Merge On Read,就會導致我們在查詢過程中需要更多的資料處理,查詢時效會降低,所以這個一塊我們犧牲了一部分資料匯入效能,來儘量滿足查詢的效能要求。
第二個問題是多表關聯的問題,一個典型場景是,有一張 DB 的主表,然後幾張關聯表,透過 Spark 或者其它的離線計算框架,把它們關聯在一起然後寫入檔案系統中,供查詢引擎進行查詢。因為 DB 的資料一般記錄數特別多,每一個表的資料量都非常大,那在關聯的過程中可能會促發 Shuffle 機制,效能就會非常差。
首先對每一張表根據前面的方案產出一個 Base 檔案和一個 Delta 檔案,那 Base 檔案其實就包含了主表和關聯表的 Base 檔案,是一個截止到某個時刻包含全部記錄數的資料快照。Delta 檔案它其實是主表或關聯表在某一個流式計算時間視窗的變更記錄。第一次關聯是用主表的 Delta 檔案跟關聯表的 Base 檔案進行關聯,這樣就拿到這個主表在視窗內變更欄位的全部狀態,這個資料結果本寫入到一個 tmpDelta 檔案中。第二次關聯是將 tmpDelta 檔案與主表的 Base 檔案進行關聯,這樣就生成了在某個時間點的全部記錄數的 tmpBase 檔案,只不過有部分關聯欄位還沒更新。第三次關聯就是將上步中生成的 tmpBase 與關聯表的 Delta 檔案進行關聯,生成了一個新的可查詢版本。關聯表的變更欄位在主表檔案裡進行更新解釋,與上一個問題類似,會隨五分鐘的時間視窗不斷滾動下去。通常情況下,DB 資料的存量資料比較多,但增量資料比較少,所以 Base 檔案基本上非常大,而 Delta 檔案都會非常小,雖然是三次關聯,但每次關聯都存在小資料集,雖然整體關聯多次,但整體的時效性還是滿足預期的。
第三個問題是日誌 DB 資料和日誌資料關聯的問題。這個問題會更復雜一些,有一部分資料是來自日誌打點,另外一部分資料是來自業務 DB 資料庫,我們的寬表是同時依賴與這兩份資料。這個場景比較典型的方案就是,把 DB 資料寫入到一個類似於高效能的快取之中,在流式計算環節查詢這個快取,把需要依賴 DB 的那部分欄位拼接上,最終再透過一個固定的視窗寫入到檔案系統,給查詢引擎查詢。這裡有兩個問題,一個是要求快取的容量比較大,能夠快取下所有的 DB 資料,另外一個是日誌的吞吐非常高,也會導致在拼接欄位的過程中頻繁的查詢這個高效能的快取。這就對快取的 QPS 和容量有比較高的要求,導致快取的成本非常高。
我們的解決方案是,首先對日誌資料透過日誌採集寫入訊息佇列中,在流式計算環節,透過固定的視窗產出 Delta 檔案,Delta 檔案是不會變的,對於 DB 資料可能是一張表或者多張表,多張表就採用之前介紹的多表關聯的方案,也是滾動產出一個可查詢的版本。但這裡也有一些變化,採用了一個冷資料分離的方案,因為日誌的 Delta 檔案它是分鐘級滾動,合併的時候我們只合並熱資料,對冷資料是會進行天級別的合併,這個實現的降級主要是為了以最低成本來滿足業務最核心的訴求,因為業務最主要的訴求其實是熱資料能夠最快速到達,對於冷資料可以接受隔天查詢。它最關鍵的是資料準確性一致,整體也參考了 Lambda 的架構思想,雖然有冷熱資料不同的兩次合併,但合併的邏輯是一致的,不需要同時寫兩套程式碼,只不過在資源層面,新增了一份全量冷資料的關聯,會多消耗一些資源,但這也不可避免,這樣既滿足業務的需求,又不會過多的消耗資源。
我們建模從之前的分層建模改成了寬表建模,寬表建模最大的一個問題是資料到位時間問題。通常情況下寬表的產出需要依賴所有依賴表,實際情況資料來源往往比較複雜,一部分表是實時產出,而其它表需要複雜邏輯處理,可能需要 T+1, 甚至 T+2 才能產出。
針對資料到位時間問題,我們給出的方案是資料分版本產出,欄位按虛實化機制。對於一些原始日誌,DB 資料,透過前面介紹的幾個方案,基本可以保證我們資料欄位分鐘級產出可查詢版本。對於時效性不敏感的欄位,可以 T+1 產出生成一個 V2 的資料版本,而一些複雜計算的或者第三方回傳的欄位可以 T+2 產出生成最終的資料版本。同一張寬表,業務在使用時會展示欄位的可用狀態,預計產出的時間。
架構選型,首先要符合業務的現狀,解決業務實際問題,不要為了技術而做技術,關鍵是解決業務的實際問題,沒有最好的架構,只有最適合我們業務的架構。第二就是架構選型需要綜合考量資源複雜度與維護成本,尤其是資源,期望以最低的成本去解決業務痛點,把錢花在刀刃上。未來規劃一個是查詢引擎效能持續最佳化,來提升查詢體驗;另一個是上層查詢工具的體驗最佳化。
提問
Q1:流批一體架構下,數倉裡的大寬表是否會存在離線資料和實時資料的拼接,還是會嚴格分開離線寬表和實時寬表?A1:我們的寬表實時表和離線表是拼在一起的,是一張寬表,只不過是分版本產出。也是面向需求,如果一個欄位有實時訴求,那這個欄位是實時產出,如果對時效沒有那麼敏感,那可能就是天級別,周級別,甚至月級別都可以,只不過是多個版本。實時和離線使用的是一張寬表,這樣查詢的時候不需要再做二次拼接,保證查詢的時效性足夠快。Q2:10 億級多個大表關聯中,Delta 檔案不需要跟 Base 檔案關聯嗎?A2:Delta檔案是需要與 Base 檔案關聯,只不過它有關聯順序,Delta 檔案跟 Base檔案關聯,然後 Base 檔案再跟 Delta 檔案關聯,然後得 Base 檔案再跟其他 Delta 檔案關聯。有三次關聯,這裡邊是包含 Base 檔案和 Delta 檔案關聯的,因為 Delta 比較小,可以做一些 Mapjoin,Mapjoin 的效能會比較好一些,不像幾個大表的直接關聯,它的效能會比較差。A3:高效能快取技術有很多,比如 Redis、HBase 等,但我們最終選擇了檔案系統,主要是出於成本的考慮,我們在沒有增加太多資源的情況,就滿足了業務的時效性訴求。成本對我們研發來說壓力是比較大的,成本可能就是第一個比較要務的事情。Q4:數倉使用了什麼技術,Doris、Clickhouse、Hive 還是 Iceberg?A4:我們沒有使用開源的引擎,而是使用我們內部自研的圖靈引擎,它是基於檔案儲存的一個引擎。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2931888/,如需轉載,請註明出處,否則將追究法律責任。