PostgreSQL 技术内幕(二) Greenplum-AO表
序言
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_來存儲行號到物理位置的映射信息,來減少額外的存儲開銷。
AO表設計框架結構總體概略
數據設計
AO表的數據設計實現面對的主要問題是數據的增刪改查,這就需要解決四個問題:數據存放、數據可見性、數據表結構設計以及數據如何定位的實現。
其中,對于變長記錄在數據庫系統中的出現有幾個原因。最常見的原因是變長域的出現,比如字符串。實現變長記錄可以采用不同的技術,但都必須解決兩個問題:
如何表示單條記錄,使得此記錄的單個屬性能夠被輕松地提取,即使這些屬性是變長的;
如何在一個塊中存儲變長的記錄,使得一個塊中的記錄能夠被輕松地提取。
具有變長屬性的記錄表示通常包含兩個部分:首先是帶有定長信息的初始部分,其結構對于相同關系的所有記錄都是一樣的。緊接著是變長屬性的內容,諸如數字值、日期或定長字符串等固定長度的屬性,被分配存儲它們的值所需的字節數。對于可變長字符串類型這類的變長屬性,在記錄的初始部分中被表示為一個(偏移量,長度)。
對于塊中變長記錄的存儲問題,比如分槽的頁結構,一般用于在塊中組織記錄,每個塊開始有塊頭,包含如下信息:塊頭的記錄項的數量、塊中的自由空間的末尾處、一個包含每條記錄的位置和大小的項組成的數組。
數據存放設計
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等平臺豐富的分析功能的基礎上,針對云平臺特性進行了大量改進和優化,實現了存算分離、湖倉一體化,滿足企業對海量數據的分析與處理需求,加速企業數字化轉型。
HashData研發、行業銷售、工程服務等崗位正在火熱招聘中,歡迎掃描下圖二維碼獲取職位詳細信息,和我們隨時聯系!
總結
以上是生活随笔為你收集整理的PostgreSQL 技术内幕(二) Greenplum-AO表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xcode真机调试中There was
- 下一篇: Django计算机毕业设计餐饮管理系统(