目錄
- AnalyticDB介紹與背景
- AnalyticDB詳細決議
- 架構設計
- 資料磁區
- 讀寫分離和讀寫流程
- 其他特性介紹
- 混合(列-行)存盤引擎
- 索引
- 小結
- 架構設計
本篇主要是根據AnalyticDB的論文,來討論AnalyticDB出現的背景,各個模塊的設計,一些特性的決議,可能還會在一些點上還會穿插一些與當前業界開源實作的比對,希望能夠有一個更加深入的探討,OK,那我們開始吧,
AnalyticDB介紹與背景
要說AnalyticDB,那起碼得知道它是干什么的,這里直接貼下百度百科的介紹:
AnalyticDB是阿里云自主研發的一款實時分析資料庫,可以毫秒級針對千億級資料進行即時的多維分析透視,
簡單地說,就是實時OLAP型資料庫,它的對標產品是Apache Kylin,Apache Druid,Clickhouse這些,然后AnalyticDB的特點,包括高并發實時攝入資料,兼容Mysql協議,無需預計算即可有的極快回應時間,多種資料源接入,大規模集群管理等,好吧,這幾個特點都很官方,不急,接下來會逐漸討論各個點,
然后介紹下AnalyticDB的背景,
首先先說說傳統的OLAP型資料倉庫,以往構建OLAP型資料倉庫通常都是采用離線模式,即在晚上設定定時任務將前一天的資料同步到資料倉庫中,第二天資料分析師或報表工具就可以根據資料產出分析結果,但這樣的問題是資料延遲太高了,商業瞬息萬變,可能今天線上出現了什么訂單激增的情況,資料分析師卻要等明天才能進行分析,這誰受得了呀,所以近幾年的趨勢就是實時數倉,簡單說就是增加一個實時接收資料以供查詢的模塊,這也叫做lambda架構,如圖,就是用一個Batch層和一個Real-time層共同提供查詢結果,[1]

好像有點扯遠了,說回AnalyticDB,它就是在大背景下提出的,所以它的一個主要特性就是實時,然后由于它本身是云原生的結構,也就是本身就是根植于阿里云上面的,面向的客戶更加廣泛,所以是有通用性的要求的,比如傳統企業都是使用Mysql,Postgresql等關系型資料庫,這些企業也沒有人力去搭建和維護Hadoop和Kylin,Druid這些集群,而Postgresql這類關系型資料庫可能會有對復雜結構對支持,比如json,vector等,所以AnalyticDB也提供了對這種復雜型別的支持,
在性能方面,AnalyticDB維持所有列的索引,用以快速檢索資料,在存盤方面,使用行-列混合存盤,使得AnalyticDB可以同時對OLAP分析和行級查詢快速回應,然后為了高并發的查詢和高吞吐的寫入,又提出了讀,寫分離,這幾個性能方面的特性,以及這些優化如何與實時查詢結合起來,在后面會詳細介紹,
總而言之,目前業界對海量資料的OLAP分析查詢方案無非兩種,通過預計算構建多維立方體,在查詢的時候直接讀取預計算好的資料做一些簡單的合并(因為磁區存盤)然后回傳給用戶,這種型別的代表是Kylin和Druid,它們的好處是比較簡單,OLAP分析查詢速度很快,缺點是不夠靈活,比如Kylin一點改動可能就要全部資料rebuild,
另一種是非預計算,充分利用各種資源(CPU,記憶體,列存盤,向量化執行),或是架構盡量優化(如AnalyticDB),來讓海量資料快速查詢得到結果,比較典型的代表是Clickhouse,查詢性能不賴,也相對靈活,但缺點是集群資料量沒法拓展到很大,
這兩種方案都有辦法這實時這個點上進行拓展,只是實作思路也不大一樣,第一種是添加一個流式層,OLAP查詢的時候分別查詢歷史資料和流式層資料然后合并回傳,第二種則是用微批方式倒入資料倉庫中實作流式查詢,
整體上,AnalyticDB更加偏向于第二種非預計算的方式實作,不過在很多設計上還考慮了行級查詢的實作和性能,所以要比Clickhouse這種要復雜一些,下面我們從幾個方面來討論它的實作,
AnalyticDB詳細決議
AnalyticDB是一個能夠在PB資料集上高并發,低延遲,實時分析查詢,并且能夠在2000+云服務器上運行的OLAP資料庫,在設計上有多個挑戰,需要兼顧多種查詢型別的性能要求,這里的多種情境包括全表掃描分析,多表join的點查詢操作,多個列的多個篩選條件等等,而這些操作又難以優化,
第二個挑戰是要設計一個底層存盤,應對不同型別的查詢所需要的不同存盤結構,比如OLAP查詢需要列式存盤,而點查詢(行級查詢)需要行式存盤,如何將這兩種存盤結構(列式,行式)結合起來以供不同查詢型別使用,同時還需要考慮到復雜型別,json,vector,text等,這也是一個難點,
第三個是實時方面的,要如何做到每秒數百萬資料寫入吞吐的同時,呈現給用戶低延遲的查詢回應和資料延遲,以前的做法將讀寫操作交由同一行程處理,但這樣一來讀寫操作的性能是互斥的,即高吞吐的寫入會影響到查詢性能和資料延遲[2],
為了解決上述挑戰,AnalyticDB引入以下特性:
- 高效的索引引擎
- 混合(列-行)存盤引擎
- 讀寫分離
- 高效檢索引擎
這些特性暫時就有個映像就好,后面會詳細對這部分闡述,
架構設計
要說這塊,我們先來看看整體的架構設計圖,

