天天看點

湖倉一體架構在火山引擎 LAS 的探索與實踐

作者:位元組跳動技術團隊

火山引擎湖倉一體分析服務 LAS(Lakehouse Analytics Service),是面向湖倉一體架構的 Serverless 資料處理分析服務,提供位元組跳動最佳實踐的一站式 EB 級海量資料存儲計算和互動分析能力,相容 Spark、Presto 生态,幫助企業輕松建構智能實時湖倉。

LAS 服務是什麼?LAS 有哪些優化特性?本文将從基礎概念、資料庫核心特性優化、資料服務化、業務實踐等角度全方位介紹湖倉一體架構在LAS的探索與實踐。

LAS服務是什麼?

在了解 Las 服務是什麼之前,先來了解一下資料平台整體行業的發展趨勢,大概分為三個階段。

湖倉一體架構在火山引擎 LAS 的探索與實踐

第一階段,一般被稱為傳統數倉,一種從 1980 年開始的基于傳統資料庫技術來做的 BI 分析場景。在這種架構下,通常計算和存儲是高度一體的。整體系統能支撐的計算能力,依賴于服務提供商的硬體配置,整體成本高,存在實體上限,擴充起來比較麻煩。

第二階段,随着技術的演進, 2010 年開始出現了以 Hadoop 技術體系為主流的傳統資料湖。在以 Hadoop 技術為主的資料平台架構下,通常可以支援服務在普通硬體上面去部署,整體的計算和存儲的擴充性都得到了解決。基于開源技術生态,多個大型公司也參與到資料湖技術發展中來,整體生态繁榮度也在逐漸提升。

但在這一階段凸顯出了一個問題,随着生态技術的發展,越來越多的開源元件開始累積。對于一個企業來說,為了解決不同領域的問題,需要運維多個開源的元件,來滿足不同領域的資料需求,就導緻整個企業的技術運維成本逐漸提升。

基于這個問題,随着技術的進一步發展,在 2020 年,湖倉一體的架構開始被提出。

相比起傳統資料湖,湖倉一體架構支援原生的 ACID 能力,支援像 BI 分析、報表分析,機器學習和流式分析多種類型的計算範式,以及雲上的對象存儲和彈性計算能力。以上能力,讓湖倉一體架構能夠有效地去解決企業的對資料規模,以及對計算能力的彈性伸縮需求。同時,湖倉一體可以在很大程度上規避傳統 Lambda 架構存在的多個計算元件,或者多種架構範式導緻的架構負擔,讓企業能夠更專注地去解決他們的業務價值。

湖倉一體架構在火山引擎 LAS 的探索與實踐

LAS 就是基于湖倉一體的架構進行設計的。從上圖來看,LAS 架構整體上分為三個部分。最上層是開發工具層,開發工具層會通過計算層提供的統一 SQL 通路服務去通路計算層,根據使用者的 SQL 類型自動做 SQL 解析。所有引擎計算能力統一由彈性容器服務來提供,可以支援彈性伸縮,按需使用。

再往下就是湖倉一體的存儲層。首先,湖倉一體存儲會通過統一的中繼資料服務,向計算層提供統一的中繼資料視圖,屏蔽底層的具體中繼資料實作細節,可以使多個引擎無縫對接到統一的中繼資料服務。

接下來是湖倉存儲引擎,它主要提供了事務管理能力,也就是 ACID 的能力,以及對資料批流一體的讀寫能力。

再往下就是 LAS 基于火山引擎對象存儲服務 TOS 和 CloudFS ,來提供 EB 級的資料存儲能力和資料通路的緩存加速能力。

以上就是 LAS 整體的技術架構。

LAS資料湖核心剖析

這一版塊将向大家呈現 LAS 資料湖核心的特性及優化。

湖倉一體架構在火山引擎 LAS 的探索與實踐

LAS 的資料湖核心—— ByteLake 它是什麼?

首先,ByteLake 是基于開源 Apache Hudi 進行内部增強的湖倉一體存儲引擎,提供湖倉一體的存儲能力。

