Postgresql技術內幕系列-第一章數據庫集合、數據庫、表的邏輯設計與物理設計

原文鏈接:http://www.interdb.jp/pg/pgsql01.html

第一章數據庫集合、數據庫、表

1.1數據庫集合(database cluster)邏輯結構

1.2數據庫集合(database cluster)的物理結構

1.2.1. 數據庫集合(database cluster)的佈局

1.2.2. database的物理佈局設計

1.2.3. 表和索引的物理佈局設計

1.2.4. Tablespaces

1.3. 堆表文件(heap table file)的內部結構

1.4. 從表中讀寫tuple的方法

1.4.1. 寫Heap Tuples

1.4.2. 讀Heap Tuples


第一章數據庫集合、數據庫、表

本章和下一章概述了PostgreSQL的基礎知識,以幫助讀者閱讀後續章節。在本章中主要描述以下主題:

  • 數據庫集合(database cluster)的邏輯結構

  • 數據庫集合(database cluster)的物理結構

  • 堆表文件(heap table file)的內部結構

  • 從表中讀寫數據的方法

如果您已經熟悉上述主題,可跳過本章閱讀後續章節。

1.1數據庫集合(database cluster)邏輯結構

 database cluster是由postgresql server管理的數據庫對象的集合,由多個database對象組成。如果你現在第一次聽到這個定義,你可能會想到“數據庫集羣”,但PostgreSQL中的術語“database cluster”並不意味着“由多個數據庫服務結點組成的集羣”。 一個PostgreSQL 服務結點能夠在單個主機上運行並管理一個數據庫集合(database cluster),即多個數據庫對象。

圖1.1描述了一個數據庫集合的邏輯結構。一個數據庫集合由多個數據庫對象組成。在關係數據庫理論中,數據庫對象是用於存儲或引用數據的數據結構,包括表、視圖、索引、序列、函數等等。在PostgreSQL中,數據庫集合由邏輯上獨立的多個數據庫對象組成,各數據庫對象都有專屬於自己的表、索引等等。

                                                                                 圖1.1 數據庫集合的邏輯結構圖

所有數據庫對象都有各自的oid(object identifiers),oid是一個無符號的四字節整數。數據庫、數據表等相關對象的oid都存放在相關的system catalogs中,比如數據庫的oid和表的oid分別存放在pg_database,pg_class表中。因此,可以使用如下方法查找數據庫、數據表的oid。

 

1.2數據庫集合(database cluster)的物理結構

一個數據庫集合對應着一個目錄,目錄中包含一些子目錄和大量數據文件。

在執行initdb的時候會初始化一個新的數據庫集合,在指定目錄下創建一個基目錄(base directory )。一般而言,我們會將這個目錄用環境變量$PGDATA來表示。

圖1.2描述了PostgreSQL中的一個數據庫集合示例。數據庫集合中的各個數據庫都對應於基目錄下的一個子目錄。數據庫中的表、索引信息存儲於子目錄中的數據文件中,一個表、索引至少有一個文件與之對應。同時,在base directory中,會生成一些配置文件和幾個包含特殊數據的子目錄。在postgresql中,tablespace的概念並不同於其他關係型數據庫,這裏一個tablespace對應的都是一個目錄,該目錄與base目錄平級,存儲一些base directory外的數據。

                                                           圖1.2 數據庫集合(database cluster)的物理結構示例

1.2.1. 數據庫集合(database cluster)的佈局

數據庫集合(database cluster)的佈局的詳細描述見官方文檔. 其中的主要文件和子目錄如下:

files

description

PG_VERSION

postgresql主版本號文件

pg_hba.conf

postgresql客戶端驗證的文件

pg_ident.conf

postgresql用戶名映射的文件

postgresql.conf

配置參數文件

postgresql.auto.conf

用於存儲在ALTER SYSTEM(版本9.4或更高版本)中設置的配置參數的文件

postmaster.opts

記錄服務端上一次啓動的命令行選項

subdirectories

description

base/

包含每個數據庫子目錄的子目錄

global/

包含集合範圍表的子目錄,例如pg_database和pg_control

pg_commit_ts/

包含事務提交時間戳數據的子目錄。 9.5版本以後

pg_clog/ (Version 9.6 or earlier)

