PostgreSQL:程式結構

天翼雲開發者社群發表於2023-02-23

本文分享自天翼雲開發者社群@《 PostgreSQL:程式結構 》,作者: 周*******平


  Postgresql 是一個C/S架構的關係型資料庫,由多個後臺程式管理資料庫,下面分別介紹一些這些程式

postgres server process 一個伺服器端程式,是所有程式的父程式。該程式管理資料庫檔案,接受客戶端與資料庫的連線,且代表客戶端對資料庫進行操作。該程式的程式名叫做 postgres。

backend process 每一個客戶端的連線都有一個後端程式存在

backgroud processes 為管理資料庫而產生的一些程式

backgroud work processes 9.3 以後版本開始有這個程式,這裡不做詳細介紹

 

postgres server process

管理後端的常駐程式,是所有程式的父程式,早期的版本叫 ’postmaster’。

主要職責:

資料庫的啟停

監聽客戶端連線。

為每個客戶連線 fork 單獨的 postgres 服務程式。

當服務程式出錯時進行修復。

管理資料檔案。

管理與資料庫執行相關的輔助程式

流程如下:

1. pg_ctl start 執行後,這個程式就會啟動

2. 從實體記憶體中分配內給給 shared memory,然後產生很多其他的 backgroup processes 

3. 等待客戶端連線,每產生一個連線就會生成一個 backend process,一個 postgres server process 只能監聽一個埠,預設埠是 5432。

 

一旦有前端連線過來, postgres 會透過 fork(2) 生成子程式。沒有 Fork(2) 的 windows 平臺的話,則利用 createProcess() 生成新的程式。這種情形的話,和 fork(2) 不同的是,父程式的資料不會被繼承過來,所以需要利用共享記憶體把父程式的資料繼承過來。

 

backend processes

透過 TCP 協議和客戶端建立通訊,當客戶端斷開時,連線消失。允許多個客戶端同時連線,連線數由 max_connections 引數控制,預設是 100,如果客戶端頻繁的和服務端建立連線然後斷開,會增加資料庫的開銷,導致伺服器負載不正常,因為資料庫本身不提供連線池的功能,如果有需要,可以使用 pgbouncer 或者 pgpool-II。

後端的處理流程

下面看看資料庫引擎 postgres 子程式的處理概要。為了簡單起見下面的說明中,把 backendprocess 簡稱為backend。Backend 的 main 函式是 PostgresMain (tcop/postgres.c)。

1.  接收前端傳送過來的查詢 (SQL文)

2.  SQL文是單純的文字,電腦是認識不了的,所以要轉換成比較容易處理的內部形式構文樹parser tree,這個處理的稱為構文解析。構文解析的模組稱為parser.這個階段只能夠使用文字字面上得來的資訊,所以只要沒語法錯誤之類的錯誤,即使是select不存在的表也不會報錯。這個階段的構文樹被稱為raw parse tree. 構文處理的入口在raw_parser (parser/parser.c)。

3. 構文樹解析完以後,會轉換為查詢樹 (Query tree)。這個時候,會訪問資料庫,檢查表是否存在,如果存在的話,則把表名轉換為OID。這個處理稱為分析處理(Analyze), 進行分析處理的模組是analyzer。 另外,PostgreSQL的程式碼裡面提到構文樹parser tree的時候,更多的時候是指查詢樹Query tree。分析處理的模組的入口在parse_analyze (parser/analyze.c)

4. PostgreSQL還透過查詢語句的重寫實現檢視(view)和規則(rule), 所以需要的時候,在這個階段會對查詢語句進行重寫。這個處理稱為重寫(rewrite),重寫的入口在QueryRewrite (rewrite/rewriteHandler.c)。

5. 透過解析查詢樹,可以實際生成計劃樹。生成查詢樹的處理稱為 ‘執行計劃處理’,最關鍵是要生成估計能在最短的時間內完成的計劃樹(plan tree)。這個步驟稱為’查詢最佳化’(不叫query optimize, 而是optimize), 而完成這個處理的模組稱為查詢最佳化器(不叫query optimizer,而是optimizer, 或者稱為planner)。執行計劃處理的入口在standard_planner (optimizer/plan/planner.c)。

