阿里云數(shù)據(jù)湖統(tǒng)一元數(shù)據(jù)與存儲(chǔ)管理實(shí)踐
一、云上數(shù)據(jù)湖架構(gòu)
首先介紹一下數(shù)據(jù)湖相關(guān)的概念和架構(gòu)。
不同的云產(chǎn)商對(duì)數(shù)據(jù)湖有著不同的定義。但是從關(guān)鍵詞上來(lái)看,基本上都是圍繞這幾個(gè)特性和目標(biāo):
(1)統(tǒng)一存儲(chǔ),即數(shù)據(jù)湖是一個(gè)統(tǒng)一的中心化的數(shù)據(jù)存儲(chǔ)。
(2)可以用來(lái)放一些原始數(shù)據(jù)。
(3)支持多種格式,包括結(jié)構(gòu)化的數(shù)據(jù)和非結(jié)構(gòu)化的數(shù)據(jù)。
首先,統(tǒng)一存儲(chǔ)主要是為了解決數(shù)據(jù)孤島的問(wèn)題。因?yàn)閭鹘y(tǒng)的數(shù)據(jù)庫(kù)或者是數(shù)據(jù)倉(cāng)庫(kù)在設(shè)計(jì)上是存算一體的,也就是在不同的查詢(xún)引擎之間,數(shù)據(jù)需要經(jīng)過(guò)清洗和同步。這樣不管是在存儲(chǔ)空間上,還是效率上,都存在一定的浪費(fèi)。而數(shù)據(jù)湖上則是使用存算分離的查詢(xún)引擎,典型的比如 Hadoop 生態(tài)的 Hive 和 Spark。再加上開(kāi)放的存儲(chǔ)格式,如 Parquet、ORC 等,來(lái)實(shí)現(xiàn)使用不同的引擎同時(shí)可以查詢(xún)同一個(gè)數(shù)據(jù)的功能。這就是早期數(shù)據(jù)湖的架構(gòu)。
在存儲(chǔ)實(shí)現(xiàn)上,數(shù)據(jù)湖通常會(huì)使用擴(kuò)展性比較高的,廉價(jià)的存儲(chǔ),比如 HDFS,或者云上的 OSS、S3 等對(duì)象存儲(chǔ)。這樣大家可以把更多的原始數(shù)據(jù),非結(jié)構(gòu)化數(shù)據(jù)直接放入,避免原始數(shù)據(jù)的丟失。
為了能夠讀取這些原始數(shù)據(jù),計(jì)算引擎通常支持類(lèi)似 schema on read 的方式,采取事后建模的高靈活性的解析方式,對(duì)數(shù)據(jù)格式?jīng)]有很強(qiáng)的約束。這種靈活性也帶來(lái)了一些弊端,比如其高度開(kāi)放性可能導(dǎo)致對(duì)安全和權(quán)限的管理相比于數(shù)倉(cāng)是有所差距的。另外,因?yàn)殚_(kāi)放存儲(chǔ),并發(fā)寫(xiě)入的場(chǎng)景尤其是流式寫(xiě)入的場(chǎng)景,事務(wù)上對(duì) ACID 的要求會(huì)更高。
是否有一種辦法使我們既能夠利用數(shù)據(jù)湖的優(yōu)勢(shì),也能讓數(shù)據(jù)湖擁有數(shù)倉(cāng)的功能特性呢?
前兩年 Databrick 提出了 Lakehouse 湖倉(cāng)一體的概念,希望讓數(shù)據(jù)湖能夠?qū)崿F(xiàn)更多數(shù)倉(cāng)的企業(yè)級(jí)能力,讓用戶(hù)可以像使用數(shù)倉(cāng)一樣地使用數(shù)據(jù)湖。
Lakehouse 概念是在數(shù)據(jù)庫(kù)的基礎(chǔ)之上,添加了幾層內(nèi)容。
首先在存儲(chǔ)上層,做了元數(shù)據(jù)的統(tǒng)一。對(duì)上層提供統(tǒng)一的元數(shù)據(jù)結(jié)構(gòu)化 SQL 的接口,讓不同的應(yīng)用,可以使用相同的元數(shù)據(jù)訪(fǎng)問(wèn)數(shù)據(jù)。
另外在性能上,支持 cache,來(lái)優(yōu)化數(shù)據(jù)湖讀取性能。
并且,利用數(shù)據(jù)湖格式實(shí)現(xiàn)事務(wù)層。目前很火熱的數(shù)據(jù)湖格式,Delta Lake、Hudi 和 Iceberg,使得我們現(xiàn)在提到數(shù)據(jù)湖場(chǎng)景,基本就跟這幾個(gè)數(shù)據(jù)湖格式劃等號(hào)了。雖然這有些夸張,但也足以證明它們?cè)跀?shù)據(jù)湖架構(gòu)中的重要地位。
最后在底層的數(shù)據(jù)湖存儲(chǔ)的實(shí)現(xiàn)上,相比于 HDFS,目前在云上也有使用對(duì)象存儲(chǔ)作為數(shù)據(jù)湖存儲(chǔ)的趨勢(shì)。因?yàn)樵粕蠈?duì)象存儲(chǔ)的擴(kuò)展性相比于自建 HDFS 要高很多。不管是在成本上,還是在可用性上其實(shí)都是會(huì)高一些。
阿里云提供了一些產(chǎn)品功能來(lái)幫助用戶(hù)使用數(shù)據(jù)湖的架構(gòu)。
首先支持多引擎計(jì)算分析,比如常見(jiàn)的 EMR 的 Spark 和 Hive、Presto、StarRocks 這些引擎,以及阿里云自研的引擎,如 MaxCompute、Hologres,都可以進(jìn)行湖上數(shù)據(jù)分析。可以根據(jù)不同場(chǎng)景來(lái)選擇合適的引擎。
另一方面這些引擎為了能夠無(wú)縫對(duì)接湖上的結(jié)構(gòu)化數(shù)據(jù),DLF(data lake formation)產(chǎn)品提供了統(tǒng)一的元數(shù)據(jù)和湖上的權(quán)限管理,作為整個(gè) lakehouse 架構(gòu)里的元數(shù)據(jù)管理層。在后面還會(huì)展開(kāi)介紹。
最后在存儲(chǔ)層上,云上的對(duì)象存儲(chǔ) OSS 是天生適合做數(shù)據(jù)湖存儲(chǔ)的,并且成本不高。同時(shí)現(xiàn)在 OSS 也支持兼容 HDFS 接口的產(chǎn)品,OSS-HDFS,是完全支持 HDFS 接口的,更適合對(duì)接一些老版本的大數(shù)據(jù)引擎。
DLF 的核心能力是提供一個(gè)全托管的統(tǒng)一元數(shù)據(jù)服務(wù),因?yàn)閿?shù)據(jù)都已經(jīng)放在數(shù)據(jù)湖上了,元數(shù)據(jù)需要一個(gè)中心化的管理才能實(shí)現(xiàn)多個(gè)引擎的無(wú)縫對(duì)接,這體現(xiàn)了元數(shù)據(jù)服務(wù)在數(shù)據(jù)湖里的重要性。這樣不同引擎讀寫(xiě)同一份數(shù)據(jù)是圍繞統(tǒng)一的 schema 做操作的,而不是每個(gè)引擎都需要單獨(dú)建外表。
同時(shí)圍繞元數(shù)據(jù),我們提供對(duì)數(shù)據(jù)的細(xì)粒度的權(quán)限管控。
另外也提供了數(shù)據(jù)湖上的一些存儲(chǔ)管理的功能。
二、數(shù)據(jù)湖統(tǒng)一元數(shù)據(jù)
下面就來(lái)具體介紹阿里云數(shù)據(jù)湖的一個(gè)重要能力,數(shù)據(jù)湖上的統(tǒng)一元數(shù)據(jù)。
在開(kāi)源大數(shù)據(jù)體系里,從早期的 map-reduce 到類(lèi)似 SQL 查詢(xún)語(yǔ)言 Hive 的誕生之后,Hive 逐漸成為了開(kāi)源數(shù)倉(cāng)的事實(shí)標(biāo)準(zhǔn),圍繞著 Hive 的元數(shù)據(jù) Hive Metastore 也成為了對(duì)接開(kāi)源數(shù)倉(cāng)的元數(shù)據(jù)標(biāo)準(zhǔn)。從此以后各個(gè)引擎,包括 Spark、Presto 等都是支持對(duì)接 Hive Metastore,圍繞 Hive Metastore 做元數(shù)據(jù)管理。
Hive Metastore 是一個(gè)常駐的無(wú)狀態(tài)的服務(wù),它可以部署一個(gè)或者多個(gè)實(shí)例。大數(shù)據(jù)引擎通過(guò) thrift 協(xié)議連接 Hive Metastore 進(jìn)行元數(shù)據(jù)的讀寫(xiě)。
Hive Metastore 的元數(shù)據(jù)本身是需要存儲(chǔ)到數(shù)據(jù)庫(kù)上,通常會(huì)用 MySQL 作為 Hive Metastore 元數(shù)據(jù)的底層存儲(chǔ)。
這就形成了常見(jiàn)的開(kāi)源大數(shù)據(jù)元數(shù)據(jù)體系。
使用 Hive Metastore 管理元數(shù)據(jù)也存在著一些問(wèn)題和挑戰(zhàn)。
首先在功能層面上它是沒(méi)有做多版本的,不能追溯之前的元數(shù)據(jù)版本。ACID 的特性和 LOCK 接口是和 Hive 引擎綁定的,在湖上多引擎的場(chǎng)景下,是沒(méi)有辦法利用到它的一些功能的。
另外因?yàn)樗┞兜氖?thrift 協(xié)議的接口,如果你自有服務(wù),或者自研引擎需要去對(duì)接會(huì)相對(duì)麻煩一些。有時(shí)可能還需要直接連 MySQL 去讀一些元數(shù)據(jù),這也不是一個(gè)比較好的方法。
還有一個(gè)問(wèn)題是它存在性能瓶頸,存在單點(diǎn)問(wèn)題和運(yùn)維成本,尤其是對(duì)元數(shù)據(jù)量比較大的用戶(hù),這是一個(gè)比較常見(jiàn)的問(wèn)題。因?yàn)閱吸c(diǎn)的 Hive Metastore Server 和 Metastore 后端連接的 MySQL 都可能會(huì)成為瓶頸,需要一些性能調(diào)優(yōu)的工作。
上圖中還列出了一些真實(shí)的客戶(hù)問(wèn)題。在 Hive Metastore 的使用過(guò)程中,首先會(huì)遇到的就是 JDBC 連接的問(wèn)題,可能會(huì)遇到一些錯(cuò)誤。比如有的時(shí)候我們查詢(xún)?cè)獢?shù)據(jù)的所有請(qǐng)求都突然變慢了,這時(shí)首先要檢查 MySQL 的狀態(tài),查看 MySQL 監(jiān)控是否有慢 SQL。如果分區(qū)數(shù)總量很大的話(huà),MySQL 表數(shù)量可能會(huì)達(dá)到上千萬(wàn),會(huì)導(dǎo)致查詢(xún)比較慢。這個(gè)時(shí)候,需要做一些數(shù)據(jù)清理,刪除一些分區(qū)來(lái)緩解這個(gè)問(wèn)題。另外在自建的數(shù)據(jù)管理系統(tǒng)或者外部系統(tǒng)中,通常不會(huì)用 thrift 協(xié)議去調(diào)用 Hive 的 Metastore Server,而是直連 JDBC,這樣連接數(shù)多的話(huà),也可能會(huì)帶來(lái)一些額外的壓力。
在內(nèi)存方面,Hive Metastore Server 的內(nèi)存存在 OOM 的風(fēng)險(xiǎn)。因?yàn)橛行┎僮?,比?list partition,會(huì)加載全部分區(qū)對(duì)象,如果有人寫(xiě)了一個(gè)糟糕的查詢(xún),比如在一個(gè)很大的分區(qū)表上,沒(méi)有加分區(qū)查詢(xún)條件,就可能會(huì)拿到上百萬(wàn)的分區(qū),最后導(dǎo)致整個(gè) Hive Metastore 內(nèi)存出現(xiàn) full gc 或者 OOM 的情況,一旦 Hive Metastore 出問(wèn)題,整個(gè)集群的作業(yè)都會(huì)受到影響。
列舉幾個(gè)我們遇到過(guò)的 StackoverflowError 的情況。如果 drop partition 的分區(qū)數(shù)量很多的話(huà),在 Hive Metastore 的內(nèi)部實(shí)現(xiàn)是遞歸的,可能會(huì)導(dǎo)致堆棧溢出報(bào)錯(cuò),無(wú)法直接執(zhí)行。
最后就是超時(shí)問(wèn)題,因?yàn)?HMS 的客戶(hù)端設(shè)計(jì)沒(méi)有分頁(yè),是全量返回的。所以在拉取元數(shù)據(jù)的時(shí)候,可能會(huì)出現(xiàn)超時(shí)的情況,這也是一個(gè)風(fēng)險(xiǎn)點(diǎn)。
這些都是我們?cè)谑褂?HMS 時(shí)候遇到的一些問(wèn)題。
因此在云上,我們提供了全托管的元數(shù)據(jù)服務(wù)的 DLF(data lake formation),采用的是完全不同的架構(gòu),來(lái)解決上面大部分問(wèn)題和痛點(diǎn)。
首先作為云產(chǎn)品,我們通過(guò)標(biāo)準(zhǔn)的 open API 暴露接口,提供了兼容 Hive2 和 Hive3 的 Metastore 接口的 client。這個(gè) client 可以直接替換掉引擎的 Hive Metastore client 實(shí)現(xiàn)類(lèi),原本訪(fǎng)問(wèn) Hive 元數(shù)據(jù)的地方可以直接替換為訪(fǎng)問(wèn)我們客戶(hù)端的實(shí)現(xiàn)類(lèi),實(shí)現(xiàn)了無(wú)縫對(duì)接。
另外除了開(kāi)源體系的引擎以外,我們也對(duì)接了阿里云上的其它大數(shù)據(jù)引擎,包括 Max Compute、Hologres、Flink 等等。云上其他大數(shù)據(jù)引擎也可以利用我們的統(tǒng)一元數(shù)據(jù)來(lái)進(jìn)行元數(shù)據(jù)管理。這樣真正做到了統(tǒng)一 catalog,用一個(gè)引擎寫(xiě)入,其它引擎讀取。比如用 Flink 入湖,之后可以直接使用 Spark 查,再用 Hologres 等做 OLAP 分析,這些都可以直接采用同一個(gè)元數(shù)據(jù)來(lái)完成。
不同于 HMS 使用 MySQL,擴(kuò)展性比較差,我們的元數(shù)據(jù)服務(wù)底層實(shí)現(xiàn)是用阿里云的表格存儲(chǔ)。表格存儲(chǔ)也是阿里云提供的一種服務(wù),面向海量數(shù)據(jù)有非常強(qiáng)的伸縮能力,擴(kuò)展性很高,所以不用擔(dān)心分區(qū)數(shù)過(guò)大帶來(lái)的擴(kuò)展性問(wèn)題。
因?yàn)槲覀兪且粋€(gè)全托管的服務(wù),對(duì)使用方提供 SLA,高可用保障,前面提到的運(yùn)維問(wèn)題也可以避免。
總結(jié)一下,我們的統(tǒng)一元數(shù)據(jù)的優(yōu)勢(shì)為,一方面因?yàn)槭侨泄埽梢詼p少元數(shù)據(jù)運(yùn)維成本;另一方面真正實(shí)現(xiàn)了對(duì)接云上多引擎。
再補(bǔ)充一些關(guān)于元數(shù)據(jù)本身實(shí)現(xiàn)的細(xì)節(jié)。
首先元數(shù)據(jù)的客戶(hù)端是兼容 Hive Metastore 行為的,實(shí)現(xiàn)了 Hive Metastore 的接口,可以直接去對(duì)接 Hive 生態(tài)相關(guān)的大數(shù)據(jù)引擎。Hive Metastore 內(nèi)部的有些行為,比如在創(chuàng)建 partition 的時(shí)候統(tǒng)計(jì) table size 等動(dòng)作,都會(huì)保留在客戶(hù)端里,所以不用擔(dān)心兼容性問(wèn)題。
另外客戶(hù)端會(huì)做一些性能優(yōu)化,包括異常重試、并發(fā)讀取、分頁(yè)查詢(xún)等。對(duì)于重復(fù)提交的請(qǐng)求,客戶(hù)端也會(huì)做一些合并壓縮,減少 IO 開(kāi)銷(xiāo)。
在服務(wù)內(nèi)部,除了剛才提到的存儲(chǔ)層的高擴(kuò)展性以外,我們也通過(guò)一些自動(dòng)的分區(qū)索引,再做一些分區(qū)過(guò)濾的性能提升。
總體來(lái)講在元數(shù)據(jù)的性能上,在一些小表上可能跟 RDS 有些差距,但是并不明顯。在大分區(qū)表上,比如單表有 300 萬(wàn)分區(qū)的場(chǎng)景下,我們的查詢(xún)性能會(huì)有比較明顯的優(yōu)勢(shì)。比如在 300 萬(wàn)分區(qū)表下,如果分區(qū)條件全部命中,list partition by filter 在我們的元數(shù)據(jù)可以在 0.5 秒內(nèi)返回,但是在 RDS 上因?yàn)樗姆謪^(qū)值沒(méi)有索引,需要花 5 秒左右才能返回。
在元數(shù)據(jù)的功能上再舉幾個(gè)例子。
首先是元數(shù)據(jù)多版本,我們會(huì)記住元數(shù)據(jù)每一次更新的前后狀態(tài),可以看到什么時(shí)間點(diǎn)加了什么字段,是誰(shuí)做的修改的。有比較好的回溯機(jī)制,實(shí)現(xiàn)元數(shù)據(jù)審計(jì)。在元數(shù)據(jù)檢索上,我們的元數(shù)據(jù)本身會(huì)把內(nèi)容同步到 ES 搜索引擎里,對(duì)外暴露,可以通過(guò)字段搜表,也可以做全局搜索。
再來(lái)看一下權(quán)限相關(guān)的問(wèn)題。
在開(kāi)源大數(shù)據(jù)場(chǎng)景下做用戶(hù)級(jí)別的權(quán)限控制,通常會(huì)有這么幾種方案:
Hive 本身提供的認(rèn)證能力,storage-based authorization和sql-standard-based authorization。但是 Hive 的實(shí)現(xiàn)都是跟 Hive 引擎綁定的。通常在其它引擎是無(wú)法使用到它的功能的,基本上也沒(méi)有人真正會(huì)在其它引擎上去使用。
大家通常做法是用 Ranger 來(lái)做權(quán)限管理。Ranger 是一個(gè)通用的多引擎方案,它可以對(duì) SQL 進(jìn)行權(quán)限管理,也可以對(duì)文件系統(tǒng)做權(quán)限管理。它的原理是從 LDAP 同步用戶(hù)信息,提供 UI 供用戶(hù)配置權(quán)限。在大數(shù)據(jù)引擎這一側(cè),可以添加各種插件,通過(guò)插件來(lái)實(shí)現(xiàn)權(quán)限的攔截和檢查。Ranger 是目前一個(gè)可行的方案,但是在公有云上面對(duì)我們自研的大數(shù)據(jù)引擎,是沒(méi)法直接對(duì)接的。另一方面雖然它包括了如 SparkSQL 等類(lèi)插件,但是官方的支持并不好,更多還是需要自研一些插件,或者找第三方插件,整體部署起來(lái)并沒(méi)有那么簡(jiǎn)單。
因此在權(quán)限這一塊,DLF 統(tǒng)一元數(shù)據(jù)也提供了鑒權(quán)的能力。
權(quán)限控制默認(rèn)是沒(méi)有開(kāi)啟的,因?yàn)椴灰欢ㄋ杏脩?hù)都需要,但是用戶(hù)可以按 catalog 級(jí)別進(jìn)行開(kāi)關(guān)。catalog 是基于 database 之上的一層管理模型,如果基于 catalog 設(shè)置權(quán)限之后,管理員就可以在控制臺(tái)進(jìn)行具體的授權(quán)操作。包括 database、table、column、function 這些粒度都可以進(jìn)行授權(quán)。也可以設(shè)置不同 action 的權(quán)限,比如只給某個(gè)人對(duì)某個(gè) table 設(shè)置 select 權(quán)限,而不設(shè)置 insert 的權(quán)限。同時(shí)也支持 RBAC,可以把權(quán)限包在 role 里,統(tǒng)一賦權(quán)給一組用戶(hù),這些基本的能力都是具備的。
在鑒權(quán)環(huán)節(jié)的實(shí)現(xiàn)上,我們提供了兩個(gè)層面的鑒權(quán),第一層面是元數(shù)據(jù)的 API,我想要查看 table 或者 create table,這種動(dòng)作會(huì)在服務(wù)端上鑒權(quán)。因?yàn)槲覀兊脑品?wù)會(huì)直接去鑒權(quán),判斷發(fā)送請(qǐng)求的用戶(hù)角色是否有相應(yīng)動(dòng)作的權(quán)限,如果沒(méi)有就會(huì)進(jìn)行攔截。另外因?yàn)橛行?SQL 操作在元數(shù)據(jù)層面感知不到,比如在元數(shù)據(jù)上可能就是查一張表,但是并不知道是在往里寫(xiě)數(shù)據(jù)還是在讀數(shù)據(jù),這個(gè)時(shí)候和 Ranger 類(lèi)似,我們也提供了引擎的插件,可以放在 Spark、Hive 上做一層攔截。和 Ranger 類(lèi)似,會(huì)在內(nèi)部檢查代理用戶(hù)到底有沒(méi)有 select 權(quán)限,沒(méi)有的話(huà)去做攔截。這兩層的鑒權(quán)模型,適用于不同的場(chǎng)景。
再介紹一個(gè)額外功能,就是元數(shù)據(jù)遷移。元數(shù)據(jù)本身無(wú)論在云上,還是自建的 MySQL 的元數(shù)據(jù),如果想要遷移,都需要一個(gè)遷移的過(guò)程。為了簡(jiǎn)化這個(gè)過(guò)程,我們?cè)诋a(chǎn)品上做了元數(shù)據(jù)遷移的功能,在控制臺(tái)上就可以做數(shù)據(jù)遷移。
簡(jiǎn)單來(lái)講我們會(huì)去連遠(yuǎn)端的 MySQL 數(shù)據(jù)庫(kù),如果這個(gè)數(shù)據(jù)庫(kù)在阿里云 VPC 內(nèi),會(huì)自動(dòng)打通網(wǎng)絡(luò),通過(guò) JDBC 直接拉取元數(shù)據(jù),轉(zhuǎn)換成我們?cè)粕系?DLF 元數(shù)據(jù),這是直接產(chǎn)品化的。除了導(dǎo)入需求,可能還會(huì)有導(dǎo)出需求,包括兩邊元數(shù)據(jù)對(duì)比的需求。這些也提供了現(xiàn)成的工具可以直接使用。在元數(shù)據(jù)遷移方面,不管是導(dǎo)入導(dǎo)出還是其它方面的需求,我們都保持開(kāi)放性,不需要擔(dān)心元數(shù)據(jù)被綁定的問(wèn)題。
除了元數(shù)據(jù)遷移,可能在有些場(chǎng)景下還需要做元數(shù)據(jù)抽取,快速構(gòu)建出湖上的元數(shù)據(jù)。元數(shù)據(jù)抽取適合于這樣的場(chǎng)景,比如數(shù)據(jù)湖上已經(jīng)有一些數(shù)據(jù)文件了,可能是從其它數(shù)倉(cāng)拷貝過(guò)來(lái)的,或者是一些零散的 CSV 數(shù)據(jù)集文件等等。這個(gè)時(shí)候因?yàn)槲覀儧](méi)有對(duì)應(yīng)表的元數(shù)據(jù),就需要用 DDL 語(yǔ)句自己去建表,再做查詢(xún),比較麻煩的,也容易出錯(cuò)。尤其是對(duì)于像 JSON 這種半結(jié)構(gòu)化的嵌套類(lèi)型,更難去寫(xiě)建表語(yǔ)句。這種情況下使用我們這個(gè)元數(shù)據(jù)抽取功能就比較方便,可以直接把元數(shù)據(jù)給推斷出來(lái)。用戶(hù)只需要填寫(xiě) OSS 路徑,我們會(huì)根據(jù)路徑格式自動(dòng)掃描下面的表,包括分區(qū)值,創(chuàng)建好之后,就會(huì)寫(xiě)入到元數(shù)據(jù)里進(jìn)行直接查詢(xún)了。包括 CSV、JSON、Parquet 、ORC 等各種格式,也包括湖格式都是可以識(shí)別出來(lái)的。值得注意的是因?yàn)槲覀冏龈袷酵茢嘈枰獟呙杷袛?shù)據(jù),會(huì)比較耗時(shí),于是我們采用了快速采樣的方式。
三、數(shù)據(jù)湖存儲(chǔ)管理與優(yōu)化
接下來(lái)介紹我們?cè)跀?shù)據(jù)湖存儲(chǔ)分流方面做的一些管理和優(yōu)化。
首先介紹一下元倉(cāng),元倉(cāng)是我們?cè)谠獢?shù)據(jù)存儲(chǔ)之外做的一個(gè)在線(xiàn)的元數(shù)據(jù)的數(shù)據(jù)倉(cāng)庫(kù)。因?yàn)樵獢?shù)據(jù)存儲(chǔ)本身是在線(xiàn)服務(wù),需要比較高的讀寫(xiě)事務(wù)保障,有些后臺(tái)分析,包括一些聚合查詢(xún)是不適合在這里做的。于是我們做了一個(gè)實(shí)時(shí)的元數(shù)據(jù)倉(cāng)庫(kù)。元倉(cāng)底層是基于 Max Compute 和 Hologres 實(shí)現(xiàn)的,它會(huì)收集元數(shù)據(jù)的變更信息,也會(huì)收集計(jì)算引擎的查詢(xún)和寫(xiě)入的信息,包括存儲(chǔ)上的信息都會(huì)實(shí)時(shí)收集到。這樣我們就形成了圍繞 database 的 table partition 做的指標(biāo)庫(kù),即 data profile 指標(biāo)。我們會(huì)把這些指標(biāo)通過(guò)標(biāo)準(zhǔn)的 API 暴露出來(lái)。一方面可以在控制臺(tái)上可以做統(tǒng)計(jì)分析,包括對(duì)接我們的一些云產(chǎn)品,如 dataworks 之類(lèi),做一些數(shù)據(jù)展示和預(yù)估。另一方面這些指標(biāo)可以用來(lái)做存儲(chǔ)生命周期的優(yōu)化和管理。
接下來(lái)舉例介紹一下 Data Profile 指標(biāo)的幾個(gè)實(shí)現(xiàn)。
首先是表和分區(qū)的大小,這是一個(gè)比較基礎(chǔ)的屬性。通常來(lái)講,表和分區(qū)大小是寫(xiě)在元數(shù)據(jù)層,即 Hive 元數(shù)據(jù)的 table property 里面,本身就定義了,計(jì)算引擎會(huì)在創(chuàng)建表或者分區(qū)的時(shí)候?qū)懭?。但是不同引擎?xiě)入的標(biāo)準(zhǔn)會(huì)不一樣,比如 Hive 是叫 totalSize,Spark 是以 Spark 開(kāi)頭的屬性值。另外,這些寫(xiě)入也需要一些參數(shù)去開(kāi)啟,不開(kāi)啟是不會(huì)進(jìn)行寫(xiě)入的。所以在實(shí)際情況中會(huì)發(fā)現(xiàn)元數(shù)據(jù)本身存儲(chǔ)的表大小是不準(zhǔn)確的。
在元倉(cāng)里,因?yàn)槲覀兡J(rèn)大部分?jǐn)?shù)據(jù)湖使用的是 OSS,我們會(huì)通過(guò) OSS 的底層存儲(chǔ)來(lái)獲取表分區(qū)的大小,這樣可以最大限度保證數(shù)據(jù)的準(zhǔn)確性。因?yàn)?OSS 提供了一個(gè) t+1 更新的存儲(chǔ)清單,這一點(diǎn)類(lèi)似于 LAMBDA 架構(gòu),會(huì) t+1 更新存儲(chǔ)清單的表和分區(qū)的存儲(chǔ)大小。另外對(duì)于實(shí)時(shí)表和分區(qū)的變更,我們也會(huì)監(jiān)聽(tīng)到,再實(shí)時(shí)的從 OSS 那邊拿到最新的大小去做更新。也就是通過(guò)存量加增量的流程去獲取表分區(qū)的大小,拿到大小之后,會(huì)每天產(chǎn)出一些分析報(bào)表,比如表的存儲(chǔ)排名,文件大小占比等等。因此我們可以看到哪些表,哪些分區(qū)的存儲(chǔ)占用比較大,去做相應(yīng)的優(yōu)化。
上面是一個(gè)比較完整的湖上管理視圖。
另外再介紹兩個(gè)關(guān)鍵指標(biāo)。
第一個(gè)指標(biāo)是表和分區(qū)的訪(fǎng)問(wèn)頻次,通過(guò)訪(fǎng)問(wèn)頻次可以鑒別那些仍然在用但訪(fǎng)問(wèn)不頻繁的表。這些表可以在 OSS 底層置為低頻存儲(chǔ),照常讀取的同時(shí)可以節(jié)省一些成本。在原理上我們通過(guò)使用引擎的 Hook 來(lái)實(shí)現(xiàn)對(duì)訪(fǎng)問(wèn)頻次的獲取,我們解析 SQL 的 plan,拿到它讀取的表和分區(qū),再提交到元數(shù)據(jù)服務(wù)里去做記錄,最后把訪(fǎng)問(wèn)頻次指標(biāo)統(tǒng)計(jì)出來(lái)。
第二個(gè)指標(biāo)是表和分區(qū)的最后訪(fǎng)問(wèn)時(shí)間。它可以用來(lái)識(shí)別這個(gè)表和分區(qū)是否還有人在訪(fǎng)問(wèn)。為了保證指標(biāo)的準(zhǔn)確性,最后訪(fǎng)問(wèn)時(shí)間是通過(guò) OSS 底層的訪(fǎng)問(wèn)日志獲取的。這樣不管通過(guò)任何引擎任何途徑讀這里面的數(shù)據(jù),訪(fǎng)問(wèn)時(shí)間都會(huì)獲取到。最后對(duì)于沒(méi)有人使用的表和分區(qū),就可以考慮做歸檔或者刪除。
結(jié)合這幾個(gè)指標(biāo),更有利于我們做庫(kù)表分區(qū)的生命周期管理。因?yàn)楹仙芷诠芾硪彩且淮笾攸c(diǎn),因?yàn)閿?shù)倉(cāng)是有存儲(chǔ)分層的概念,但在數(shù)據(jù)湖上是沒(méi)有一個(gè)比較完整的管理能力。我們目前就在做這方面相關(guān)的事情。
首先我們使用的標(biāo)準(zhǔn)型 OSS 對(duì)象存儲(chǔ)是提供了存儲(chǔ)分層能力的,也可以按需設(shè)置成低頻歸檔,冷歸檔這些層次。設(shè)置好歸檔之后,會(huì)對(duì)數(shù)據(jù)訪(fǎng)問(wèn)方式產(chǎn)生影響,但是存儲(chǔ)成本會(huì)大幅降低。
用戶(hù)首先可以設(shè)置一些規(guī)則,包括基于分區(qū)值,分區(qū)的創(chuàng)建時(shí)間,上面提到的訪(fǎng)問(wèn)頻次等指標(biāo),配置規(guī)則設(shè)定閾值,比如多長(zhǎng)時(shí)間沒(méi)人訪(fǎng)問(wèn)或者 30 天內(nèi)訪(fǎng)問(wèn)頻次低于幾次。后臺(tái)就會(huì)定期把符合這些條件的分區(qū)的整個(gè)目錄做歸檔,或置為低頻等。
另外歸檔和冷歸檔做了之后是不能直接訪(fǎng)問(wèn)的,需要一個(gè)解凍的流程。如果用戶(hù)有一天需要訪(fǎng)問(wèn)已經(jīng)歸檔的數(shù)據(jù),可以一鍵解凍,整個(gè)目錄就可以直接使用,而不需要像 OSS 那樣逐個(gè)文件進(jìn)行解凍操作。這種存儲(chǔ)生命周期管理的存儲(chǔ)優(yōu)化,對(duì)于存儲(chǔ)量比較高的數(shù)據(jù)湖用戶(hù)來(lái)說(shuō)會(huì)是一個(gè)比較好的實(shí)踐。
四、數(shù)據(jù)湖格式管理與優(yōu)化
最后介紹一下在數(shù)據(jù)湖格式層面,我們做的管理和優(yōu)化。
常見(jiàn)湖格式 Hudi、Iceberg 有幾個(gè)特點(diǎn),為了實(shí)現(xiàn) ACID,它們的底層數(shù)據(jù)文件更新,copy on write 之后,讀取的都是新版本的數(shù)據(jù)文件,但是老版本的數(shù)據(jù)還會(huì)保留在存儲(chǔ)側(cè)。時(shí)間一長(zhǎng)就需要清理歷史版本的數(shù)據(jù)文件。另一方面頻繁流式寫(xiě)入會(huì)產(chǎn)生很多小文件,通??梢允褂妹钍謩?dòng)清理,或者結(jié)合在 streamming 任務(wù)當(dāng)中,配置一些參數(shù),比如多少 commit 清理一次,但是這對(duì)流式寫(xiě)入本身的性能會(huì)產(chǎn)生影響。針對(duì)這種情況,業(yè)內(nèi)很多公司都使用額外部署 table service 的方式,不影響流式寫(xiě)入,另起一個(gè)批作業(yè)去清理和優(yōu)化。DLF 把這種 table service 做在了云服務(wù)里面,這樣使用 DLF 湖格式的用戶(hù),可以直接在控制臺(tái)上配置規(guī)則,比如基于版本號(hào)更新多少次就做一次清理。后臺(tái)就會(huì)跑任務(wù)做 vacuum 或者 optimize 命令,整個(gè)過(guò)程也是全托管的,用戶(hù)不用關(guān)心背后使用的資源。
具體實(shí)現(xiàn)原理為,元倉(cāng)會(huì)維護(hù)很多元數(shù)據(jù)的變化和引擎消息,也會(huì)感知到哪些湖格式表發(fā)生了寫(xiě)入和變化。每一次表的寫(xiě)入,就會(huì)觸發(fā)規(guī)則引擎去做一次判斷是否滿(mǎn)足條件,如果滿(mǎn)足條件就會(huì)觸發(fā)動(dòng)作的執(zhí)行。目前我們對(duì) Delta Lake 已經(jīng)有比較完整的支持了,對(duì) Hudi 的支持也在進(jìn)行當(dāng)中。這是一個(gè)比較新的模塊。
再具體介紹下湖格式管理的幾種優(yōu)化策略。
第一種也是最常見(jiàn)的,基于版本間隔,清理清理歷史文件或者合并小文件。比如寫(xiě)入了 20 個(gè) commit 之后就會(huì)自動(dòng)觸發(fā)整個(gè)表的清理,或者小文件合并。這個(gè)閾值是可以隨用戶(hù)級(jí)別或者作業(yè)級(jí)別做配置的。內(nèi)部會(huì)把這些合并的任務(wù)放在一個(gè)隊(duì)列里,這樣前一個(gè)合并任務(wù)還沒(méi)有跑完,是不會(huì)跑下一個(gè)合并任務(wù)的,避免并發(fā)執(zhí)行,產(chǎn)生寫(xiě)沖突現(xiàn)象。
第二種合并規(guī)則是我們?cè)诳蛻?hù)實(shí)踐過(guò)程當(dāng)中覺(jué)得比較實(shí)用的,基于時(shí)間分區(qū)自動(dòng)合并上一個(gè)分區(qū)的小文件。因?yàn)樵诹魇綄?xiě)入的場(chǎng)景下,通常會(huì)按時(shí)間順序去命名分區(qū)值,每寫(xiě)入一個(gè)新分區(qū)就代表上一個(gè)分區(qū)寫(xiě)入停止。在這個(gè)時(shí)候,一旦發(fā)現(xiàn)有新分區(qū)創(chuàng)建,就可以去對(duì)上一個(gè)分區(qū)做一些優(yōu)化和合并的動(dòng)作。這樣上一個(gè)分區(qū)后續(xù)的查詢(xún)性能就能得到保證,同時(shí)這種做法也能最大程度避免合并任務(wù)和寫(xiě)入流任務(wù)的寫(xiě)沖突。當(dāng)然為了實(shí)現(xiàn)這個(gè)方案,我們內(nèi)部也是做了時(shí)間格式的支持,自動(dòng)處理了很多分區(qū)值的時(shí)間格式。這樣我們就可以自動(dòng)識(shí)別這些時(shí)間分區(qū)哪個(gè)分區(qū)是最新的,哪個(gè)分區(qū)是上一分區(qū)的。
五、問(wèn)答環(huán)節(jié)
Q1:DLF 元數(shù)據(jù)的管理,跟 Databricks 推的 unity catalog 有什么區(qū)別?
A1:DLF 元數(shù)據(jù)管理有點(diǎn)類(lèi)似于 Hive Metastore 的升級(jí)。Databricks 推的 unity catalog 其實(shí)是跟它的執(zhí)行引擎,Databricks 的 Spark 的綁定比較多,它是基于 Databricks 的引擎去做很多事情。我們對(duì)單個(gè)引擎的集成沒(méi)有 unity catalog 那么完整,但是更聚焦在云上的統(tǒng)一元數(shù)據(jù),即同一份元數(shù)據(jù)可以被云上各種各樣的引擎,包括自研的和開(kāi)源的引擎,都能統(tǒng)一的進(jìn)行讀寫(xiě)??偨Y(jié):我們對(duì)云上統(tǒng)一的數(shù)據(jù)這個(gè)角度做的比較多,針對(duì)的是多引擎的打通。對(duì)某一個(gè)引擎內(nèi)部做的集成沒(méi)有 unity catalog 那么深入。
Q2:DLF 的 OPEN API 是開(kāi)源的嗎?
A2:首先我們是一個(gè)全托管的云產(chǎn)品,內(nèi)部的實(shí)現(xiàn)是做成云服務(wù)。然后我們會(huì)提供標(biāo)準(zhǔn)的 API,用戶(hù)可以通過(guò)阿里云的 SDK 對(duì) API 的調(diào)用和使用。最后我們的元數(shù)據(jù) client,適配 Hive client,同時(shí) client 本身也是開(kāi)源的,內(nèi)部的元數(shù)據(jù)服務(wù)是在云上實(shí)現(xiàn)的。
Q3:DLF 針對(duì)小文件治理,計(jì)算資源控制。跟湖格式相關(guān)的小文件合并的問(wèn)題。
A3:目前因?yàn)槲覀兊暮袷降男∥募卫懋a(chǎn)品還處在公測(cè)階段,還沒(méi)有進(jìn)行真正的計(jì)費(fèi)。底層的資源我們是內(nèi)部提供的,不使用用戶(hù)的資源。我們內(nèi)部是會(huì)做一些針對(duì)單租戶(hù)的,最大的使用量的限制的。目前計(jì)費(fèi)策略還沒(méi)有明確的推出。這個(gè)可能會(huì)等到后續(xù)足夠完善之后再去做相關(guān)事情。
Q4:現(xiàn)在的 Hive Hook 解析 HSQ 的 SQL,Matestore 的 listener 能監(jiān)聽(tīng) DDL 嗎?
A4:我們現(xiàn)在實(shí)現(xiàn)的 listener 是能夠監(jiān)聽(tīng)到 DDL 的。首先 DLF 元數(shù)據(jù)本身,因?yàn)閯偛盘岬搅宋覀円灿性獋}(cāng)。其實(shí)內(nèi)部是會(huì)監(jiān)聽(tīng)到所有元數(shù)據(jù)的變更,同時(shí)我們也會(huì)基于引擎的 Hook 去監(jiān)聽(tīng)表查詢(xún)的信息,維護(hù)到 DLF 元倉(cāng)里面。因?yàn)槲覀兊膶?shí)現(xiàn)是沒(méi)有 Metastore 的,用戶(hù)可以從 DLF 的 Data Profile 的 API 進(jìn)行獲取。如果想自己實(shí)現(xiàn)像以前的 Hive Metastore 一樣的 Metastore 的 listener,這是不支持的。但是可以基于我們?cè)粕系?API 去獲取元信息。