PG系列5:PG體系結構

一. PG體系結構

1.1 PG的體系結構概述

PostgreSQL的主要結構如下:


image.png

1.2 PostgreSQL進程概述

PostgreSQL進程結構圖譜和分類:


image.png

二. PG內存結構

image.png

內存結構分為共享內存、本地內存。類似于Oracle的SGA和PGA。

共享內存
是指數(shù)據(jù)庫服務器向操作系統(tǒng)申請的共享內存段,如數(shù)據(jù)共享緩沖區(qū)、日志緩沖區(qū)、事務提交日志內存區(qū)等,提供給PostgreSQL服務器的所有進程使用。

數(shù)據(jù)共享緩沖區(qū):PostgreSQL把要操作和處理的表、index,讀入到內存中,放到該區(qū)域緩存。類似于Oracle的database buffer cache。其大小由shared_buffers參數(shù)決定。

日志緩沖區(qū):用于緩存數(shù)據(jù)庫中對數(shù)據(jù)修改的日志記錄,如:update table test set id=1這條SQL語句,數(shù)據(jù)庫會把這個操作的信息記錄在該內存區(qū),將來寫出到日志文件中,如果配置為歸檔模式,則最終寫出到歸檔日志文件中去,用于恢復使用。其大小由wal_buffers參數(shù)決定。類似于Oracle的log buffer。

提交日志緩沖區(qū):該內存區(qū)域有別于wal buffer日志緩沖區(qū)。它用于記錄數(shù)據(jù)庫中所有事務的提交狀態(tài),事務是否已經(jīng)提交,是否已經(jīng)終止,是否進行中,子事務等狀態(tài)信息。用于MVCC。

本地內存
當我們和數(shù)據(jù)庫建立一個連接請求時,數(shù)據(jù)庫幫我們創(chuàng)建1個后端進程。并給該后端進程分配的內存區(qū)域,該內存區(qū)域只屬于這一個后端進程使用,可以認為是私有的。用于處理和響應我們向數(shù)據(jù)庫發(fā)起的請求操作。通常包含:工作區(qū)work mem、維護工作區(qū)、臨時緩沖區(qū)。

工作區(qū):該內存區(qū)用于處理客戶端SQL語句請求的order by排序、distinct過濾、表合并連接merge-join、哈希連接hash-join操作等。由work_mem參數(shù)決定大小。

維護工作區(qū):該內存區(qū)域用于處理重建索引reindex、vacuum空間回收操作、給表添加外鍵約束等。由maintenance_work_mem參數(shù)決定大小。

臨時緩沖區(qū):該內存區(qū)用于創(chuàng)建和訪問臨時表時,存放臨時表的數(shù)據(jù)。該內存區(qū)和因為SQL中因為大表排序或hash table而在服務器上建立的臨時文件(位于pgsql_tmp路徑下)沒有直接關系。由temp_buffers參數(shù)決定大小。

三. PostgreSQL進程

PostgreSQL數(shù)據(jù)庫的進程可以分為三類:后臺進程、后端進程或叫服務器進程、客戶端進程或用戶進程。

3.1 后臺進程

image.png

此處我的測試庫因為有些功能還未安裝,有些進程不存在。

  1. /usr/pgsql-15/bin/postmaster -D /var/lib/pgsql/15/data/是數(shù)據(jù)庫服務器的master進程,其它諸如checkpoint,background writer,walwrite,autovacuum launcher,stats collector,logical replication launcher都是由它fork的子進程。當然,數(shù)據(jù)庫運行模式不同,配置不同,也可能有其它后臺進程,如歸檔進程等。

  2. postgres:PostgreSQL數(shù)據(jù)庫的核心進程,也是整個cluster的父進程,該進程出現(xiàn)問題,整個cluster就over了。該進程由操作系統(tǒng)的守護進程1號進程派生。Linux下的1號進程是整個服務器的守護進程,類比于Java程序中的Object類,一切類的父類。

  3. checkpointer:檢查點進程,等價于Oracle的CKPT進程,負責完成數(shù)據(jù)庫的檢查點,通知數(shù)據(jù)庫的寫進程DBWR將內存中的臟數(shù)據(jù)寫出到磁盤。

  4. background writer :等價于Oracle的DBWR進程,負責將內存中的臟數(shù)據(jù)寫出到磁盤。

  5. walwriter :等價于Oracle的LGWR進程,負責將日志緩沖區(qū)中的記錄關于數(shù)據(jù)庫的修改的日志寫出到日志文件中去,確保數(shù)據(jù)的修改不會丟失,用于恢復使用。

  6. autovacuum launcher:自動清理工作進程。由于PostgreSQL不像Oracle那樣有undo的機制,將數(shù)據(jù)被修改前的信息寫入到undo,然后修改數(shù)據(jù)。PostgreSQL采取的是在原數(shù)據(jù)塊上進行保留舊的數(shù)據(jù),并作標記,等到將來修改提交生效之后,舊的數(shù)據(jù)(dead tuple翻譯為死元組)不需要的話,就得清理,由該進程來完成。

  7. stats collector:統(tǒng)計信息收集進程。用于及時的更新數(shù)據(jù)庫中的統(tǒng)計信息,如表、index有多少條記錄,數(shù)據(jù)分布等,給優(yōu)化器提供最新的信息,便于優(yōu)化器選擇最優(yōu)的執(zhí)行計劃。避免統(tǒng)計信息不準確,導致優(yōu)化器選擇錯誤的執(zhí)行計劃,導致SQL性能下降或偏差。

  8. logical replication launcher:邏輯復制進程。用于完成邏輯復制的工作。

  9. archiver:歸檔進程,等價于Oracle的ARCH進程,用于完成數(shù)據(jù)庫日志文件的歸檔。當數(shù)據(jù)庫配置了歸檔模式之后,可以看到該進程。