6. 按照執行計劃裡面的步驟可以完成查詢要達到的目的。執行執行計劃樹裡面步驟的處理稱為執行處理 ‘execute’, 完成這個處理的模組稱為執行器‘Executor’, 執行器的入口地址為,ExecutorRun (executor/execMain.c)

7. 執行結果返回給前端。

8. 返回到步驟一重複執行。

 

backgroud processes

根據 pg_hba.conf 定義的安全策略來判斷是否允許進行連線,根據策略,會拒絕某些特定的 IP 及網路,或者也可以只允許某些特定的使用者或者對某些資料庫進行連線。

Postgres 會接受前端過來的查詢,然後對資料庫進行檢索,最好把結果返回,有時也會對資料庫進行更新。更新的資料同時還會記錄在事務日誌裡面(PostgreSQL 稱為 WAL 日誌),這個主要是當停電的時候,伺服器當機,重新啟動的時候進行恢復處理的時候使用的。另外,把日誌歸檔儲存起來,可在需要進行恢復的時候使用。在 PostgreSQL 9.0 以後,透過把 WAL 日誌傳送其他的 postgreSQL,可以實時得進行資料庫複製,這就是所謂的‘資料庫複製’功能。

這裡顯示了 PostgreSQL 資料庫的程式資訊。在以下示例中,一個 postgres 伺服器程式(pid 為 9687),兩個後端程式(pids 為 9697 和 9717)以及表 2.1 中列出的幾個後臺程式正在執行。

 

PostgreSQL 資料庫系統的主要功能都集中於 Postgres 程式,其入口是 Main 模組中的 main 函式,在初始化資料集簇,啟動資料庫伺服器是,都將從這裡開始執行。Main 模組主要的工作時確定當前的作業系統平臺,並據此做一些平臺相關的環境變數設定和初始化,然後透過對命令列引數的判斷,將控制轉到相應的模組中去。下圖是 main 函式的呼叫流程。

 

PostgreSQL 系統主函式 main 的流程

PostgreSQL 守護程式 Postmaster 為使用者連線請求分配後臺 Postgres 服務程式,還將啟動相關的後臺服務程式:SysLogger(系統日誌程式)、PgStat(統計資料收集程式)、AutoVacuum(系統自動清理程式)。在 Postmaster 進入到迴圈監聽中時啟動如下進行:BgWriter(後臺寫程式)、WalWriter(預寫式日誌寫程式)、PgArch(預寫式日誌歸檔程式)。這些程式將在後續文章中介紹。

下圖是 PostgreSQL 的後臺流程圖:

 

 

blackground writer

Writer process 在適當的時間點把共享記憶體上的快取寫往磁碟。透過這個程式,可以防止在檢查點的時候(checkpoint),大量的往磁碟寫而導致效能惡化,使得伺服器可以保持比較穩定的效能。Background writer 起來以後就一直常駐記憶體,但是並非一直在工作,它會在工作一段時間後進行休眠,休眠的時間間隔透過postgresql.conf 裡面的引數 bgwriter_delay 設定,預設是 200 微秒。

這個程式的另外一個重要的功能是定期執行檢查點 (checkpoint)。

檢查點的時候,會把共享記憶體上的快取內容往資料庫檔案寫,使得記憶體和檔案的狀態一致。透過這樣,可以在系統崩潰的時候可以縮短從 WAL 恢復的時間,另外也可以防止 WAL 無限的增長。 可以透過 postgresql.conf 的 checkpoint_segments、checkpoint_timeout 指定執行檢查點的時間間隔。

Writer 程式是把共享記憶體中的髒頁寫到磁碟上的程式。它的作用有兩個:一是定期把髒資料從記憶體緩衝區刷出到磁碟中,減少查詢時的阻塞;二是 PG 在定期作檢查點時需要把所有髒頁寫出到磁碟,透過 BgWriter 預先寫出一些髒頁,可以減少設定檢查點(CheckPoint,資料庫恢復技術的一種)時要進行的IO操作,使系統的IO負載趨向平穩。BgWriter 是 PostgreSQL8.0 以後新加的特性,它的機制可以透過 postgresql.conf 檔案中以"bgwriter_"開頭配置引數來控制:

bgwriter_delay:backgroud writer 程式連續兩次 flush 資料之間的時間的間隔。預設值是 200,單位是毫秒。

bgwriter_lru_maxpages:backgroud writer 程式每次寫的最多資料量,預設值是 100,單位 buffers。如果髒資料量小於該數值時,寫操作全部由 backgroud writer 程式完成;反之,大於該值時,大於的部分將有 server process 程式完成。設定該值為0時表示禁用 backgroud writer 寫程式,完全有 server process 來完成;配置為-1時表示所有髒資料都由 backgroud writer 來完成。(這裡不包括 checkpoint 操作)

bgwriter_lru_multiplier:這個參數列示每次往磁碟寫資料塊的數量,當然該值必須小於 bgwriter_lru_maxpages。設定太小時需要寫入的髒資料量大於每次寫入的資料量,這樣剩餘需要寫入磁碟的工作需要 server process 程式來完成,將會降低效能;值配置太大說明寫入的髒資料量多於當時所需 buffer 的數量,方便了後面再次申請 buffer 工作,同時可能出現 IO 的浪費。該引數的預設值是 2.0。


bgwriter 的最大資料量計算方式:
1000/bgwriter_delay\*bgwriter_lru_maxpages\*8K = 最大資料量

bgwriter_flush_after:資料頁大小達到 bgwriter_flush_after 時觸發 BgWriter,預設是 512KB。

 

vacuum程式

Autovacuum(自動清理)啟動程式

autovacuum launcher process 是依賴於 postmaster 間接啟動 vacuum 程式。而其自身是不直接啟動自動vacuum程式的。透過這樣可以提高系統的可靠性。

PG 資料庫中,對資料進行 UPDATE 或者 DELETE 操作後,資料庫不會立即刪除舊版本的資料,而是標記為刪除狀態。這是因為PG資料庫具有多版本的機制,如果這些舊版本的資料正在被另外的事務開啟,那麼暫時保留他們是很有必要的。當事務提交後,舊版本的資料已經沒有價值了,資料庫需要清理垃圾資料騰出空間,而清理工作就是 AutoVacuum 程式進行的。postgresql.conf 檔案中與 AutoVacuum 程式相關的引數有:

autovacuum:是否啟動系統自動清理功能,預設值為 on。

log_autovacuum_min_duration:這個引數用來記錄 autovacuum 的執行時間,當 autovaccum 的執行時間超過 log_autovacuum_min_duration 引數設定時,則 autovacuum 資訊記錄到日誌裡,預設為 "-1", 表示不記錄。

autovacuum_max_workers:設定系統自動清理工作程式的最大數量。

autovacuum_naptime:設定兩次系統自動清理操作之間的間隔時間。

autovacuum_vacuum_threshold 和 autovacuum_analyze_threshold:設定當表上被更新的元組數的閾值超過這些閾值時分別需要執行 vacuum 和 analyze。

autovacuum_vacuum_scale_factor 和 autovacuum_analyze_scale_factor:設定表大小的縮放係數。

autovacuum_freeze_max_age:設定需要強制對資料庫進行清理的XID上限值。

autovacuum_vacuum_cost_delay:當 autovacuum 程式即將執行時,對 vacuum 執行 cost 進行評估,如果超過 autovacuum_vacuum_cost_limit 設定值時,則延遲,這個延遲的時間即為 autovacuum_vacuum_cost_delay。如果值為 -1,表示使用 vacuum_cost_delay 值,預設值為 20 ms。

autovacuum_vacuum_cost_limit:這個值為 autovacuum 程式的評估閥值, 預設為 -1,表示使用 "vacuum_cost_limit " 值,如果在執行 autovacuum 程式期間評估的 cost 超過autovacuum_vacuum_cost_limit,則 autovacuum 程式則會休眠。

自動 vacuum程式

autovacuum worker process 程式實際執行 vacuum 的任務。有時候會同時啟動多個 vacuum 程式。

WAL writer(預寫式日誌寫)

