天天看點

PostgreSQL 技術内幕(二) Greenplum-AO表

作者:HashData

序言

Greenplum(以下簡稱 GP)是一種基于開源PostgreSQL基礎上采用MPP架構的關系型分布式資料庫,具有強大的大規模資料分析處理能力。

GP有兩種存儲格式:Heap表和AO表。其中,AO表是Greenplum所特有的,主要面向OLAP場景,支援行存和列存,批量的資料寫入,有利于高吞吐資料量的加載,同時支援對資料進行壓縮,AOCO不僅支援表級别的壓縮,同時也支援列級别的壓縮。

GP-AO表的分析速度快,對于OLAP場景,每次分析涉及的字段較少,可以保證行級嚴格事務。這種設計對大批量資料的通路和統計需求而言,能夠有效提升分析速度。

在近期的直播中,HashData核心研發工程師介紹了GP-AO表的設計和特點。以下内容根據直播文字整理。

存儲引擎概要

Heap表是從PostgreSQL繼承而來的,目前是 GP 預設的表存儲格式,隻支援行存儲,不支援列存和壓縮。

Heap頁面存儲的資料稱為元組(Tuple),在實體檔案上不按照某種順序進行排序。當讀取Heap檔案頁面時,也不會對元組的排序做任何假設。Heap的存儲格式對于OLTP和OLAP兩個通路模式都是比較有效的,但更适合OLTP的場景,比如插入新的元組不需要考慮元組間的互相順序,而且删除和更新元組也非常簡單。

在OLAP的場景下,查詢更多的是全表掃描。通常情況下,查詢語句并不會讀取所有列的資料,而會篩選出感興趣的列。由于元組是将所有列都存放在一塊,這樣會增加額外的IO開銷。此外,Heap由于采用跨頁存儲,檢索非常複雜,對資料的壓縮也不如AO和AOCS,對存儲的開銷較大。

總體來講,Heap表的設計理念注重平衡性,對于元組增删查改的應用場景支援比較均衡。

存儲引擎的AO表設計

在OLAP的場景下,資料大多是一些曆史資料或日志資料,一般不會修改或者僅少量修改,元組更多以追加而非修改的方式存放。

另外,從資料通路方式來看,OLAP需要讀取大量記錄,記錄多以掃描的方式進行讀取。同時,由于每個頁面存在空洞或者已經被删除的無效資料,掃描通路方式對于Heap來說并不高效。

基于此,Greenplum引入了AO表,用來專門存儲以追加方式插入的元組。最開始設計時,AO表被稱為Append Only表,隻支援追加新元組。在後來的演進中,也支援了删除和更新元組操作,是以現在AO表指的是Append Optimized。

與Heap表相比,AO表存儲更緊湊,記錄之間沒有空餘空間,在AO表上進行分析通常效率更高。此外,AO表可以支援列式存儲,在處理大批量資料時具有顯著優勢,非常适合向量計算和JIT架構。

對于AO表的每個檔案,元組總是添加到檔案末尾,是以檔案的結尾位址(EOF)就可以作為資料可見性判斷的依據。隻有當事務成功送出後,從原來EOF之後新追加的元組才對外可見,否則對外隻能看到檔案原來的EOF。

對此,Greenplum中提供了兩個系統表記錄相應的資訊:pg_appendonly和pg_aoseg.pg_aoseg_。其中,pg_appendonly表中segrelid記錄了AO表所對應pg_aoseg.pg_aoseg_表的OID。

pg_aoseg.pg_aoseg_為Heap表,記錄了AO表每個資料檔案的EOF。這樣可以通過MVCC來管理pg_aoseg.pg_aoseg_表中的EOF資訊。

pg_aoseg.pg_aoseg_中的OID指的是AO表的OID。

另外,Heap的元組存儲了太多可見性相關的資訊,由于EOF已經可以作為可見性判斷的依據,是以AO表中存儲的元組不需要存儲這些額外的資訊。

二者對比而言,AO表存儲的元組結構是MemTuple,Heap假設的通路場景是随機和順序通路,AO表假設的通路場景多是順序掃描;Heap表的塊必須要大小一緻,便于随機尋址,AO表的塊并不需要定長存儲,可以進行變長壓縮,以節省空間。

Heap表的塊多個程序間需要共享,通過共享緩沖區進行管理。AO表由于是變長塊,而且多是順序掃描,是以不經過共享緩沖區。