包含事務提交狀態數據的子目錄。它在版本10中重命名爲pg_xact.  CLOG將在5.4章節中詳解。

.

pg_dynshmem/

包含動態共享內存子系統使用的文件的子目錄。9.4版本以後

pg_logical/

包含邏輯解碼的狀態數據的子目錄。9.4版本以後

pg_multixact/

包含多事務狀態數據的子目錄(用於 shared row locks)

pg_notify/

包含LISTEN / NOTIFY狀態數據的子目錄

pg_repslot/

包含複製槽數據的子目錄(9.1版本以後)

pg_serial/

包含有關已提交的序列化事務(9.1版本以後)信息的子目錄

pg_snapshots/

包含導出快照的子目錄(9.2版本以後)。 PostgreSQL的函數pg_export_snapshot在此子目錄中創建快照信息文件

pg_stat/

包含統計子系統永久文件的子目錄

pg_stat_tmp/

包含統計子系統臨時文件的子目錄

pg_subtrans/

包含子事物狀態數據的子目錄

pg_tblspc/

表空間符號鏈接目錄

pg_twophase/

包含prepare事務的狀態文件

pg_wal/ (Version 10 or later)

包含WAL(Write Ahead Logging)段文件的子目錄。在版本10中從pg_xlog重命名而來

.

pg_xact/ (Version 10 or later)

包含事務提交狀態數據的子目錄。在版本10中從pg_clog重命名而來.CLOG將在5.4章節中詳解

pg_xlog/ (Version 9.6 or earlier)

包含WAL(Write Ahead Logging)段文件的子目錄。在版本10中重命名爲pg_wal

1.2.2. database的物理佈局設計

每個數據庫都會在$PGDATA/base下面生成一個子目錄,如下圖,都會一一對應。子目錄名稱與數據庫的OID一致。例如,數據庫sampledb 的oid爲16384,其對應的子目錄名稱爲16384。

1.2.3. 表和索引的物理佈局設計

每一個表和索引如果不超過1G大小,都只有一個文件。表和索引也有和數據庫一樣的OID,另外還有一個relfilenode,這個值不會總是匹配OID,在發生一truncate,reindex,cluster等相關的操作,會發生變化,見如下示例:

可以看到開始oid和relfilenode是一樣的,truncate後,relfilenode發生了變化。

如果數據數據文件超過1GB,那麼就會新生成一個文件,如下:

注意:表和索引的文件大小的限制可以在編譯的時候通過--with-segsize設置

觀察數據庫子目錄發現,每一個表都包含oid_fsm和oid__vm命名的文件。其中oid_fsm對應於free space map,存儲數據表文件中每個頁的剩餘空間,詳見Section 5.3.4 。oid__vm對應於visibility map,存儲數據表文件中每個頁中各tuple的可見性信息,詳見Section 6.2

hey may also be internally referred to as the forks of each relation; the free space map is the first fork of the table/index data file (the fork number is 1), the visibility map the second fork of the table's data file (the fork number is 2). The fork number of the data file is 0.

這裏需要提一下每個關係表中的forknum的含義。在表或索引文件中, free space map(fsm文件)是第一個fork,其forknum爲1。visibility map(vm文件)是第二個fork,其forknum爲2。數據文件的forknum爲0。

1.2.4. Tablespaces

在PG中,除了base目錄,自己新建的tablespace對應的目錄都會再pg_tblspc下,如下圖 

                                                                    圖. 1.3. Tablespace 物理佈局

當用戶執行CREATE TABLESPACE語句並設置tablespace目錄,pg將會在指定目錄下創建version-specific子目錄,格式爲版本號_子目錄(如PG_9.4_201409291),命名方法如下:

例如,當用戶在 '/home/postgres/tblspc'目錄下創建一個名稱爲'new_tblspc'的tablespace時,生成的tablespace oid爲16386,pg將在tablespace對應目錄下( '/home/postgres/tblspc',)創建子目錄'PG_9.4_201409291' 

在base目錄pg_tblspc子目錄下,生成16386的符號鏈接,指向“tablespace oid爲16386的'new_tblspc'對應的目錄,即'/home/postgres/tblspc'。