3.2 后端進程(backend)或服務器進程

當我們的應用程序和圖形界面的客戶端工具,連接到PostgreSQL數(shù)據(jù)庫服務器時。master進程會為該應用程序創(chuàng)建1個服務器進程,用于處理和響應該客戶端應用程序的請求。

image.png
image.png

如上,可以看到服務器進程號是18499,通過select pg_backend_pid()查詢。同時,看到服務器上該進程的父進程是1164,由 /usr/pgsql-15/bin/postmaster -D /var/lib/pgsql/15/data/這個主進程派生。

后端進程或服務器進程的數(shù)量由max_connections參數(shù)決定。

每一個后端進程一次只能訪問一個數(shù)據(jù)庫。它和客戶端進程進行TCP通信,開戶端斷開之后,該進程自動回收消失。客戶端重新連接或發(fā)起新連接時重新創(chuàng)建新的后端進程。

由于進程的創(chuàng)建或回收,比較消耗操作系統(tǒng)的資源,因此,多數(shù)情況下,應用系統(tǒng)都會通過連接池的方式和數(shù)據(jù)庫建立連接。

從PostgreSQL官方給后端進程的命名可以看到還是一脈相承的,比如,我們查看當前會話所在的后端進程號或者叫服務器進程的時候,我們調用的是pg_backend_pid()函數(shù),殺會話所在進程時,調用的是pg_terminate_backend(),或者pg_cancel_backend()。

3.3 用戶進程或客戶端進程

指的是連接數(shù)據(jù)庫服務器的應用程序或者客戶端工具等。


image.png

每個用戶進程或者客戶端進程對應一個服務端進程。


image.png

這2張圖引自《PostgreSQL for DBA Architects》p21-22。

3.4 數(shù)據(jù)庫服務器啟動流程

當我們通過pg_ctl工具來啟動PostgreSQL數(shù)據(jù)庫時,先在操作系統(tǒng)上創(chuàng)建1個master進程,然后該進程派生出一系列的后臺進程,同時該進程監(jiān)聽$PGDATA/postgresql.conf配置文件中指定的端口。并且,向操作系統(tǒng)申請內存,用于數(shù)據(jù)庫的正常運行操作,處理客戶端的連接請求操作處理。最后,數(shù)據(jù)庫可以正常對外提供服務。

image.png

四. PG邏輯結構

PG邏輯結構圖解:

image.png

image.png

4.1 PostgreSQL cluster

當我們在一臺服務器上安裝部署并且初始化一個PostgreSQL數(shù)據(jù)庫之后,嚴格的講,其實是我們安裝部署了一套PostgreSQL數(shù)據(jù)庫軟件,然后初始化了一個PostgreSQL的database cluster。這里的cluster是什么概念呢?

首先,這里的cluster完全是個邏輯上的概念,它是指一系列的數(shù)據(jù)庫的集合。它所包含的數(shù)據(jù)庫就是指,當我們以postmaster -D /var/lib/pgsql/15/data/ start來啟動數(shù)據(jù)庫cluster時,由這個-D參數(shù)指定,或者是PGDATA環(huán)境變量指定的路徑下的所有的數(shù)據(jù)庫的集合。當然,這么說太抽象了,其實,從物理上,我們可以這么說明和解釋:

[root@sr-fe ~]# su - postgres
上一次登錄:日 6月 25 10:10:40 CST 2023pts/2 上
-bash-4.2$ 
-bash-4.2$ env | grep PGDATA
PGDATA=/var/lib/pgsql/15/data
-bash-4.2$ 
-bash-4.2$ 
-bash-4.2$ ll /var/lib/pgsql/15/data
total 68
drwx------ 5 postgres postgres    33 Jun  9 19:17 base
-rw------- 1 postgres postgres    30 Jun 25 00:00 current_logfiles
drwx------ 2 postgres postgres  4096 Jun 25 10:10 global
drwx------ 2 postgres postgres   188 Jun 15 00:00 log
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_commit_ts
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_dynshmem
-rw------- 1 postgres postgres  4574 Jun 12 10:28 pg_hba.conf
-rw------- 1 postgres postgres  1636 Jun  9 19:17 pg_ident.conf
drwx------ 4 postgres postgres    68 Jun 22 22:16 pg_logical
drwx------ 4 postgres postgres    36 Jun  9 19:17 pg_multixact
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_notify
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_replslot
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_serial
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_snapshots
drwx------ 2 postgres postgres     6 Jun 12 10:28 pg_stat
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_stat_tmp
drwx------ 2 postgres postgres    18 Jun  9 19:17 pg_subtrans
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_tblspc
drwx------ 2 postgres postgres     6 Jun  9 19:17 pg_twophase
-rw------- 1 postgres postgres     3 Jun  9 19:17 PG_VERSION
drwx------ 3 postgres postgres    60 Jun  9 19:17 pg_wal
drwx------ 2 postgres postgres    18 Jun  9 19:17 pg_xact
-rw------- 1 postgres postgres    88 Jun  9 19:17 postgresql.auto.conf
-rw------- 1 postgres postgres 29451 Jun 12 10:25 postgresql.conf
-rw------- 1 postgres postgres    58 Jun 22 22:16 postmaster.opts
-rw------- 1 postgres postgres    95 Jun 22 22:16 postmaster.pid
-bash-4.2$ 
-bash-4.2$ oid2name
All databases:
  Oid  Database Name  Tablespace
--------------------------------
    5       postgres  pg_default
    4      template0  pg_default
    1      template1  pg_default
-bash-4.2$ 
-bash-4.2$ psql
psql (9.2.24, server 15.3)
WARNING: psql version 9.2, server version 15.0.
         Some psql features might not work.
Type "help" for help.

postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(3 rows)

postgres=# select oid,datname from pg_database ;
 oid |  datname  
-----+-----------
   5 | postgres
   1 | template1
   4 | template0
(3 rows)

postgres=# 

4.2 database和cluster的關系

數(shù)據(jù)庫是指一些列數(shù)據(jù)庫對象的集合,比如表,index,view,function等這些數(shù)據(jù)庫對象隸屬于一個特定的數(shù)據(jù)庫。

cluster指的是一些列數(shù)據(jù)庫的集合。比如:一個cluster初始化之后,包含3個默認數(shù)據(jù)庫:postgres,默認的管理數(shù)據(jù)庫;template0,默認的不可修改的空數(shù)據(jù)庫;template1,默認的模板數(shù)據(jù)庫,當我們創(chuàng)建數(shù)據(jù)庫時,會參照該數(shù)據(jù)庫來創(chuàng)建。

當我們在template1模板數(shù)據(jù)庫中創(chuàng)建和安裝1個數(shù)據(jù)庫插件extension,uuid之后,再去創(chuàng)建新的數(shù)據(jù)庫,那么新數(shù)據(jù)庫中就會自動包含該uuid這個extension。

1個cluster可以包含多個數(shù)據(jù)庫,反過來1個database只能隸屬于1個cluster。

image.png

4.3 數(shù)據(jù)庫實例和cluster的關系

我們定義數(shù)據(jù)庫實例是指一堆PostgreSQL的后臺進程和內存結構,cluster指的是我們在初始化數(shù)據(jù)庫時,指定的PGDATA環(huán)境變量指向的操作系統(tǒng)上的那個路徑下的一堆的文件。

一個數(shù)據(jù)庫實例在其一個生命周期內(從啟動到關閉)只能“掛載”一個數(shù)據(jù)庫cluster,反之,一個cluster也只能被一個實例掛載訪問。二者之間是嚴格的一對一關系。