為了支援删除操作,AO表引入了VisibilityMap。如果元組被删除,将會在VisibilityMap中标記。該VisibilityMap資訊由系統表pg_aovisimap_儲存。AO表的更新操作的實作也就轉換為删除操作+插入操作。

由于AO表采用的是變長塊,無法通過檔案内的邏輯塊号或行号直接定位到實體位置,但是為了支援在AO表上建立索引,需要通過行号快速定位到實體位置以便進行元組讀取。當AO表上建有索引時,Greenplum中會建立系統表pg_aoblkdir_來存儲行号到實體位置的映射資訊,來減少額外的存儲開銷。

PostgreSQL 技術内幕(二) Greenplum-AO表

AO表設計架構結構總體概略

資料設計

AO表的資料設計實作面對的主要問題是資料的增删改查,這就需要解決四個問題:資料存放、資料可見性、資料表結構設計以及資料如何定位的實作。

其中,對于變長記錄在資料庫系統中的出現有幾個原因。最常見的原因是變長域的出現,比如字元串。實作變長記錄可以采用不同的技術,但都必須解決兩個問題:

  1. 如何表示單條記錄,使得此記錄的單個屬性能夠被輕松地提取,即使這些屬性是變長的;
  2. 如何在一個塊中存儲變長的記錄,使得一個塊中的記錄能夠被輕松地提取。

具有變長屬性的記錄表示通常包含兩個部分:首先是帶有定長資訊的初始部分,其結構對于相同關系的所有記錄都是一樣的。緊接着是變長屬性的内容,諸如數字值、日期或定長字元串等固定長度的屬性,被配置設定存儲它們的值所需的位元組數。對于可變長字元串類型這類的變長屬性,在記錄的初始部分中被表示為一個(偏移量,長度)。

對于塊中變長記錄的存儲問題,比如分槽的頁結構,一般用于在塊中組織記錄,每個塊開始有塊頭,包含如下資訊:塊頭的記錄項的數量、塊中的自由空間的末尾處、一個包含每條記錄的位置和大小的項組成的數組。

資料存放設計

AO表通過EOF來控制可見性,相比于HeapTuple,元組中不再需要存儲可見性相關資訊。MemTuple除了可用在AO表存儲格式之外,還會用在執行器中,因為當元組從磁盤讀取出到執行器後,不再需要保留可見性相關資訊。

資料檢索設計

對于AO表,每個事務會寫不同的分片檔案,是以AO表中元組的位置不再像Heap表中的線性結構,而是由分片檔案編号(7位,範圍0~127)和檔案内行号(40位)确定。AO表通過AOTupleId資料結構來表示AO表中元組的位址,由于元組位址會用到多個地方,比如索引、索引掃描等,是以AOTupleId采用了和ItemPointerData同樣的大小和對齊方式。AOTupleId一共48位6位元組,以16位對齊。

AO表采用的是變長塊,和ItemPointerData不同的是,并不能簡單的從AOTupleId對應到元組的實體位置。在Greenplum中,引入了塊目錄(BlockDirectory)的表和資料結構,來友善查找從分片檔案号以及塊内行号來獲得在檔案中的實體位置。隻有當AO表上建立有索引時,塊目錄才會建立。

塊目錄表維護了從分片檔案号、列組編号(columngroup_no,AO表始終為0,AOCS會用到)、起始行号三者到minipage的映射。minipage是由MinipageEntry組成的數組,每個MinipageEntry記錄了一個或多個塊的起始位置,包含如下資訊:表MinipageEntry重要成員、firstRowNum 起始行号、fileOffset檔案内偏移位置、rowCount 行數。

索引掃描或者位圖掃描時,會通過AOTupleId讀取單個元組,其掃描過程如下:

  1. 通過AOTupleId計算得到目标分片檔案号segmentFileNum和行号rowNum。
  2. 如果目前打開分片檔案不是segmentFileNum,則關閉目前檔案,重新打開第 segmentFileNum号檔案。
  3. 判斷AOTupleId在塊目錄表中是否存在,如果不存在,說明AOTupleId是舊的被回收的元組,或者之前異常終止事務插入的元組,這種情況下,傳回讀取失敗。
  4. 通過VisibilityMap進行可見性檢查,如果不可見,傳回失敗。
  5. 将檔案的讀取起止範圍設定為塊目錄表項中的起止範圍。由于塊目錄表項可能對應一個或者多個塊,是以表項中記錄的起始位置可能比實際的檔案塊大。
  6. 調用函數scanToFetchTuple在起止範圍内逐個讀取檔案塊,直到找到某個塊滿足:currentBlock.firstRowNum <= rowNum <= currentBlock.lastRowNum。如果沒找到,則傳回失敗。
  7. 在塊内查找查找行号為rowNum的元組并傳回。
  8. 儲存目前分片檔案号,目前塊起止行号,目前塊目錄資訊,下次再次讀取單個元組時,如果:

   a、AOTupleId在目前起止行号之前,直接在目前塊内查找元組,否則直接跳轉到b。

   b、在目前塊目錄範圍内,跳轉到第4步開始執行。