WAL writer process 把共享記憶體上的 WAL 快取在適當的時間點往磁碟寫,透過這樣,可以減輕後端程式在寫自己的 WAL 快取時的壓力,提高效能。另外,非同步提交設為 true 的時候,可以保證在一定的時間間隔內,把 WAL 快取上的內容寫入 WAL 日誌檔案。

預寫式日誌 WAL(Write Ahead Log,也稱為 Xlog)的中心思想是對資料檔案的修改必須是隻能發生在這些修改已經記錄到日誌之後,也就是先寫日誌後寫資料(日誌先行)。使用這種機制可以避免資料頻繁的寫入磁碟,可以減少磁碟 I/O。資料庫在當機重啟後可以運用這些 WAL 日誌來恢復資料庫。postgresql.conf 檔案中與 WalWriter 程式相關的引數如下:

wal_level:控制 wal 儲存的級別。wal_level 決定有多少資訊被寫入到 WAL 中。 預設值是最小的(minimal),其中只寫入從崩潰或立即關機中恢復的所需資訊。replica 增加 wal 歸檔資訊同時包括只讀伺服器需要的資訊。(9.6 中新增,將之前版本的 archive 和 hot_standby 合併)
logical 主要用於logical decoding 場景

fsync:該引數直接控制日誌是否先寫入磁碟。預設值是 ON(先寫入),表示更新資料寫入磁碟時系統必須等待 WAL 的寫入完成。可以配置該引數為 OFF,表示更新資料寫入磁碟完全不用等待 WAL 的寫入完成。

synchronous_commit:引數配置是否等待 WAL 完成後才返回給使用者事務的狀態資訊。預設值是 ON,表明必須等待 WAL 完成後才返回事務狀態資訊;配置成 OFF 能夠更快地反饋回事務狀態。

wal_sync_method:WAL 寫入磁碟的控制方式,預設值是 fsync,可選用值包括 open_datasync、fdatasync、fsync_writethrough、fsync、open_sync。open_datasync 和 open_sync 分別表示在開啟 WAL檔案時使用 O_DSYNC 和 O_SYNC 標誌;fdatasync 和 fsync 分別表示在每次提交時呼叫 fdatasync 和 fsync 函式進行資料寫入,兩個函式都是把作業系統的磁碟快取寫回磁碟,但前者只寫入檔案的資料部分,而後者還會同步更新檔案的屬性;fsync_writethrough 表示在每次提交併寫回磁碟會保證作業系統磁碟快取和記憶體中的內容一致。

full_page_writes:表明是否將整個 page 寫入 WAL。

wal_buffers:用於存放 WAL 資料的記憶體空間大小,系統預設值是 64K,該引數還受 wal_writer_delay、commit_delay 兩個引數的影響。

wal_writer_delay:WalWriter 程式的寫間隔時間,預設值是 200 毫秒,如果時間過長可能造成 WAL 緩衝區的記憶體不足;時間過短將會引起 WAL 的不斷寫入,增加磁碟 I/O 負擔。

commit_delay:表示一個已經提交的資料在 WAL 緩衝區中存放的時間,預設值是 0 毫秒,表示不用延遲;設定為非 0 值時事務執行 commit 後不會立即寫入 WAL 中,而仍存放在 WAL 緩衝區中,等待 WalWriter 程式週期性地寫入磁碟。

commit_siblings:表示當一個事務發出提交請求時,如果資料庫中正在執行的事務數量大於 commit_siblings 值,則該事務將等待一段時間(commit_delay 的值);否則該事務則直接寫入 WAL。系統預設值是 5,該引數還決定了 commit_delay 的有效性。

wal_writer_flush_after:當髒資料超過閾值時,會被刷出到磁碟。

 

CheckPoint(檢查點)程式

檢查點是系統設定的事務序列點,設定檢查點保證檢查點前的日誌資訊刷到磁碟中。 postgresql.conf 檔案中與之相關的引數有:

stats collector process

統計資訊的收集程式。收集好統計表的訪問次數,磁碟的訪問次數等資訊。收集到的資訊除了能被 autovaccum 利用,還可以給其他資料庫管理員作為資料庫管理的參考資訊。