如果用戶在一個已經創建的數據庫(sampledb)中,創建新表,將表的tablespace設置爲'new_tblspc',那麼pg將在'new_tblspc'對應的version specific子目錄下創建一個新的目錄,該目錄名稱爲sampledb的oid(16384),同時,在此目錄下,創建一個表文件,以表oid命名。

1.3. 堆表文件(heap table file)的內部結構

在表對應的datafile中,被分離爲固定大小的page(or block),默認爲8KB,這些page在datafile中從0開始計數,如果一個page被填充滿,那麼就會生成新的page以添加到文件,所以我們看到的datafile會隨着表的增大,也在不斷增大。

一個堆表數據文件的內部結構設計如下圖:

  

一個表的page包括三種類型的數據

1.heap tuple: 存放數據本身,從一個page的末端有序的堆積。

2.line pointer: 一個四字節的行指針,指向每一個heap tuple,也叫item pointer,line pointer是一個簡單的數組,索引page的數據文件是從1開始計數,也叫offset number,新的tuple增加到page時,line piniter就在推送到數組中,指向新的tuple。

3.header data:  header data是page生成的時候隨之產生的,由pageHeaderData定義結構,24個字節長,包含了page的一般信息,主要結構描述如下:

  • pd_lsn: 存儲XLOG最後的改變的這個page的LSN號,是一個8字節的無符號整數,和WAL相關,後續章節會有描述

  • pd_checksum:存儲page的校驗和

  • pd_lower,pd_upper:  pd_lower指向line pointer的尾部,pd_upper指向最新heap tuple的開頭

  • pd_special: 此變量用於索引,在表的page中,它指向page的末尾(在索引的page中,它指向特殊空間的開頭)

1.4. 從表中讀寫tuple的方法

1.4.1. 寫Heap Tuples

假設一個數據表只有一個page,這個page裏只有一個tuple,page中的pd_lower指向第一個tuple的指針(first line pointer),同時第一個tuple的指針(first line pointer)和pd_upper都指向第一個heap tuple,如圖1.5(a)。

當tuple2插入後,2號line pointer指向tuple2的頭部,pd_lower指向了2號line pointer的末尾,pd_upper指向了tuple2的頭部,如圖1.5(b),其他的數據(pg_lsn,pg_checksum, pg_flags等等)也會適當的被重寫。第5.3節第9章會詳細介紹。                                                                              圖1.5 寫heap tuple

1.4.2. 讀Heap Tuples

有兩種方法可以讀取Heap Tuples,分別是順序掃描和B-tree索引掃描:

順序掃描(Sequential scan) –表中的所有page中的所有tuple通過每個page中的所有line pointer依次讀取,如圖1.6(a)。

B-tree索引掃描(B-tree index scan) –每個索引文件都包含index tuple,每個index tuple都是由索引鍵和一個指向目標heap tuple的point構成的TID所構成,如果索引的鍵值被找到,那麼就從index tuple中獲取TID的值去找想要的數據。如以下示例:通過索引的鍵值Queen,在index tuple中找到對應的TID(block=7,Offset=2),這裏的意思就是第七個page的第二個tuple.因此PG不需要在page中進行沒有必要的掃描,如圖1.6(b)。

                                                     圖1.6 順序掃描和索引掃描

關於PG中索引的原理,推薦閱讀以下內容:

Indexes in PostgreSQL — 1 

Indexes in PostgreSQL — 2 

Indexes in PostgreSQL — 3 (Hash) 

Indexes in PostgreSQL — 4 (Btree) 

Indexes in PostgreSQL — 5 (GiST) 

Indexes in PostgreSQL — 6 (SP-GiST) 

Indexes in PostgreSQL — 7 (GIN) 

Indexes in PostgreSQL — 9 (BRIN) 

PostgreSQL同樣支持TID-Scan, Bitmap-Scan,和Index-Only-Scan。

TID-Scan是一種通過使用待獲取tuple 的TID直接訪問tuple 的方法。例如,要在表中找到第0個塊中的第一個tuple ,請發出以下查詢:

Index-Only-Scan詳細內容見第7章

譯自:

http://www.interdb.jp/pg/pgsql01.html

 

參考:

https://blog.csdn.net/dazuiba008/article/details/80363430

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章