它的第一個主要能力是提供了湖倉統一的中繼資料服務,完全相容開源的 Hive Metastore,可以無縫對接多種計算引擎。第二個主要能力是可以支援對海量資料的 Insert,完全相容 Hive SQL,可以平遷傳統數倉場景下的 Hive 任務。第三,ByteLake 支援對大規模曆史資料的 Update 和 Delete,以及對新增資料的 Upsert 和 Append 能力。最後,ByteLake 支援流批一體的讀寫能力,提供流式讀寫的 source 和 sink,支援近實時分析。

ByteLake 又是怎麼做到這些能力的呢?接下來從以下幾個特性來展開闡述。

湖倉一體架構在火山引擎 LAS 的探索與實踐

如何實作高效資料更新?

第一個場景是流式寫入更新場景。在這種場景下,最明顯的特點就是小批量資料頻繁寫入更新。但主要的問題是如何去定位要寫入的記錄呢?是做 update 操作還是 insert 操作?

在這樣的背景下,ByteLake 提供了一種 Bucket Index 的索引實作方案。

這是基于哈希的一種索引實作方案。它可以快速地去定位一條記錄所對應的 Fail Group,進而快速定位目前記錄是否已經存在,來判斷這一條記錄是做 Update 還是做 Insert 操作,進而可以快速地将這種小規模的資料去添加到 Append Log。在讀取時,通過 Compaction 就可以将 LogFile 和 BaseFile 裡邊的資料進行 Merge 去重,進而達到資料更新的效果。

針對日志資料入湖,通常來說是不需要主鍵的,這種基于 Hash 索引的實作方式,是需要有 Shuffle 操作的。因為在基于 Hash 的索引實作中,當一批資料過來之後,會根據這一批資料去找分别對應的 File Group,再基于 File Group 去聚合要更新的這些資料,通過同一個 Task,去更新同一個 File Group 來實作原子寫入。

在資料 Shuffle 的過程,其實對于資料湖日志寫入是有額外的開銷的,但 ByteLake 提供了一種 Non index 的實作方案,去掉了索引的限制,可以減少資料 Shuffle 的過程,進而達到快速入湖的能力。

湖倉一體架構在火山引擎 LAS 的探索與實踐

存量資料如何高效更新?

存量資料,一大特點就是資料量大,單表的規模可能有幾百 TB ,甚至到 PB 的級别。針對于這種大規模的曆史資料的更新場景,如何去提升更新性能?其實最主要的就是要如何去降低資料更新的規模。

基于此,ByteLake 提出了一種實作方案——Column Family,将單表多列的場景分别存儲到不同列簇。不同的檔案可以基于 Row Number 進行聚合,合并後就是一個完整的行。如果要更新曆史資料,隻需要去找到要更新的那些列對應的 Column Family 對應的檔案,把這些檔案做一些局部更新,就可以達到整體更新的效果。進而在很大程度上減少這些非必要資料的掃描,提升存量曆史資料更新場景的性能。

湖倉一體架構在火山引擎 LAS 的探索與實踐

如何提升并發性能?

談到并發,通常會有兩部分内容。比如有很多個任務同時去往 ByteLake 引擎裡邊寫資料,這就意味着有大批量的任務去通路 ByteLake 的 MetaStore Service。在這種場景下,ByteLake MetaStore Service 就會成為一個性能瓶頸。

為了突破這個瓶頸,除了無限的堆加資源之外,另一個比較有效的方案就是增加緩存。通過中繼資料服務端去緩存比較熱點的資料,比如 Commit Metadata 和 Table Metadata,來達到服務端的性能提升。

另外一塊,是在引擎側做優化。比如在 Flink 引擎層面将 Timeline 的讀取優化到 JobManager 端。同一個任務下,隻要 JobManager 去通路 Hive ByteLake MetaStore Service,緩存到 JobManager 的本地之後,所有的 TaskManager 隻要去通路 JobManager 本身緩存的 Timeline 資訊就可以了。

湖倉一體架構在火山引擎 LAS 的探索與實踐

從單個任務的視角來看,比如多個任務要同時去更新同一張表,這種情況下要保證資料的正确性,同時又能保證并發性能,應該如何來做?ByteLake 提供的解決方案——基于樂觀鎖的一個并發控制。

針對多任務寫同一個表的場景,ByteLake 可以支援多種并發政策的設定。業務可以根據對資料一緻性的要求,以及對資料并發性能的要求,選擇靈活的并發政策,來達到它的資料并發寫入的性能名額。

LAS資料湖服務化設計