PgStat 程式是 PostgreSQL 資料庫的統計資訊收集器,用來收集資料庫執行期間的統計資訊,如表的增刪改次數,資料塊的個數,索引的變化等等。收集統計資訊主要是為了讓最佳化器做出正確的判斷,選擇最佳的執行計劃。 postgresql.conf 檔案中與 PgStat 程式相關的引數,如下:

track_activities:表示是否對會話中當前執行的命令開啟統計資訊收集功能,該引數只對超級使用者和會話所有者可見,預設值為 on(開啟)。

track_counts:表示是否對資料庫活動開啟統計資訊收集功能,由於在 AutoVacuum 自動清理程式中選擇清理的資料庫時,需要資料庫的統計資訊,因此該引數預設值為 on。

track_io_timing:定時呼叫資料塊 I/O,預設是 off,因為設定為開啟狀態會反覆的呼叫資料庫時間,這給資料庫增加了很多開銷。只有超級使用者可以設定

track_functions:表示是否開啟函式的呼叫次數和呼叫耗時統計。

track_activity_query_size:設定用於跟蹤每一個活動會話的當前執行命令的位元組數,預設值為 1024,只能在資料庫啟動後設定。

stats_temp_directory:統計資訊的臨時儲存路徑。路徑可以是相對路徑或者絕對路徑,引數預設為 pg_stat_tmp,設定此引數可以減少資料庫的物理 I/O,提高效能。此引數只能在 postgresql.conf 檔案或者伺服器命令列中修改。

 

Logging collector

postgresql 的活動狀態寫到日誌資訊檔案(並非事務日誌),在指定的時間間隔裡面,對日誌檔案進行 rotate.

Archiver

Archive process 把 WAL 日誌轉移到歸檔日誌裡。如果儲存了基礎備份以及歸檔日誌,即使實在磁碟完全損壞的時候,也可以回覆資料庫到最新的狀態。

類似於 Oracle 資料庫的 ARCH 歸檔程式,不同的是 ARCH 是吧 redo log 進行歸檔,PgArch 是把 WAL 日誌進行歸檔。再深入點,WAL 日誌會被迴圈使用,也就是說,過去的 WAL 日誌會被新產生的日誌覆蓋,PgArch 程式就是為了在覆蓋前把 WAL 日誌備份出來。歸檔日誌的作用是為了資料庫能夠使用全量備份和備份後產生的歸檔日誌,從而讓資料庫回到過去的任一時間點。PG 從 8.X 版本開始提供的 PITR(Point-In-Time-Recovery)技術,就是運用的歸檔日誌。

PgArch 程式透過 postgresql.conf 檔案中的如下引數進行

archive_mode:表示是否進行歸檔操作,可選擇為 off(關閉)、on(啟動)和 always(總是開啟),預設值為 off(關閉)。

archive_command:由管理員設定的用於歸檔 WAL 日誌的命令。在用於歸檔的命令中,預定義變數“%p”用來指代需要歸檔的 WAL 全路徑檔名,“%f”表示不帶路徑的檔名(這裡的路徑都是相對於當前工作目錄的路徑)。每個WAL段檔案歸檔時將呼叫archive_command所指定的命令。當歸檔命令返回 0 時,PostgreSQL 就會認為檔案被成功歸檔,然後就會刪除或迴圈使用該 WAL 段檔案。否則,如果返回一個非零值,PostgreSQL 會認為檔案沒有被成功歸檔,便會週期性地重試直到成功。

archive_timeout:表示歸檔週期,在超過該引數設定的時間時強制切換 WAL 段,預設值為 0(表示禁用該功能)。

wal sender / wal receiver

wal sender 程式和 wal receiver 程式是實現 postgresql 複製(streaming replication)的程式。Wal sender 程式透過網路傳送 WAL 日誌,而其他 PostgreSQL 例項的 wal receiver 程式則接收相應的日誌。Wal receiver 程式的宿主PostgreSQL(也稱為 Standby)接受到 WAL 日誌後,在自身的資料庫上還原,生成一個和傳送端的 PostgreSQL(也稱為 Master)完全一樣的資料庫。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31490526/viewspace-2716590/,如需轉載,請註明出處,否則將追究法律責任。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70014251/viewspace-2936571/,如需轉載,請註明出處,否則將追究法律責任。

相關文章