前面與說到,AnalyticDB是云原生的,AnalyticDB主要依賴于兩個外部結構,任務管理與調度組件Fuxi,和分布式存盤系統Pangu,而這幾個組件又都是基于阿里云的Apsara(負責管理底層的物理主機并向上層提供服務),
整體架構上看還是比較簡單的,主要就是對外提供JDBC/ODBC介面,內部由多個協調器(Coordinator)負責統一管理寫節點和讀節點(讀寫分離),
- 協調器(Conrdinator):協調器負責接收JDBC/ODBC的讀寫請求,分發到不同的讀或寫節點,
- 寫節點(Write Node):負責處理寫請求并將資料寫入到Pangu中持久化,
- 寫節點(Read Node):負責處理查詢請求并回傳,
在具體的處理流程中,Fuxi資源分配和異步調度(類似yarn),而資料計算則是使用管道的方式進行計算,如下圖:

圖中,資料按頁(Page)進行切分(Pangu的存盤特性),資料處理以管道的方式進行處理,且資料流轉的不同階段均在記憶體中執行,看這張圖其實有點像Spark的資料處理流程,當然AnalyticDB本身也是使用DAG模型進行資料處理,
不過按照論文中說的資料完全在記憶體中處理還是有點不現實,雖然這樣能極大提高處理的效率,但遇到資料量太大導致記憶體裝不下的情況,還是需要暫時落到磁盤上,就類似Spark有提供多種persist方案一樣,否則查詢的并發量勢必會受到一些影響,但這樣一來可能查詢回應又降低了,魚與熊掌不可兼得啊,
資料磁區
在一開始創建表的時候,可以分配資料按照兩級磁區進行存盤,這里通過論文中的小例子闡述兩級磁區的實作,如以下建表陳述句:
CREATE TABLE db_name.table_name (
id int,
city varchar,
dob date,
primary key (id)
)
PARTITION BY HASH KEY(id)
PARTITION NUM 50
SUBPARTITION BY LIST (dob)
SUBPARTITION OPTIONS (available_partition_num = 12);
第一級索引,可以讓資料按照指定列進行hash磁區以及指定磁區數,比如上述建表陳述句指定一級索引為id,磁區數是50個,這樣可以讓資料根據id的hash值分布到不同的50個磁區中,這一列通常是使用高基數的列,諸如用戶Id等,
第二級索引(SUBPARTITION,可選)可以針對某個列指定最大磁區數,用來對資料保留和回收,通常使用日期型別資料,比如如果指定按天進行磁區,最大磁區為12,那么資料僅會保留12天內的資料,
讀寫分離和讀寫流程
大多數傳統的OLAP資料庫,都是使用一個執行緒負責處理用戶SQL的操作,不管是寫請求(Insert)還是讀請求(Select),這在查詢和寫入的并發量都很高的情況下會出現資源爭用的情況,針對這種情況AnalyticDB提出讀寫分離的解決方案,寫節點負責寫,讀節點負責讀資料,兩種節點彼此分離,這樣就避免了高并發場景下讀寫資源互斥的情況,
寫節點主要是master和worker架構,由zookeeper進行協調管理,寫master節點負責分配一張表的磁區給不同的寫worker節點,在一個SQL到達的時候,Coordinators會首先識別是讀還是寫SQL陳述句,若是寫,那么會先發送到對應的寫worker節點,寫worker節點先將資料存到記憶體,定期以日志的形式持久化到Pangu中形成Pangu日志,當日志一定規模的時候,才會構建真正的資料和全量索引,
而對于讀節點,同樣每個節點會被實作分配不同的磁區,功能上,AnalyticDB有兩種讀模式,實時讀取(real-time read),寫入資料立即可讀,和延遲讀(boundedstaleness),即容忍一定時間的寫入資料延遲,延遲讀是默認采用的方式,雖然與一定資料延遲,但查詢回應更快,通常而言也足夠了,
而實時讀,那么可以立即查詢到剛剛寫入的資料,之所以能這么快,其中一個原因是讀節點會直接從寫節點中獲取更新資料,也就是說寫節點在某種程度上說充當了快取,其他的OLAP資料庫的做法通常有兩種,一種是用一個segment專門存盤實時資料,OLAP查詢的時候,會掃描實時segment和離線資料,合并后回傳用戶,比如最新的kylin streaming就是這樣實作,一種是微批匯入資料到存盤引擎中,然后用以檢索,這樣的話寫入頻率(微批的間隔)會大大影響檢索性能,AnalyticDB的方式可以說是一種比較新穎的方式,借助讀寫分離的架構和強大的索引能力(下面介紹),可以實作實時寫入且低延遲檢索,
不過其實這會面臨一個問題,資料同步的一致性問題(讀寫節點資料不一致),AnalyticDB是怎么做的呢?這里也不賣關子,主要是使用一個版本號來處理,Coordinators在分發寫請求給寫節點,寫節點更新后會回傳更新后的磁區版本號給Coordinators,Coordinators分發讀請求給讀節點時,也會帶上這一個磁區版本號,讀節點就會與自己快取的版本號對比,發現自己小的話,就會去拉取寫節點的最新資料(寫節點有一定的快取功能),
可以發現,通過讀寫分離的機制,以及預先分配好讀/寫節點的資料磁區(hash),能提高資料處理的并行度,并且減少資料計算產生的資料傳輸網路開銷,比如join的shuffle操作就不需要進行大規模的資料再磁區,而后有能夠將兩種請求相互解耦,每種操作關心自身就可以,方便以后的拓展,
OK,到這里系統的架構,資料磁區,讀寫流程就差不多說完了,接下來再討論下它的其他特性,
其他特性介紹
混合(列-行)存盤引擎
先說下背景,OLAP查詢一般會有全表掃描操作,所以主流做法是使用列式存盤,因為列式存盤可以極大減少磁盤IO操作,提高提高全表掃描性能,但這種對點查詢(即行級別)查詢和更新等不甚友好,而如Mysql這種行級存盤,點查詢方便,但OLAP操作又會又額外更多開銷(資料壓縮比低),許多主流系統的做法是,基本摒棄另一種功能,比如Mysql不適合做大規模OLAP查詢,Kylin,或者說hive這種不支持行級別更新(特殊情況下可以,但支持不好),Druid則更加極致,直接就不存明細了,
而AnalyticDB卻通過行-列混合存盤結構,不僅兼顧OLAP分析和點查詢,還實作了復雜型別的存盤(json,vector),不過在介紹它的行-列混合存盤結前,先來看看流行的列式存盤結構,然后再引出AnalyticDB的行列混合,
我們以開源的列式存盤結構Parquet為例來看列式存盤是怎么存盤資料的,