但是,在一臺服務器上,我們可以安裝一套PostgreSQL數(shù)據(jù)庫軟件,用這個數(shù)據(jù)庫軟件可以創(chuàng)建多個實例和多個cluster。每個實例對應于一個cluster。只要每個cluster所指定的監(jiān)聽端口不同,我們就可以同時運行多個實例和cluster。

如:我們可以在這套環(huán)境上再初始化一個實例和cluster,將其指向另外一個監(jiān)聽端口即可。

4.4 PostgreSQL的tablespace

定義:
tablespace依然是一個邏輯概念,它是隸屬于cluster的。

查看:
通過pg_tablespace字典表來查看cluster下表空間的信息,或者是\db命令也可查看表空間的信息;

image.png

默認表空間:
每個cluster默認情況下有2個表空間,分別命名為pg_default用于存放各個數(shù)據(jù)庫私有的數(shù)據(jù)庫對象,pg_global用于存放cluster全局共享的數(shù)據(jù)庫對象信息,例如:cluster中數(shù)據(jù)庫本身的信息,表空間的信息,數(shù)據(jù)庫訂閱信息,數(shù)據(jù)庫復制信息,數(shù)據(jù)庫認證授權信息、控制文件等。

專用表空間:
pg_global表空間是專表空間專用的,只能存放全局共享的數(shù)據(jù)庫對象,不能存放用戶數(shù)據(jù),否則報錯:

pg_default,表空間,默認存放cluster下所有數(shù)據(jù)庫的所有數(shù)據(jù)庫對象。這一點兒上,倒是有點兒類似于Oracle數(shù)據(jù)庫的專表空間專用。

表空間和數(shù)據(jù)庫的關系:一個表空間可以給多個數(shù)據(jù)庫使用,一個數(shù)據(jù)庫里的不同數(shù)據(jù)庫對象也可以存放在不同的表空間下。表空間和數(shù)據(jù)庫的關系,不嚴格的講,可以說是多對多的關系。不像Oracle數(shù)據(jù)庫中,一個數(shù)據(jù)庫可以包含多個表空間,且每個表空間只能屬于一個數(shù)據(jù)庫使用。
如何創(chuàng)建表空間:OS上路徑需提前創(chuàng)建,postgres用戶得有讀寫操作系統(tǒng)文件系統(tǒng)權限,數(shù)據(jù)庫管理員權限。

-- 不要把表空間目錄放在data目錄下
-bash-4.2$ psql
psql (9.2.24, server 15.3)
WARNING: psql version 9.2, server version 15.0.
         Some psql features might not work.
Type "help" for help.

postgres=# 
postgres=# create tablespace newtbs location '/var/lib/pgsql/15/data/tbs_dir';
WARNING:  tablespace location should not be inside the data directory
CREATE TABLESPACE
postgres=# 

創(chuàng)建庫表時指定表空間:

create database newdb tablespace newtbs;
create table test_tbs(id int) tablespace newtbs ;

修改表空間:
數(shù)據(jù)庫創(chuàng)建之后,或者數(shù)據(jù)庫對象(表、index等)創(chuàng)建之后,也可以分別通過alter database/table/index來修改表空間信息。

表空間作用:
主要用于邏輯上隔離數(shù)據(jù)庫對象,或者用于數(shù)據(jù)庫存儲空間規(guī)劃或遷移存儲。想要通過表空間的設置,進而對于數(shù)據(jù)庫性能提升?作用不大,畢竟現(xiàn)在基本上都是直接上SSD(Solid State Drive )存儲給數(shù)據(jù)庫使用。

4.5 Sgment

一個段是分配給一個邏輯結構(一個表、一個索引或其他對象)的一組區(qū),是數(shù)據(jù)庫對象使用的空間的集合;段可以有表段、索引段、回滾段、臨時段和高速緩存段等。

4.6 Extent

區(qū)是數(shù)據(jù)庫存儲空間分配的一個邏輯單位,它由連續(xù)數(shù)據(jù)塊所組成。第一個段是由一個或多個盤區(qū)組成。當一段中間所有空間已完全使用,PostgreSQL為該段分配一個新的范圍。

4.7 Block

數(shù)據(jù)塊是PostgreSQL 管理數(shù)據(jù)文件中存儲空間的單位,為數(shù)據(jù)庫使用的I/O的最小單位,是最小的邏輯部件。默認值8K。

五. PG物理結構

5.1 cluster的物理結構圖譜

image.png

5.2 cluster在文件系統(tǒng)上的結構

目錄文件概述:

base                --存放默認數(shù)據(jù)庫的目錄
global              --存放的數(shù)據(jù)庫相關的字典視圖或者表文件        
pg_commit_ts            --事務存放的提交的時間戳數(shù)據(jù)
pg_dynshmem     --動態(tài)內存分配存放的空間(dynamic share memeory)
pg_hba.conf         --基于主機的配置文件
pg_ident.conf           --基于對等認證的配置文件
pg_logical          --存儲數(shù)據(jù)庫內部狀態(tài)的邏輯解碼數(shù)據(jù)
pg_multixact            --存放多事務狀態(tài)的數(shù)據(jù)
pg_notify               --消息通知目錄(LISTEN狀態(tài)目錄)
pg_replslot         --存放復制槽的數(shù)據(jù)
pg_serial               --提交的可串行化事務的狀態(tài)數(shù)據(jù)
pg_snapshots            --執(zhí)行導出快照函數(shù)時的狀態(tài)信息數(shù)據(jù)
pg_stat                 --統(tǒng)計信息目錄
pg_stat_tmp             --臨時統(tǒng)計信息目錄
pg_subtrans         --子事務目錄
pg_tblspc            --表空間映射目錄
pg_twophase         --兩階段提交狀態(tài)的數(shù)據(jù)
PG_VERSION          --存放主版本編號的文件
pg_wal              --存儲 WAL 文件的目錄
pg_xact                 --事務提交的狀態(tài)數(shù)據(jù)
postgresql.auto.conf --存儲通過 ALTER SYSTEM 命令修改的參數(shù)文件(可以手動修改)
postgresql.conf       --數(shù)據(jù)庫的參數(shù)配置文件
postmaster.opts       --上一次數(shù)據(jù)庫啟動狀態(tài)的命令
postmaster.pid        --存放當前數(shù)據(jù)庫的主進程編號及相關目錄及端口的信息
image.png

數(shù)據(jù)文件:
$PGDATA/base:用于存放當前cluster下所有的數(shù)據(jù)庫,數(shù)字化命名的路徑表示各個數(shù)據(jù)庫,每個數(shù)字表示數(shù)據(jù)庫的oid。一些創(chuàng)建數(shù)據(jù)庫時指定了表空間的庫會存在表空間目錄下,而不是這個目錄。

image.png

關于文件名中類似如1213_fsm、1213_vm的文件,表示空閑空間映射文件(free space map),可見性映射文件(visibility map)。


image.png

表空間:
global:用于存放cluster級別共享的全局表,如pg_database,pg_tablespace表,其文件命名依舊采用oid的數(shù)字化格式。

image.png

我們可以從數(shù)據(jù)庫中驗證這些cluster級別的共享系統(tǒng)表的oid和global下的數(shù)字文件名匹配:
image.png

5.3 測試一個表空間及表的例子

代碼:

create tablespace testtbs location '/var/lib/pgsql/newtbs';
create database newdb tablespace testtbs;
\c newdb;
create table test_tbs(id bigint) tablespace newtbs ;
insert into test_tbs select * from generate_series(1,1000000000);

測試記錄:

postgres=# create tablespace testtbs location '/var/lib/pgsql/newtbs';
CREATE TABLESPACE
postgres=# create database newdb tablespace testtbs;
CREATE DATABASE
postgres=# 
postgres=# 
postgres=# \c newdb;
psql (9.2.24, server 15.3)
WARNING: psql version 9.2, server version 15.0.
         Some psql features might not work.
You are now connected to database "newdb" as user "postgres".
newdb=# create table test_tbs(id bigint) tablespace newtbs ;
CREATE TABLE
newdb=# insert into test_tbs select * from generate_series(1,1000000000);

INSERT 0 1000000000
newdb=# 
image.png

表的數(shù)據(jù)達到1G后會分割文件,所以會有這么多的文件

表大小達34GB

SELECT pg_size_pretty(pg_total_relation_size('test_tbs'));
image.png
SELECT datname, oid as database_id FROM pg_database WHERE datname = 'newdb';

SELECT nspname, oid as schema_id FROM pg_namespace WHERE nspname = 'public';

SELECT relname, oid as table_id FROM pg_class WHERE relname = 'test_tbs';

SELECT pg_relation_filepath('public.test_tbs');
image.png

參考:

  1. https://github.com/digoal/blog
  2. https://blog.csdn.net/m0_50880099/article/details/124431279
  3. http://www.taodudu.cc/news/show-1287235.html?action=onClick
  4. https://www.modb.pro/mes/1820
  5. https://www.modb.pro/db/389130
  6. http://www.knockatdatabase.com/2021/04/01/postgresql-architecture/#1
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容