這個版塊将向大家呈現 ByteLake 服務化過程中的一些設計實踐。

湖倉一體架構在火山引擎 LAS 的探索與實踐

CatalogService :統一的中繼資料視圖

CatalogService 主要提供了與 HMS 的相容接口,同時為所有的查詢引擎提供了統一的中繼資料視圖,解決了異構資料源的中繼資料管理問題。

CatalogService 整體分三層,第一層是 Catalog Federation,提供統一的視圖和跨地域的資料通路能力。以及提供了對源資料請求的路由能力,可以根據中繼資料請求的類型,支援通過 Mapping 的方式,來路由不同的服務請求對應的底層中繼資料服務執行個體。

第二層是 CatalogService 下層的具體中繼資料服務的實作,比如 Hive MetaStore Service 以及 ByteLake MetaStore Service 等。可能還有不同的中繼資料服務對接到 CatalogService,來統一向上層引擎提供這種中繼資料服務。

最後一層是 MetaStore 的存儲層,它通過插件式的方式來提供不同的存儲引擎,來滿足上層不同中繼資料服務執行個體的存儲要求。

BMS 詳解

湖倉一體架構在火山引擎 LAS 的探索與實踐

湖倉一體中繼資料管理服務

Bytelake MetaStore Service,簡稱 BMS,它是一個湖倉一體的中繼資料管理服務,整體的架構分為以下幾個部分。首先第一個就是 Catalog,Catalog 是對單表的中繼資料通路的抽象。主要邏輯是通過 MetaStore Client 來通路 Meta Server,同時它會去緩存單表的 Schema 資訊以及屬性等資訊。

另外一部分就是 Meta Server,也就是 BMS 裡邊最核心的部分。它主要是包含兩大部分服務層,第一是 Bytelake MetaStore 中繼資料服務模型,比如 Table Service,Timeline Service,Partition Service 和 Snapshot Service。存儲層提供了 MetaStore 所有中繼資料的存儲能力。最後一部分就是 Eventbus, Eventbus 主要目的是為了将中繼資料的 CUD 事件發送給監聽者,來達到中繼資料資訊的分發和同步。

湖倉一體架構在火山引擎 LAS 的探索與實踐

中繼資料寫入流程

關于中繼資料寫入流程,簡單來講,當有一個 Client 去送出了 Instant 之後,Bytelake Catalog 會去通路 Bytelake Meta Store 的接口,會将 Instance 改成 Completed,然後将請求發到 Bytelake 的 MetaStore,之後 Bytelake MetaStore Server 會做一個原子送出。

在此之後,Timeline Service 會把送出的狀态更新到資料庫裡邊。接下來這些分區資訊将再被送出給 Partition Service,同步到對應的分區存儲表裡去。最後一步,把這些所有的變更作為一個快照,同步到 Snapshot Service 裡,它會把檔案層面的變更存儲到資料庫裡,做持久化存儲。

湖倉一體架構在火山引擎 LAS 的探索與實踐

中繼資料讀取流程

對于源資料的讀取流程,舉個例子,有一個計算引擎它讀取了一個 SQL,通過 SQL 解析拿到一張表,這張表會通過Bytelake Catalog Service去請求Bytelake MetaStore,最終會路由到 Table Service 拿到這些表的資訊。

拿到表的資訊做 SQL Plan 優化的時候,會做一些分區的下推或裁剪。這個時候會去請求到 Bytelake 的 Partition Service 做過濾,接着會根據分區資訊去掃描檔案,在此過程中會去請求 Timeline Service 擷取對應的 Timeline 資訊。接下來,基于 Timeline 的資訊時間去 Snapshot Service 拿到對應檔案,再通過 SQL 執行器來實作資料檔案的讀取。

湖倉一體架構在火山引擎 LAS 的探索與實踐

中繼資料變更通知

中繼資料變更通知具體的實作流程主要依托于兩個部分。

一是 Eventbus,二是 listener。所有的中繼資料請求都會發送到 Eventbus,由 Eventbus 分發事件到所有已經注冊的 Listener 上面。listener 再根據下遊系統的需求,去訂閱 Eventbus 裡邊的對應事件類型進行響應,進而達到讓上下遊的元件感覺到中繼資料的變化,實作中繼資料的同步。

TMS詳解:

湖倉一體架構在火山引擎 LAS 的探索與實踐

統一表管理服務

LAS 的另外一個服務——TMS,全稱是 Table Management Service。它主要解決的問題是異步任務的托管優化。為什麼會做異步任務的托管優化?因為正常來講,Flinker SQL 任務寫 ByteLake 表的過程,其實就是把批量的資料寫入下遊表裡邊去。随着時間的推移,一個是 Commit 的日志非常多,另外一個是小檔案非常多。通常的 Flink 引擎層面的實作方案,是在資料寫了一定的次數後,追加一個 Compaction 操作,把之前寫入的檔案做一個壓縮。

但針對流式任務去做 Compaction,對正常的流式任務穩定性有很大影響,因為壓縮本身是一個開銷比較大的動作,對流式計算資源的消耗是很難去評估的,會導緻整個流式寫入任務的波動,進而影響流式寫入任務的穩定性。

基于此,LAS 提供了一個統一的表管理服務,異步托管這些本身内置到引擎内部的任務,統一由 Table Management Service 來托管。它整體的架構是一個主從架構,主要包含的元件一個是 Event Receiver,用來接收 Metastore 下發的一個 Event。PlanGenerator 就是根據 Meta store Server 下發的 Event 資訊,來觸發 Action Plan 的生成。

什麼是 Action Plan?簡單講,就是這一次要做哪些事情,比如你要做一個壓縮任務,還是做一次曆史檔案的清理,還是做一些小檔案的合并,都稱為 Action Plan。Job Scheduler 就是去排程需要被執行的 Acting Plan。

什麼是 Job Manager?它主要用于和叢集互動,比如 Yarn 或 K8S,管理 Action Plan 對應的執行任務,做一些任務運維層面的工作。

湖倉一體架構在火山引擎 LAS 的探索與實踐

執行計劃生成

就執行計劃生成展開來講,Plan Generator 會接收 Metastore 下發的一些事件,根據使用者在表的 DDL 裡的配置政策,來決定是否要生成執行計劃。

這個政策通常會有幾種,比如,一種基于它 Delta Commit 的數量,連續送出了多次達到了一定的門檻值,就會觸發一個 Action Plan 的生成,來做一次資料的壓縮。另外一種,是根據 Log File 的大小,來判斷 Compaction 操作是否需要執行。PlanGenerator 政策會根據目前 Log File 的 Meta 資訊,來決定是否要觸發 Action Plan 的生成。

湖倉一體架構在火山引擎 LAS 的探索與實踐

執行計劃排程管理

執行計劃生成結束之後,最後一步就是怎麼去排程管理執行計劃。執行計劃排程的核心流程主要由 Job Scheduler 來做,Job Scheduler 會定時地去輪詢已經生成的 Action Plan,再分發給 Job Manager。Job Manager 拿到了 Action Plan 之後,會到叢集上送出一個任務,同時不斷去輪詢任務的狀态,更新任務的狀态到資料庫,保證 Action Plan 執行的可靠性和穩定性。通常 JobScheduler 一般會有先進先出的排程政策,來保證 Action Plan 達到預期排程效果。

LAS在位元組跳動的業務實踐️

湖倉一體架構在火山引擎 LAS 的探索與實踐

抖音電商在湖倉一體架構下的業務實踐

抖音電商的業務場景,主要是營銷大促、流量診斷以及物流狀态的監控。他們的業務痛點是什麼?資料量大,計算邏輯複雜,同質資料源也比較多,寬表的建構成本比較高,包括一些其他的技術問題。還有一個痛點就是計算周期長,增量計算成本比較高。

基于 LAS 湖倉一體架構下,可以解決哪些問題呢?

首先,通過 LAS 快資料入湖能力,可以解決多資料源的快速入湖。把外部的業務系統和業務日志,通過 LAS 這種實時入湖能力快速導入到 ODS 層。通過離線數倉可以直接引用 ODS 層的準實時入庫資料,來達到離線數倉的日增量資料,同步提升資料的時效性。

其次,實時數倉中 DW 層的一些明細資料,也可以通過流式入湖的能力,直接導入到 ByteLake,達到資料複用的目的。當把這些資料導到了 ByteLake 之後,針對大寬表場景,就可以基于 ByteLake 的多流拼接能力,直接在底層的存儲引擎層,實作寬表的建構。進而解決在正常場景下,通過 Flink SQL 做多源或多流 join,導緻的任務狀态比較大,或者任務處理複雜度比較高的這種穩定性問題,進而更好地去保障業務資料的及時性和穩定性。

