本篇主要是根據AnalyticDB的論文,來討論AnalyticDB出現的背景,各個模塊的設計,一些特性的解析。可能還會在一些點上還會穿插一些與當前業界開源實現的比對,但願可以有一個更加深刻的探討。OK,那咱們開始吧。html
要說AnalyticDB,那起碼得知道它是幹什麼的。這裏直接貼下百度百科的介紹:算法
AnalyticDB是阿里雲自主研發的一款實時分析數據庫,能夠毫秒級針對千億級數據進行即時的多維分析透視。sql
簡單地說,就是實時OLAP型數據庫,它的對標產品是Apache Kylin,Apache Druid,Clickhouse這些。而後AnalyticDB的特色,包括高併發實時攝入數據,兼容Mysql協議,無需預計算便可有的極快響應時間,多種數據源接入,大規模集羣管理等。好吧,這幾個特色都很官方,不急,接下來會逐漸討論各個點。數據庫
而後介紹下AnalyticDB的背景。json
首先先說說傳統的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是一個可以在PB數據集上高併發,低延遲,實時分析查詢,而且可以在2000+雲服務器上運行的OLAP數據庫。在設計上有多個挑戰,須要兼顧多種查詢類型的性能要求。這裏的多種情境包括全表掃描分析,多表join的點查詢操做,多個列的多個篩選條件等等,而這些操做又難以優化。
第二個挑戰是要設計一個底層存儲,應對不一樣類型的查詢所須要的不一樣存儲結構。好比OLAP查詢須要列式存儲,而點查詢(行級查詢)須要行式存儲。如何將這兩種存儲結構(列式,行式)結合起來以供不一樣查詢類型使用,同時還須要考慮到複雜類型,json,vector,text等,這也是一個難點。
第三個是實時方面的,要如何作到每秒數百萬數據寫入吞吐的同時,呈現給用戶低延遲的查詢響應和數據延遲。之前的作法將讀寫操做交由同一進程處理,但這樣一來讀寫操做的性能是互斥的,即高吞吐的寫入會影響到查詢性能和數據延遲[2]。
爲了解決上述挑戰,AnalyticDB引入如下特性:
這些特性暫時就有個映像就好,後面會詳細對這部分闡述。
要說這塊,咱們先來看看總體的架構設計圖。
前面與說到,AnalyticDB是雲原生的,AnalyticDB主要依賴於兩個外部結構,任務管理與調度組件Fuxi,和分佈式存儲系統Pangu,而這幾個組件又都是基於阿里雲的Apsara(負責管理底層的物理主機並向上層提供服務)。
總體架構上看仍是比較簡單的,主要就是對外提供JDBC/ODBC接口,內部由多個協調器(Coordinator)負責統一管理寫節點和讀節點(讀寫分離)。
在具體的處理流程中,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底層使用的存儲引擎,其強大毋庸置疑。所謂列式存儲,能夠簡單理解成就是將一整列數據壓縮打包,而後按順序存儲。
存儲中有三級結構:
在最後是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這種全面的道路呢?
以上~