PostgreSQL是最像Oracle的開源數據庫,我們可以拿Oracle來比較學習它的體系結構,比較容易理解。PostgreSQL的主要結構如下:
一、存儲結構
PG數據存儲結構分為:邏輯存儲結構和物理存儲存儲。其中:邏輯存儲結構是內部的組織和管理數據的方式;物理存儲結構是操作系統中組織和管理數據的方式。
1、邏輯存儲結構
所有數據庫對象都有各自的oid(object identifiers),oid是一個無符號的四字節整數,相關對象的oid都存放在相關的system catalog表中,比如數據庫的oid和表的oid分別存放在pg_database,pg_class表中。
在邏輯存儲結構中有幾個術語需要解釋:
- 數據庫集群-Database cluster
也叫數據庫集簇。它是指有單個PostgreSQL服務器實例管理的數據庫集合,組成數據庫集群的這些數據庫使用相同的全局配置文件和監聽端口、共用進程和內存結構。一個DataBase Cluster可以包括:多個DataBase、多個User、以及Database中的所有對象。如上圖所示。
- **數據庫-Database **
在PostgreSQL中,數據庫本身也是數據庫對象,並且在邏輯上彼此分離,除數據庫之外的其他數據庫對象(例如:表、索引等等)都屬於他們各自的數據庫。
- 表空間-tablespace
數據庫在邏輯上分成多個存儲單元,稱作表空間。表空間用作把邏輯上相關的結構放在一起。數據庫邏輯上是由一個或多個表空間組成。初始化的時候,會自動創建pg_default和pg_global兩個表空間。
\db
其中:
pg_global:用於存放系統表。
pg_default:該表空間的物理文件存儲在數據目錄中的base目錄中。
創建自己的表空間,並在該表空間上創建表
create tablespace mydemotbs location '/home/postgres/training/pgsql/data/mydemotbs';
create table testtable1(tid int primary key,tname text) tablespace mydemotbs;
- 模式-Schema
當創建一個數據庫時,會為其創建一個名為public的默認Schema。Schema是數據庫中的命名空間,在數據庫中創建的所有對象都是在Schema中創建,一個用戶可以從同一個客戶端連接中訪問不同的Schema。而不同的Schema中可以有多個同名的Table、Index、View、Sequence、Function等等數據庫對象。可以通過下面的方式來查看當前數據庫的Schema
\dn
- 段-segment
一個段是分配給一個邏輯結構(一個表、一個索引或其他對象)的一組區,是數據庫對象使用的空間的集合;段可以有表段、索引段、回滾段、臨時段和高速緩存段等。
- 區-extent
區是數據庫存儲空間分配的一個邏輯單位,它由連續數據塊所組成。第一個段是由一個或多個盤區組成。當一段中間所有空間已完全使用,PostgreSQL為該段分配一個新的范圍。
- 塊-block(Page)
數據塊是PostgreSQL 管理數據文件中存儲空間的單位,為數據庫使用的I/O的最小單位,是最小的邏輯部件。默認值8K。
- 數據庫對象-Database object
如:表、視圖、索引、序列、函數等等。在PostgreSQL中的所有數據庫對象都由各自的對象標識符(OID)進行內部的管理。例如,數據庫的OID存儲在pg_database系統表中,可以通過下面的語句進行查詢。
select oid,datname from pg_database;
而數據庫中的表、索引、序列等數據庫對象的OID則存在了pg_class系統表中,例如可以通過下面的語句查詢前面創建的testtable1表的OID。
select oid,relname,relkind,relfilenode from pg_class where relname ='testtable1';
2、物理存儲結構
在執行initdb的時候會初始化一個目錄,通常我們都會在系統配置相關的環境變量$PGDATA來表示,初始化完成后,會再這個目錄生成相關的子目錄以及一些文件。在postgresql中,表空間的概念並不同於其他關系型數據庫,這里一個Tablespace對應的都是一個目錄。如下圖就是PG的物理結構:
每個目錄的功能與作用如下所示:
而PostgreSQL的物理存儲結構主要是指硬盤上存儲的文件,包括:數據文件、日志文件、參數文件、控制文件、redo日志(WAL)。下面分別進行介紹。
- 數據文件(表文件)
顧名思義,數據文件用於存儲數據。文件名以OID命名,對於超出1G的表數據文件,PostgreSQL會自動將其拆分為多個文件來存儲,而拆分的文件名將由pg_class中的relfilenode字段來決定。如下所示:
select oid,relname,relkind,relfilenode from pg_class where relname ='testtable1';
查看目錄表空間mydemotbs的目錄(其中:13578是數據庫OID,16385是表的OID)
在PostgreSQL中,將保存在磁盤中的塊(Block)稱為Page。數據的讀寫是以Page為最小單位,每個Page默認的大小是8K。在編譯PostgreSQL時指定BLCKSZ大小將決定Page的大小。每個表文件由逗哥BLCKSZ字節大小的Page組成。在分析型數據庫中,適當增加BLCKSZ大小可以小幅度提升數據庫的性能。
- 日志文件
PostgreSQL日志文件的類型,分為以下幾種:
① 運行日志(pg_log)
默認沒有開啟,開啟后會自動生成。查看postgresql.conf文件的配置可以看到相關的參數設置。這個日志一般是記錄服務器與DB的狀態,比如各種Error信息,定位慢查詢SQL,數據庫的啟動關閉信息,發生checkpoint過於頻繁等的告警信息,諸如此類。該日志有.csv格式和.log。建議使用.csv格式,因為它一般會按大小和時間自動切割。pg_log是可以被清理刪除,壓縮打包或者轉移,同時並不影響DB的正常運行。當我們有遇到DB無法啟動或者更改參數沒有生效時,第一個想到的就是查看這個日志。
② 重做日志(pg_xlog)
pg_xlog 這個日志是記錄的Postgresql的WAL信息,默認存儲在目錄$PGDATA/pg_wal/,是一些事務日志信息(transaction log)。默認單個大小是16M,源碼安裝的時候可以更改其大小(./configure --with-wal-segsize=target_value 參數,即可設置)這些日志會在定時回滾恢復(PITR), 流復制(Replication Stream)以及歸檔時能被用到,這些日志是非常重要的,記錄着數據庫發生的各種事務信息,不得隨意刪除或者移動這類日志文件,不然你的數據庫會有無法恢復的風險。
③ 事務日志(pg_xact)
pg_xact是事務提交日志,記錄了事務的元數據。默認開啟。內容一般不能直接讀。默認存儲在目錄$PGDATA/pg_xact/。
④ 服務器日志
如果用pg_ctl啟動的時候沒有指定-l參數來指定服務器日志,錯誤可能會輸出到cmd前台。服務器日志記錄了數據庫的重要信息。
- 參數文件
主要包括postgresql.conf、pg_hba.conf和pg_ident.conf這三個參數文件。下面分別進行介紹:
① postgresql.conf
PostgreSQL的主要參數文件,有很詳細的說明和注釋,和Oracle的pfile,MySQL的my.cnf類似。默認在$PGDATA下。很多參數修改后都需要重啟。9.6之后支持了alter system來修改,修改后的會存在$PGDATA/postgresql.auto.conf下,可以reload或者 restart來使之生效。
② pg_hba.conf
這個是黑白名單的設置。文件里有詳細的參數說明,默認參數如下:
③ pg_ident.conf
pg_ident.conf是用戶映射配置文件,用來配置哪些操作系統用戶可以映射為數據庫用戶。結合pg_hba.conf中,method為ident可以用特定的操作系統用戶和指定的數據庫用戶登錄數據庫。
- 控制文件
控制文件記錄了數據庫運行的一些信息,比如數據庫id,是否open,wal的位置,checkpoint的位置等等。controlfile是很重要的文件。
控制文件的位置:$PGDATA/global/pg_control,可以使用命令bin/pg_controldata查看控制文件的內容,如下:
- redo日志(WAL)
默認保存在$PGDATA/pg_wal目錄下,如下所示:
文件名稱為16進制的24個字符組成,每8個字符一組,每組的意義如下:
00000001 00000000 00000001
時間線 邏輯ID 物理ID
通過下面的語句進行WAL的手動切換:
select pg_switch_wal();
再次查看pg_wal目錄,如下所示:
二、進程結構
執行下面的命令列出所有的PostgreSQL的進程。
ps -ef | grep postgres
① Postmaster進程
主進程Postmaster是整個數據庫實例的總控制進程,負責啟動和關閉數據庫實例。用戶可以運行postmaster,postgres命令加上合適的參數啟動數據庫。實際上,postmaster命令是一個指向postgres的鏈接,如下圖所示。
更多時候我們使用pg_ctl啟動數據庫,pg_ctl也是通過運行postgres來啟動數據庫,它只是做了一些包裝,讓我們更容易啟動數據庫,所以,主進程Postmaster實際是第一個postgres進程,此進程會fork一些與數據庫實例相關的輔助子進程,並管理他們。
當用戶與PostgreSQL數據庫建立連接時,實際上是先與Postmaster進程建立連接。此時,客戶端程序會發出身份證驗證的消息給Postmaster進程,Postmaster主進程根據消息中的信息進行客戶端身份驗證。如果驗證通過,它會fork一個子進程postgres為這個連接服務,fork出來的進程被稱為服務進程,查詢pg_stat_activity表可以看到的pid,就是這些服務進程的pid。
select pid from pg_stat_activity;
② SysLogger進程
在postgresql.conf里啟用 運行日志(pg_log)后,會有SysLogger進程。SysLogger會在日志文件達到指定的大小時關閉當前日志文件,產生新的日志文件。相關配置參數如下:
③ BgWriter后台寫進程
BgWriter是PostgreSQL中在后台將臟頁寫出到磁盤的輔助進程,引入該進程主要為達到如下兩個目的:
首先,數據庫在進行查詢處理時若發現要讀取的數據不在緩沖區中時要先從磁盤中讀入要讀取的數據所在的頁面,此時如果緩沖區已滿,則需要先選擇部分緩沖區中的頁面替換出去。如果被替換的頁面沒有被修改過,那么可以直接丟棄;但如果要被替換的頁已被修改,則必需先將這頁寫出到磁盤中后才能替換,這樣數據庫的查詢處理就會被阻塞。通過使用BgWriter定期寫出緩沖區中的部分臟頁到磁盤中,為緩沖區騰出空間,就可以降低查詢處理被阻塞的可能性。
其次,PostgreSQL在定期作檢查點時需要把所有臟頁寫出到磁盤,通過BgWriter預先寫出一些臟頁,可以減少設置檢查點時要進行的IO操作,使系統的IO負載趨向平穩。通過BgWriter對共享緩沖區寫操作的統一管理,避免了其他服務進程在需要讀入新的頁面到共享緩沖區時,不得不將之前修改過的頁面寫出到磁盤的操作。
④ WalWriter預寫日志寫進程
該進程用於保存WAL預寫日志。預寫式日志WAL(Write Ahead Log,也稱為Xlog)的中心思想是對數據文件的修改必須是只能發生在這些修改已經記錄到日志之后,也就是先寫日志后寫數據。如果遵循這個過程,那么就不需要在每次事務提交的時候都把數據塊刷回到磁盤,這一點與Oracle數據庫是完全一致的。postgresql.conf文件中與WalWriter進程相關的參數如下:
#------------------------------------------------------------------------------
# WRITE AHEAD LOG
#------------------------------------------------------------------------------
#wal_level = minimal # minimal, replica, or logical
# (change requires restart)
#fsync = on # flush data to disk for crash safety
# (turning this off can cause
# unrecoverable data corruption)
#synchronous_commit = on # synchronization level;
# off, local, remote_write, remote_apply, or on
#wal_sync_method = fsync # the default is the first option
# supported by the operating system:
# open_datasync
# fdatasync (default on Linux)
# fsync
# fsync_writethrough
# open_sync
#full_page_writes = on # recover from partial page writes
#wal_compression = off # enable compression of full-page writes
#wal_log_hints = off # also do full page writes of non-critical updates
# (change requires restart)
#wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers
# (change requires restart)
#wal_writer_delay = 200ms # 1-10000 milliseconds
#wal_writer_flush_after = 1MB # measured in pages, 0 disables
#commit_delay = 0 # range 0-100000, in microseconds
#commit_siblings = 5 # range 1-1000
⑤ PgArch歸檔進程
從PostgreSQL 8.x開始,有了PITR(Point-In-Time-Recovery)技術,該技術支持將數據庫恢復到其運行歷史中任意一個有記錄的時間點;PITR的另一個重要的基礎就是對WAL文件的歸檔功能。PgArch輔助進程的目標就是對WAL日志在磁盤上的存儲形式進行歸檔備份。但在默認情況下,PostgreSQL是非歸檔模式,因此看不到PgArch進程。PgArch進程通過postgresql.conf文件中的如下參數進行配置:
# - Archiving -
#archive_mode = off # enables archiving; off, on, or always
# (change requires restart)
#archive_command = '' # command to use to archive a logfile segment
# placeholders: %p = path of file to archive
# %f = file name only
# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
⑥ AutoVacuum自動清理進程
在PG數據庫中,對數據進行UPDATE或者DELETE操作后,數據庫不會立即刪除舊版本的數據,而是標記為刪除狀態。這是因為PG數據庫具有多版本的機制,如果這些舊版本的數據正在被另外的事務打開,那么暫時保留他們是很有必要的。當事務提交后,舊版本的數據已經沒有價值了,數據庫需要清理垃圾數據騰出空間,而清理工作就是AutoVacuum進程進行的。postgresql.conf文件中與AutoVacuum進程相關的參數有:
#------------------------------------------------------------------------------
# AUTOVACUUM
#------------------------------------------------------------------------------
#autovacuum = on # Enable autovacuum subprocess? 'on'
# requires track_counts to also be on.
#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and
# their durations, > 0 logs only
# actions running at least this number
# of milliseconds.
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min # time between autovacuum runs
#autovacuum_vacuum_threshold = 50 # min number of row updates before
# vacuum
#autovacuum_vacuum_insert_threshold = 1000 # min number of row inserts
# before vacuum; -1 disables insert
# vacuums
#autovacuum_analyze_threshold = 50 # min number of row updates before
# analyze
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table
# size before insert vacuum
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart)
#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
# before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
# autovacuum, -1 means use
# vacuum_cost_limit
⑦ PgStat統計信息收集進程
PgStat進程是PostgreSQL數據庫的統計信息收集器,用來收集數據庫運行期間的統計信息,如表的增刪改次數,數據塊的個數,索引的變化等等。收集統計信息主要是為了讓優化器做出正確的判斷,選擇最佳的執行計划。postgresql.conf文件中與PgStat進程相關的參數,如下:
#------------------------------------------
# RUNTIME STATISTICS
#------------------------------------------
# - Query/Index Statistics Collector -
#track_activities = on
#track_counts = on
#track_io_timing = off
#track_functions = none # none, pl, all
#track_activity_query_size = 1024 # (change requires restart)
#stats_temp_directory = 'pg_stat_tmp'
⑧ CheckPoint檢查點進程
檢查點是系統設置的事務序列點,設置檢查點保證檢查點前的日志信息刷到磁盤中。postgresql.conf文件中與之相關的參數有:
# - Checkpoints -
#checkpoint_timeout = 5min # range 30s-1d
#max_wal_size = 1GB
#min_wal_size = 80MB
#checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0
#checkpoint_flush_after = 256kB # measured in pages, 0 disables
#checkpoint_warning = 30s # 0 disables
三、內存結構
PostgreSQL的內存結構,分為:本地內存和共享內存。它們的關系如下圖所示:
① 本地內存:每個后端進程(backend process)自己使用的
② 共享內存:所有進程共同使用