湖倉一體架構在火山引擎 LAS 的探索與實踐

消費行業傳統數倉架構更新

消費行業的客戶場景,實際就是在零售場景下的财務管理、庫存管理相關的一些計算場景。客戶的實作方案基于傳統的資料庫,業務和離線分析的請求都是統一在一個傳統資料庫上邊來做的。

在這種場景下,其實整個 RDBMS 要同時承接業務處理邏輯和離線 ETL 分析邏輯。随着業務資料量的增長,很快就會發現傳統資料庫的計算能力和存儲支撐能力達到了上限,導緻計算能力不足,擴充性比較差,無法在滿足後續的業務資料規模的上量。

LAS 針對這種場景的解決方案,是将客戶的離線 ETL 的分析場景,通過實時內建的方式直接導入到 LAS 裡邊,通過 LAS 的彈性計算能力,為使用者的 ETL 分析場景提供有效的算力保障。在滿足客戶低成本限制的情況下,達到客戶預期的計算效果,和對資料産出的及時性的要求。同時會通過雲上的 ByteHouse 服務來解決客戶自建的 CK 的運維成本以及性能調優的問題。優化了原有的基于 RDBMS 的資料鍊路,保證業務資料量快速增長的同時,滿足它的底層的算力要求。

湖倉一體架構在火山引擎 LAS 的探索與實踐

湖倉一體架構下的批流融合計算

典型場景就是資料實時入湖,客戶的資料源會通過 Flink SQL 持續地去寫入到 LAS 的 Bytelake 表裡。但下遊如果是一個離線任務,其實使用者沒辦法很便利地去判斷資料寫到了哪個位置,或者分區資料現在是不是已經完備的。

如果僅依賴系統時間來實作,比如在上遊的這種 Flink SQL 任務,在寫入過程正常時倒沒有特别大的問題。但是一旦上遊 Flink SQL 任務出現一些資料積壓或者任務異常的場景,下遊依賴系統時間去排程,就會存在某些分區會出現資料空洞或資料偏移的問題。例如本來資料應該落在 7 點的分區,因為上遊的這些 SQL 任務的消費延遲,導緻 7 點的資料并沒有準時地落下來, 導緻下遊去消費 7 點的資料的時候,拿到的是一個不完整的資料,導緻出現資料空洞或資料偏移的問題。

針對這種場景,LAS 提供了一種叫歸檔的能力,也就是在 Flink SQL 寫入的過程中,會基于業務事件時間實時寫入對應的資料分區。通過 ByteLake 提供歸檔能力,分區資料就緒後,可自動生成一個歸檔标簽。下遊的 spark SQL 任務可以根據分區是否有歸檔标簽,來判斷對應分區的資料是否就緒,來決定目前離線任務是不是要排程起來。

這項能力的實作邏輯,其實就是 Flink SQL 每次去送出一個 Commit 的時候,會去判斷目前送出的業務的事件時間,是否比目前的未送出分區的時間超過了某一個門檻值。比如目前分區的時間是 7 點,Flink SQL 在持續送出微批資料的時候,它判斷出來目前的最小的業務時間已經到 7 點半了,而業務定義的可容忍的延遲間隔是 15 分鐘, ByteLake 認為這個資料其實已經寫完了,就會把 7 點的分區資料打上一個歸檔标簽,來标示資料已經完成了。下遊就可以去正常地去消費 7 點的分區資料,進而保證資料的完整性。

在提供了這種歸檔能力的情況下,LAS 的整體計算鍊路就可以實作批流融合。比如 ODS 的 ByteLake 表是一個準實時的表,下層的 Spark SQL 任務可以直接通過 Spark ETL 去做處理,産出一個離線表。可能後邊還會有一些 SQL 場景依賴離線表做資料的準實時消費。在這種情況下,Flink SQL 會再生成一張 ByteLake 表,這張表同樣可以被下遊的 Spark SQL 的離線任務依賴,進而達到在整個 Pipeline 裡,做到批流計算互相融合的狀态。

繼續閱讀