B站基於Iceberg的湖倉一體架構實踐

陶然陶然發表於2023-01-09

   背景

  在B站,每天都有PB級的資料注入到大資料平臺,經過離線或實時的ETL建模後,提供給下游的分析、推薦及預測等場景使用。面對如此大規模的資料,如何高效低成本地滿足下游資料的分析需求,一直是我們重點的工作方向。

  我們之前的資料處理流程基本上是這樣的:採集端將客戶端埋點、服務端埋點、日誌、業務資料庫等資料收集到HDFS、Kafka等儲存系統中,然後透過Hive、Spark、Flink等離線和實時引擎對資料進行ETL處理及數倉建模,資料儲存使用ORC列式儲存格式,使用者可以透過Presto、Spark等引擎對數倉建模後的資料進行資料探索以及構建BI報表。對於大部分的資料服務和部分BI報表,Presto、Spark訪問ORC格式資料可能無法滿足使用者對於查詢響應時間的要求,這時需要將資料寫入ClickHouse等這種專門的OLAP引擎或者進一步處理資料後寫入HBase、Redis等KV儲存系統中等方式解決。  

  當前的資料處理流程雖然在一定程度上可以滿足目前的業務需求,但是整個流程的效率和成本都還有很大的提升空間,主要體現在:

  為了提升查詢效率,從Hive表出倉到ClickHouse、HBase、Redis、ElasticSearch、Mysql等外部系統中,需要額外的資料開發工作,額外的儲存冗餘,但同時擁有了更少的資料靈活性,複雜的元件支援增加了資料服務開發的成本,更長的資料處理流程也降低了穩定性和可靠性。

  對於未出倉的資料,使用者無論是進行資料探索還是使用BI報表,都還受SQL on Hadoop本身效能所限,和使用者期望的互動式響應有很大差距。

  本文主要介紹為了應對以上挑戰,我們在湖倉一體方向上的一些探索和實踐。

   為什麼需要湖倉一體

  在討論這個問題前,我們可能首先要明確兩個概念:什麼是資料湖?什麼是資料倉儲?這兩個概念在業界都有大量的討論,每個人的說法也不盡相同,我們嘗試總結如下,對於資料湖:

  使用統一的分散式儲存系統,可假設為無限容量。

  有統一的後設資料管理系統。

  使用開放的資料儲存格式。

  使用開放的資料處理引擎對資料進行加工和分析。

  我們之前的大資料架構基本上是一個典型的資料湖架構,使用HDFS作為統一的儲存系統,Hive metastore提供統一的Schema後設資料管理,資料以CSV、JSON、ORC等開放儲存格式儲存在HDFS上,使用者可以使用SQL、DataSet、FileSystem等各個層次的API使用Hive、Spark、Presto、Python等框架或語言訪問資料。

  資料湖架構的好處是有非常大的靈活性,結構化、半結構化、非結構化資料都可以放在資料湖中,使用者可以使用任意合適的引擎對所有的資料進行靈活的資料探索,幾乎沒有任何限制,但是它也存在很大的缺陷,最主要的就是資料管理和查詢效率的問題。

  對於資料倉儲:

  自定義的資料儲存格式。

  自己管理資料的組織方式。

  強Schema資料,對外提供標準的SQL介面。

  具有高效的計算儲存一體設計和豐富的查詢加速特性。

  資料倉儲(OLAP引擎)對於資料的要求相對更加嚴格,以ClickHouse為例,必須是預先定義的強Schema資料透過JDBC寫入ClickHouse中,ClickHouse使用自己的儲存格式儲存資料,並且會對資料檔案進行排序或者檔案合併之類的資料組織最佳化,對外提供SQL介面,不會暴露內部的資料檔案,提供索引等高階的查詢加速特性,內部的計算引擎和儲存格式也會有很多的一體協同最佳化,一般認為專門的資料倉儲查詢效率會優於資料湖架構,在B站的實踐上,大部分場景,像ClickHouse對比Spark、Presto也確實有量級上的效能提升。  

  在我們實際的資料處理場景中,除了AI和資料探索等場景,探索未知資料的未知問題,比較依賴資料湖架構的靈活性,其實大部分的場景是基於已知資料的,即我們的資料開發同學,實際上是基於Hive表的強Schema資料,進行從ODS,DWD,DWB到ADS等各個業務數倉的分層建設,本質上我們是主要是基於資料湖的架構進行業務數倉的建設,如何提升這部分場景的查詢效率,使用成本和使用者體驗是我們在這方面工作的核心內容。

  湖倉一體是近兩年大資料一個非常熱門的方向,如何在同一套技術架構上同時保持湖的靈活性和倉的高效性是其中的關鍵。常見的是兩條技術路線:一條是從分散式數倉向湖倉一體演進,在分散式數倉中支援CSV、JSON、ORC、PARQUET等開放儲存格式,將資料的處理流程從ETL轉換為ELT,資料注入到分散式數倉後,在分散式數倉中進行業務數倉的建模工作,比如AWS RedShift及SnowFlake等;另外一條是從資料湖向湖倉一體演進,基於開放的查詢引擎和新引入的開放表儲存格式達到分散式數倉的處理效率,這方面閉源商業產品的代表是DataBricks SQL,他們基於相容Spark API的閉源Photon核心和DeltaLake儲存格式以及S3物件儲存的湖倉一體架構,宣稱在TPC-DS Benchmark上效能超過專門的雲資料倉儲SnowFlake。在開源社群領域,Iceberg、Hudi、DeltaLake等專案的出現也為在SQL on Hadoop的資料湖技術方案上實現湖倉一體提供了基礎的技術儲備。在B站,基於我們之前的技術棧和實際的業務場景,我們選擇了第二個方向,從資料湖架構向湖倉一體演進。

   B站的湖倉一體架構

  對於B站的湖倉一體架構,我們想要解決的問題主要有兩個:一是鑑於從Hive表出倉到外部系統(ClickHouse、HBase、ES等)帶來的複雜性和儲存開發等額外代價,儘量減少這種場景出倉的必要性。二是對於基於SQL on Hadoop的分析查詢場景,提升查詢效率,降低成本。我們基於Iceberg構建了我們的湖倉一體架構,在具體介紹B站的湖倉一體架構之前,我覺得有必要先討論清楚兩個問題,為什麼Iceberg可以構建湖倉一體架構,以及我們為什麼選擇Iceberg?

  1. 為什麼基於Iceberg可以構建湖倉一體架構?

  對比開放的SQL引擎、儲存格式如:Presto、Spark、ORC、Parquet和分散式數倉如:ClickHouse、SnowFlake對應層的實現,其實差別不大,開源分散式引擎一直在逐漸補足SQL Runtime和儲存層的一些影響效能的高階特性,比如Runtime CodeGen,向量化執行引擎,基於statistic的CBO,索引等等,當前兩者最大的一個不同在於對於資料組織的管理能力。對於資料湖架構來說,資料檔案在HDFS的分佈組織是由寫入任務決定的,而對於分散式數倉來說,資料一般是透過JDBC寫入,資料的儲存組織方式是由數倉本身決定的,所以數倉可以按照對於查詢更加友好的方式組織資料的儲存,比如對資料檔案定期compact到合適的大小或者對資料進行合理排序和分組,對於大規模的資料來說,資料的最佳化組織可以大大提高查詢的效率。Iceberg、Hudi、DeltaLake等新的表儲存格式的出現,最主要的特性就是可以在HDFS上自組織管理表的metadata資訊,從而提供了表資料的Snapshot及粗粒度的事務支援能力,基於此,我們可以在開放的查詢引擎之外,非同步地,透明地對Iceberg、Hudi、DeltaLake格式的資料進行重新的資料組織最佳化,從而達到了分散式數倉類似的效果。

  2. 為什麼選擇Iceberg?

  Iceberg、Hudi以及DeltaLake是基本同時期出現的開源表儲存格式專案,整體的功能和定位也是基本相同,網上已經有很多相關對比介紹的文章,這裡就不詳細比較了,我們選擇Iceberg的主要原因是:Iceberg在三個裡面是表儲存格式抽象的最好的,包括讀寫引擎、Table Schema、檔案儲存格式都是pluggable的,我們可以進行比較靈活的擴充套件,並保證和開源以及之前版本的相容性,基於此我們也比較看好該專案的長遠發展。

  下圖是我們整體的湖倉一體架構,支援開放的Spark、Flink等引擎從Kafka、HDFS接入資料,然後Magnus服務會非同步地拉起Spark任務對Iceberg資料進行重新的儲存組織最佳化,我們主要是用Trino作為查詢引擎,並引入Alluxio做Iceberg的後設資料和索引資料的快取加速。  

