是最先進的數據庫。他的第一個版本在1989年發布,從那時開始,他得到了很多擴展。根據db-enginers上的排名情況,PostgreSQL目前在數據庫領域排名第四。
本篇博客,我們來討論一下PostgreSQL的內部架構,以及各個組件之間如何交互。這將是本期PostgreSQL DBA系列博客的基石。
一、PostgreSQL的架構
PostgreSQL的物理架構非常簡單,它由共享內存、一系列后台進程和數據文件組成。 (如下圖)
二、Shared Memory
共享內存是服務器服務器為數據庫緩存和事務日志緩存預留的內存緩存空間。其中最重要的組成部分是Shared Buffer和WAL Buffer。
Shared Buffer
Shared Buffer的目的是減少磁盤IO。為了達到這個目的,必須滿足以下規則:
- 當需要快速訪問非常大的緩存時(10G、100G等)
- 如果有很多用戶同時使用緩存,需要將內容盡量縮小
- 頻繁訪問的磁盤塊必須長期放在緩存中
WAL Buffer
WAL Buffer是用來臨時存儲數據庫變化的緩存區域。存儲在WAL Buffer中的內容會根據提前定義好的時間點參數要求寫入到磁盤的WAL文件中。在備份和恢復的場景下,WAL Buffer和WAL文件是極其重要的。
三、PostgreSQL 進程類型
PostgreSQL有四種進程類型
- Postmaster (Daemon) Process(主后台駐留進程)
- Background Process(后台進程)
- Backend Process(后端進程)
- Client Process(客戶端進程)
Postmaster Process
主后台駐留進程是PostgreSQL啟動時第一個啟動的進程。啟動時,他會執行恢復、初始化共享內存愛你的運行后台進程操作。正常服役期間,當有客戶端發起鏈接請求時,它還負責創建后端進程。
如果通過pstree命令查看進程之間的關系,你會發現Postmaster進程是其他所有進程的父進程。
Background Process
PostgreSQL操作需要的后台進程列表如下:
進程 | 作用 |
---|---|
logger | 將錯誤信息寫到log日志中 |
checkpointer | 當檢查點出現時,將臟內存塊寫到數據文件 |
writer | 周期性的將臟內存塊寫入文件 |
wal writer | 將WAL緩存寫入WAL文件 |
Autovacuum launcher | 當自動vacuum被啟用時,用來派生autovacuum工作進程。autovacuum進程的作用是在需要時自動對膨脹表執行vacuum操作。 |
archiver | 在歸檔模式下時,復制WAL文件到特定的路徑下。 |
stats collector | 用來收集數據庫統計信息,例如會話執行信息統計(使用pg_stat_activity視圖)和表使用信息統計(pg_stat_all_tables視圖) |
Backend Process
最大后台鏈接數通過max_connections參數設定,默認值為100。后端進程用於處理前端用戶請求並返回結果。查詢運行時需要一些內存結構,就是所謂的本地內存(local memory)。本地內存涉及的主要參數有:
- work_mem:用於排序、位圖索引、哈希鏈接和合並鏈接操作。默認值為4MB。
- maintenance_work_mem:用於vacuum和創建索引操作。默認值為64MB。
- temp_buffers:用於臨時表。默認值為8MB。
Client Process
客戶端進程需要和后端進程配合使用,處理每一個客戶鏈接。通常情況下,Postmaster進程會派生一個紫禁城用來處理用戶鏈接。
四、數據庫結構
想要理解PostgreSQL的數據庫結構,需要先了解一些重要的概念。
數據庫相關概念:
- PostgreSQL由一系列數據庫組成。一套PostgreSQL程序稱之為一個數據庫群集。
- 當initdb()命令執行后,template0 , template1 , 和postgres數據庫被創建。
- template0和template1數據庫是創建用戶數據庫時使用的模版數據庫,他們包含系統元數據表。
- initdb()剛完成后,template0和template1數據庫中的表是一樣的。但是template1數據庫可以根據用戶需要創建對象。
- 用戶數據庫是通過克隆template1數據庫來創建的;
表空間相關概念:
- initdb()后馬上創建pg_default和pg_global表空間。
- 建表時如果沒有指定特定的表空間,表默認被存在pg_default表空間中。
- 用於管理整個數據庫集群的表默認被存儲在pg_global表空間中。
- pg_default表空間的物理位置為$PGDATA\base目錄。
- pg_global表空間的物理位置為$PGDATA\global目錄。
- 一個表空間可以被多個數據庫同時使用。此時,每一個數據庫都會在表空間路徑下創建為一個新的子路徑。
- 創建一個用戶表空間會在$PGDATA\pg_tblspc目錄下面創建一個軟連接,連接到表空間制定的目錄位置。
表相關概念:
- 每個表有三個數據文件。
- 一個文件用於存儲數據,文件名是表的OID。
- 一個文件用於管理表的空閑空間,文件名是OID_fsm。
- 一個文件用於管理表的塊是否可見,文件名是OID_vm。
- 索引沒有_vm文件,只有OID和OID_fsm兩個文件
其他需要注意的地方
表和索引創建時文件名是OID,此時的OID和pg_class.relfilenode的值是一樣的。不管怎樣,當我們執行重寫操作時(truncate,cluster,vacuum full,reindex等),被修改對象的relfilenode值也會被修改,文件名也會隨着reffilenode值一起改變。我們可以通過pg_relation_filepath('<object_name>')視圖很容易的檢查文件位置和名稱。
五、運行測試
initdb()完成后,如果登錄數據庫查詢視圖pg_database,我們可以看到template0 , template1和 postgres數據庫已經被創建好了。
- 通過datistemplate列,我們可以看到template0和template1是用戶創建數據庫時使用的模版數據庫,其他的都不是模版數據庫。
- 通過datlowconn列,可以看出該數據庫是否允許訪問。因為template0數據庫不能被訪問,所以該數據庫的內容不能被修改。
- 設置兩個模版數據庫的原因是template0是初始狀態,而template1數據庫則是可以集成用戶某些需求的模版數據庫。
- postgres數據庫時使用模版template1創建的默認數據庫,如果鏈接時不指定數據庫名稱,默認連接到postgres數據庫。
- 數據庫存儲在$PGDATA\base目錄下。路徑的名稱和數據庫OID的名稱一致。
六、創建用戶數據庫
上文提過,用戶數據庫創建是通過克隆template1數據庫。為了驗證這個規則,我們現在template1中創建一個表t1,緊接着創建一個mydb01數據庫,檢查t1表是否在mydb01中存在。
pg_default tablespace
initdb()后,如果登錄數據庫查詢pg_tablespace視圖,會發現pg_global和pg_default表空間已經創建好。
pg_default表空間的位置為$PGDATA\base。每一個數據庫都擁有一個以自己OID命令的子路徑。
pg_global表空間
pg_global表空間用於存儲集群級別的數據。
- 例如pg_開頭的表
- pg_global表空間路徑為$PGDATA\global.
七、創建用戶表空間
pg_tablespace視圖顯示myts01表空間已經被創建好。
$PGDATA/pg_tblspc路徑下有一個符號鏈接指到目標目錄。
下面分別連接到postgres和mydb01數據庫,創建表。
如果查看/data01路徑下的內容,會發現上面創建的兩個數據庫中的t1表,分別在下面有一個對應OID的文件夾存在。
修改表空間位置
PostgreSQL在創建表空間時指定一個特定的路徑。因此,如果該特定路徑已經滿了,數據就不能在向里面存儲了。為了解決該問題,我們可以使用磁盤管理程序擴展空間。但是如果不想使用磁盤管理程序,我們可以通過該表表空間的位置來解決該問題。命令如下:
八、Vacuum是什么?
vacuum執行如下操作:
- 收集表和索引的統計信息
- 整理表
- 清理表和索引中的死亡塊
- 被XID凍結,防止XID回繞
#1 和 #2 是數據庫管理需要的。#3 和 #4 PostgreSQL MVCC 特性的要求。
九、PostgreSQL和Oracle MySQL之間的區別
二者之間最大的不同是MVCC模型和共享池(shared pool)。
指標 | ORACLE | PostgreSQL |
---|---|---|
MVCC模型 | UNDO | Store previous |
實現方法 | Segment | record within block |
共享池 | exists | it does not exist |
MVCC模型的區別
為了增加並發,必須遵循“讀操作不阻塞寫操作,寫操作不阻塞讀操作”的原則。為了實現這個原則,多版本並發控制(MVCC)理論被引入。Oracle使用UNDO段實現MVCC。而PostgreSQL存儲之前的記錄在數據塊中,它通過事務XID和事務的xmin、xmax來控制事務版本。
Shared Pool
PostgreSQL不提供共享池。這對於熟悉Oracle的用戶來說有點尷尬。共享池是Oracle中最基本和最重要的組件。PostgreSQL在進程級別提供SQL信息的共享能力,而不是共享池。換句話說,如果我們在同一個進程中多次執行相同的SQL,它只會硬解析一次。