Parquet本身是hadoop底層使用的存盤引擎,其強大毋庸置疑,所謂列式存盤,可以簡單理解成就是將一整列資料壓縮打包,然后按順序存盤,
存盤中有三級結構:
- 行組(Row Group):按照行將資料物理上劃分為多個單元,每一個行組包含一定的行數,一個行組包含這個行組對應的區間內的所有列的列塊,
- 列塊(Column Chunk):在一個行組中每一列保存在一個列塊中,行組中的所有列連續的存盤在這個行組檔案中,不同的列塊可能使用不同的演算法進行壓縮,一個列塊由多個頁組成,
- 頁(Page):每一個列塊劃分為多個頁,頁是壓縮和編碼的單元,對資料模型來說頁是透明的,在同一個列塊的不同頁可能使用不同的編碼方式[3],
在最后是Footer模塊,這里存盤的是資料的元資料資訊,比如列名,列的型別,還有一些統計資訊,min,max,用以提升部分檢索的效率,同時Parquet也支持復雜型別的存盤,說簡單點就是將復雜型別Map,List等轉換成schema樹,把樹的葉子節點當做列資料存盤,
簡單了解了列式存盤,我們再來看AnalyticDB的行-列混合存盤,

注意圖中左右兩部分分別是兩個檔案,左邊的是元資料檔案,存盤諸如欄位名,一些簡單的統計資訊幫助過濾,這個檔案比較小通常駐存在記憶體中,這部分內容和前面的Parquet的Footer存盤內容類似,這里就不多介紹了,主要還是介紹下右邊部分,即資料存盤方式,
圖片右邊,資料以row group的形式存盤,每個row group中存盤固定數量的行,但是在row group中依舊采用列式存盤,即同一列的被存盤到一起,稱為Data bolck,所有的Data block按順序存盤(這點和列式存盤一樣),而Data block是最小的操作單元(快取,讀取等),注意這里不像Parquet那樣,每一個Data block再分多個Page,
看上去,它的存盤結構和Parquet是類似的,只是沒有再將Data block劃分成多個Page,這里論文沒和Parquet對比,也沒論述很清楚,不過最主要的區別應該就是這里了,為什么Data block不需要再劃分?因為它沒那么多資料呀,在Parquet里,一個row group的資料量是GB級別的,所以一個row group中的列需要再劃分,而AnalyticDB中,它的row group明顯是小數量級的,可能一個row group僅僅是MB級別的資料量,這一點細微的差別,使AnalyticDB在點查詢的時候就可以直接幾MB內獲取一行全部資料,而Parquet可能需要在1G內才能獲取一行資料,這也是為什么AnalyticDB的叫做行-列混合存盤結構,
對比Parquet和AnalyticDB,它們的設計分歧可能是天生的,Hadoop適合存盤追加的資料,以及非結構化資料,它的場景更多是在大資料存盤和加載,所以不會考慮單行查詢的場景,而AnalyticDB要考慮各種檢索,所以設計上就會要差異,當然AnalyticDB這樣也不是沒有缺點,比如它在全表掃描性能會有所下降,
說完AnalyticDB的存盤結構,再來說說AnalyticDB如何存盤復雜型別資料,
復雜型別資料(json,vector)存盤有個難點,這種復雜型別的資料通常大小是不定的,而且往往會出乎意料的大,如果按照上面提到row group的方式,可能一個block entry會非常大,所以需要一種其他型別的存盤結構來存盤復雜型別資料,
具體的做法可以說借鑒了hadoop的存盤思路,既然復雜型別資料大小不一樣,可能大可能小,那就將資料統一用32KB大小的塊組織起來,稱為FBlock,一個復雜型別資料可能分散在多個FBlock中(超過32KB),多個FBlock按順序存盤,然后使用稀疏索引,方便快速查詢,這樣的設計無疑可以方便得將復雜資料進行存盤,同時通過稀疏索引又能在一定程度上保證檢索的速度,
最后再說說如何支持update和delete操作,
一般的列式存盤是不怎么支持行級更新和洗掉操作的,因為資料都是壓縮成二進制進行存盤,如果支持行級更新,那并你需要先解壓縮,整塊資料,然后洗掉資料,再壓縮存盤,要是并發量一上來那簡直是災難,
那hbase的底層是hadoop,它是怎樣實作更新和洗掉的呢?這是因為hbase使用LSM-tree,我之前也過一篇介紹這個東西,也興趣可以看看資料的存盤結構淺析LSM-Tree和B-tree,粗略說就是將更新和洗掉操作都按key-value的形式追加到檔案末尾,然后整個檔案定期去重,只保留最新的key的資料,舊的key資料就被洗掉了,檢索的時候如果也多個key,只會認最新的那個key的資料,當然具體細節要復雜得多,
AnalyticDB也是類似的思想,不過做了一些改變,它使用一個存盤在記憶體中的bit-set結構,記錄更新和洗掉的資料id以及對應版本號,同時使用copy-on-write(寫時復制)技術提供多版本支持,更新和洗掉操作都會改變版本號,然后查詢的時候會提供一個版本號去查找對應的更新和洗掉資訊,然后在查詢結果中和結果進行合并,這樣就實作了更新和洗掉操作,
稍稍總結下AnalyticDB的存盤結構,行-列混合存盤的優勢確實是也的,它算是犧牲一部分OLAP查詢的性能,換取一些靈活性,而這樣的換取,使得它擁有快速行級檢索,更新洗掉的能力,對AnalyticDB而言是值得的,
索引
索引可以說是一個資料庫系統中極為重要的優化設計,目前主流的索引,包括B+tree,倒排索引,稀疏索引等等,但它們都有各種局限,比如B+tree插入分裂代價太大,倒排索引只支持特定型別,有些索引雖然能提供快速檢索的能力但對寫入性能有負擔,那么AnalyticDB的索引是怎樣實作的呢?
AnalyticDB重度使用倒排索引加速檢索效率,首先,AnalyticDB對每一列都建立一個倒排索引,索引的key是列的值,索引的值的列的行號,前面的存盤結構中可以看到,每個row group存盤的是固定的行數,所以可以快速檢索到對應的行,而針對不同的資料量特點,提供了bitmap和int array兩種結構存盤倒排索引,達到一定閾值的時候會做相應轉化,
而針對復雜型別資料(三種,json,full text,vector),還是通過倒排提供支持,只是針對不同型別做了不同的優化改動,
先說json,以往查詢json資料的做法,是要先讀取json然后決議再然后查詢,這樣效率很低,AnalyticDB采用空間換時間的思路,將json資料先決議,然后對每個列構建倒排索引(和單列倒排索引類似),在查詢的時候就可以直接根據索引快速定位到對應的json,
full-text的索引方式應該是和ElasticSearch類似的,即詞到整個檔案的倒排索引,查詢時還會按TF/IDF評分將結果回傳給用戶(ES也是這樣),
第三種型別是vector型別資料,主要采用NNS(nearest neighbour search)方法來加速查詢(看名字和KNN演算法有點像),大意也是用類似計算臨近資料的方式加速檢索,
對于增量資料,由于資料是落盤到磁盤上才構建全量索引,索引增量資料和已經落盤的資料有檢索性能的區別,所以需要對增量資料額外構建索引來彌補這種差距,而AnalyticDB對增量資料構建的是排序索引,所謂排序索引,本質上是一個陣列,存盤的是資料的id,具體原理比較難解釋清楚,可以理解為就是存盤排序后的資料的id,通過排序索引可以將全表檢索的復雜度從O(n)降低到O(log n),也是一種空間換時間的思路了,
說完AnalyticDB,我們來對比下其他索引結構,Apache Kylin就不必多說了,基本就是依托于Hbase的row-key索引機制,算是比較弱的索引機制,
和AnalyticDB比較像是應該算是Druid,也是倒排索引,不過是bitmap結構存盤的倒排索引,它的倒排索引是經過優化的,叫Roaring bitmap,可以規避存盤小資料時候的存盤空間問題,相比于AnalyticDB的大而全的索引,Druid可以說是小而美,只對維度資料存盤bitmap索引,并且是和資料一起存盤在檔案中,而非AnalyticDB那樣資料和索引分開存,出現這樣的原因一個是場景上,Druid畢竟是面向OLAP查詢的,索引它只需要對維度索引構建就行,這樣的好處在于實作簡單,存盤也不會占用太多空間,而針對單一OLAP場景,其實這樣也已經足夠了,
小結
總而言之言而總之,AnalyticDB因為其是云原生,底層存盤,資源調度等都是依托于阿里云的其他服務,所以它開源出來是不現實的(畢竟人家還靠這個賺錢),哪怕真的開源,使用者使用開源存盤和資源調度方案估計也難以做到它在阿里云生態上那么好,
不過它的架構和一些特性還是很有借鑒意義的,比如讀寫分離,預先磁區,還有行-列混合存盤,強大的索引機制,索引機制如何跟底層的存盤相互配合等,這些東西目前開源的一些系統可能還沒有或沒AnalyticDB那么完善,一方面可能是因為這些東西實作起來后,配置上會比較復雜,阿里云的東西不怕復雜,因為后端對用戶是不可見的,要是開源系統搞得特別復雜,工程師們就不大會想用這些東西,畢竟為了一些可能用不著的性能提升,引入一個后續可能維護復雜的系統,是否值得也是需要權衡的,
總體上看,AnalyticDB還是走在了業界的前頭的,好像也通過了TPC-DS的全流程測驗,算是未來可期,未來開源資料庫的方向會不會從分化走上AnalyticDB這種全面的道路呢?
以上~
Applying the Kappa architecture in the telco industry ??
AnalyticDB paper ??
深入分析 Parquet 列式存盤格式
??
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/153357.html
標籤:Java
上一篇:LeetCode–最長公共前綴