Magnus:Iceberg智慧管理服務

  Magnus是我們湖倉一體架構的核心元件,它負責管理最佳化所有的Iceberg表中的資料。Iceberg本身是一個表儲存格式,雖然其專案本身提供了基於Spark、Flink等用於合併小檔案,合併metadata檔案或者清理過期Snapshot資料等Action Job,但是要依賴外部服務排程這些Action Job,而Magnus正是承擔這個角色。我們對Iceberg進行了擴充套件,當Iceberg表發生更新的時候,會傳送一個event資訊到Magnus服務中,Magnus服務維護一個佇列用於儲存這些commit event資訊,同時Magnus內部的Scheduler排程器會持續消費event佇列,並根據對應Iceberg表的後設資料資訊及相關的策略決定是否及如何拉起Spark任務最佳化Iceberg表的資料組織。  

Iceberg核心增強

  對於豐富的多維分析場景,我們也有針對性的在Iceberg核心和其他方面進行了定製化增強,這裡簡要介紹兩個方面:Z-Order排序和索引。

  Z-Order排序

  Iceberg在表的metadata中記錄了檔案級別每個列的MinMax資訊,並且支援小檔案合併以及全域性Linear排序(即Order By),這兩者配合起來,我們可以在很多查詢場景實現非常好的DataSkiping效果,比如我們對於某個Iceberg表的資料檔案按照欄位a進行全域性排序後,如果後續查詢帶有a的過濾條件,查詢引擎會透過PredictePushDown把過濾條件下推到檔案訪問層,我們就可以根據MinMax索引把所有不需要的檔案直接跳過,只訪問資料所在的檔案即可。

  在多維分析的實際場景中,一般都會有多個常用的過濾欄位,Linear Order只對靠前欄位有較好的Data Skip效果,通常會採用將低基數字段作為靠前的排序欄位,從而才能保證對於後面的排序欄位在過濾時也有一定的Data Skipping效果,但這無法從根本上解決問題,需要引入一種新的排序機制,使得多個常用的過濾欄位均能夠獲得比較好的Data Skipping效果。

  Interleaved Order(即Z-Order)是在影像處理以及數倉中使用的一種排序方式,Z-ORDER曲線可以以一條無限長的一維曲線,穿過任意維度的所有空間,對於一條資料的多個排序欄位,可以看作是資料的多個維度,多維資料本身是沒有天然的順序的,但是Z-Order透過一定規則將多維資料對映到一維資料上,構建z-value,從而可以基於一維資料進行排序,此外Z-Order的對映規則保證了按照一維資料排序後的資料同時根據多個排序欄位聚集。

  參考wikipedia中的Z-Order介紹,可以透過對兩個資料位元位的交錯填充來構建z-value,如下圖所示,對於(x, y)兩維資料,資料值 0 ≤ x ≤ 7, 0 ≤ y ≤ 7,構建的z-values以及z-order順序如下:  

  可以看到,如果根據z-values的順序對資料進行排序,並平均分為4個檔案,無論我們在查詢中使用x還是y欄位過濾進行點查詢,都可以skip一半的不相干檔案,如果資料量更大,效果會更好,也就是說,基於Z-Order分割槽儲存的檔案,可以在多個欄位上擁有比較好的Data Skipping效果。我們對Spark進行了增強,支援Z-Order Range Partitioner用於對Iceberg資料進行檔案間的排序組織,擴充套件了Iceberg表的元資訊,使用者可以自定義期望的Iceberg表的Distribution資訊,支援按照Hash、Range、Z-Order等方式進行檔案間資料排序,以及對應的OptimizeAction用於拉起Spark任務,按照使用者定義的Distribution資訊對Iceberg表進行重組織。具體詳情可查詢參考文獻[1](透過資料組織加速大規模資料分析)。

  索引

  Iceberg預設儲存檔案級別每列的Min、Max資訊,並用於TableScan階段的檔案過濾,基本等價於分散式數倉中的MinMax索引,MinMax索引對於排序後的欄位DataSkipping效果很好,但是對於非排序欄位,資料隨機散佈於各個檔案,使用該欄位過濾時,MinMax索引基本很難有檔案Skip的效果,BloomFilter索引在這種場景下可以更好地發揮作用,尤其是當欄位基數較大的時候。布隆過濾器實際上是一個很長的二進位制向量和多個Hash函式,資料透過多個函式對映到二進位制向量的位元位上,布隆過濾器的空間效率和查詢時間都非常高效,非常適合用於檢索一個元素是否存在於一個集合中。

  布隆過濾器的空間效率和查詢時間都非常高效,但是在使用上也有侷限之處,主要是它能夠支援的過濾條件是有限的,只適用於:=、IN、NotNull等等值表示式,對於常見的Range過濾,比如>、>=、<、<=等是不支援的。為了支援更豐富的過濾表示式,我們引入了BitMap索引。BitMap也是一個非常常見的資料結構,將一組正整形資料對映到位元位,相比於BloomFilter,不存在Hash衝突的情況,所以不會出現False-Positive,但是一般需要更多的儲存空間。對於高基數字段的BitMap索引,落地實現主要的問題在於:

  需要儲存欄位基數對應個BitMap,儲存代價太大。

  在Range過濾時,使用BitMap判斷是否可以Skip檔案時,需要訪問大量BitMap,讀取代價太大。

  為了解決以上問題,我們引入了Bit-sliced Encoded Bitmap實現。具體詳情可查詢參考文獻[2](透過索引加速湖倉一體分析)。

  在B站的落地

  基於Iceberg的湖倉一體方案在B站的資料分析場景正逐漸落地,我們目前已經支撐PB級的資料量,每天響應幾萬個查詢,其中P90的查詢可以在1s內響應,滿足了多個運營分析資料服務互動式分析的需求。接下來,我們希望能夠將湖倉一體架構作為我們OLAP數倉建模的基礎,統一大部分的業務數倉分析層資料的儲存和查詢,簡化技術架構,提升查詢效率,節省資源成本。

   總結和展望

  相比於傳統的SQL on Hadoop技術棧,基於Iceberg的湖倉一體架構,在保證了和已有Hadoop技術棧的相容性情況下,提供了接近分散式數倉的分析效率,兼顧了湖的靈活性和倉的高效性,從我們落地實踐的經驗看,對於使用者基本透明,只是一種新的Hive表儲存格式,沒有更多使用和認知的門檻,和已有的大資料平臺工具和服務也能非常小代價地整合。為了進一步提高在不同場景的查詢效率和使用體驗,我們還在以下方向對Iceberg進行進一步的增強:

  星型模型的資料分佈組織,支援按照維度表欄位對事實表資料進行排序組織和索引。

  預計算,透過預計算對固定查詢模式進行加速。

  智慧化,自動採集使用者查詢歷史,分析查詢模式,自適應調整資料的排序組織和索引等。

  後續的進展我們會持續更新,歡迎感興趣的小夥伴來和我們一起交流溝通。

  參考文獻:

  [1] 透過資料組織加速大規模資料分析:

  [2] 透過索引加速湖倉一體分析:

來自 “ 嗶哩嗶哩技術 ”, 原文作者:OLAP平臺;原文連結:http://server.it168.com/a2023/0109/6785/000006785246.shtml,如有侵權,請聯絡管理員刪除。

相關文章