資料可見性設計

AO表引入了另外一個輔助的Heap表pg_aoseg.pg_aovisimap_,稱為VisibilityMap。該表記錄了删除元組的資訊,并且通過MVCC控制可見效。如果删除成功,通過該表就能查詢到删除元組,進而判斷元組不可見。如果每個删除的元組占用一行,顯然不經濟并且低效。大部分情況下,AO表中元組的通路都是順序掃描,VisibilityMap借鑒了塊目錄表的思路,将多個相鄰元組可見性資訊放在一起存儲。

Greenplumn中用位圖來表示每個分片檔案中元組的可見性,如果元組被删除,對應位圖的比特位置1。每32768(APPENDONLY_VISIMAP_MAX_RANGE)個元組的可見性比特位作為一個位圖存儲在表pg_aoseg.pg_aovisimap_的visimap屬性中。

Greenplum中在位圖表上建立了索引,友善快速查找。每個位圖4096個位元組,包含32768位,可以表示32768個元組的可見性。VisibilityMap通過函數AppendOnlyVisimapDelete_Init開始位圖的修改,通過函數AppendOnlyVisimapDelete_Finish結束位圖的修改。當某個行被删除時,需要判斷該行對應的位圖是否以及已經在VisibilityMap表中已記錄,如果是則讀入記憶體中,否則初始化一個全零的位圖。

AOCS列存的設計

AO表整個行一起存儲,當需要讀取屬性内容時,首先需要讀取變長塊,解壓縮,然後再擷取其中一個屬性的值。這樣做有時代價非常高,即使查詢隻涉及其中一個屬性,也需要将所有内容讀出來。在OLAP分析領域,列式存儲是應對這種查詢場景、提高性能的常見優化方式。Greenplum中也提供列式存儲:AOCS

AOCS将不同的屬性分成不同的檔案存儲。查詢時,隻需要讀取需要的屬性所對應的檔案,其它屬性不需要讀入,節省了磁盤IO開銷。

同AO表一樣,AOCS的通路也不需要通過共享緩沖區管理器,直接從磁盤進行讀寫。另外,AOCS對資料的壓縮做了特殊的處理,能夠獲得更好的表現。

AOCS表的存儲類似AO表的存儲,但是将每個列存儲在單獨的分片檔案中,其中,第0到127号分片檔案存儲第一個屬性,第128到255号分片檔案存儲第二個屬性,依次類推。其中,第0,128,256等檔案邏輯上屬于第0個分片檔案,配置設定給Utility模式使用,第1,129,257等檔案邏輯上屬于第1個分片檔案,依次類推。

對于MPP模式,一個可以支援127個配置設定檔案并發寫。通過這種實體檔案映射到邏輯分片檔案的方法,AOCS表可以複用與AO表相同的邏輯,比如AOTupleId、索引、VisibilityMap。

結語

HashData核心基于開源的PostgreSQL和Greenplum Database建構,中繼資料采用開源的KV資料庫FoundationDB提供持久化,通過ORC、Parquet等開放檔案格式與其它大資料系統實作互通互聯。

傳統的Greenplum、Teradata等MPP 架構的資料庫,存儲、計算是緊耦合的,資料存儲在本地系統,存儲能力的擴充通過增加叢集節點實作,這樣會導緻計算資源嚴重浪費,無法滿足業務的發展。

作為一款企業級資料倉庫産品,HashData在PostgreSQL和Greenplum Database等平台豐富的分析功能的基礎上,針對雲平台特性進行了大量改進和優化,實作了存算分離、湖倉一體化,滿足企業對海量資料的分析與處理需求,加速企業數字化轉型。

END

HashData研發、行業銷售、工程服務等崗位正在火熱招聘中,歡迎掃描下圖二維碼擷取職位詳細資訊,和我們随時聯系!

繼續閱讀