行程模型#

PostgreSQL 伺服器實例由數個彼此互動的行程(process)組成。

伺服器啟動時第一個被拉起的行程是 postgres,傳統上稱為 postmaster。它負責:

  • 衍生(spawn)所有其他行程(類 Unix 系統使用 fork 系統呼叫)
  • 監督這些行程:任一行程失敗時重新啟動它;若共享資料有損毀風險,則重啟整個伺服器
側註:為什麼還沒改用執行緒?

行程模型因其簡單性,從一開始就被 PostgreSQL 採用,而關於是否改用執行緒(thread)的討論也從未停歇。

現行模型有幾項缺點:

  • 靜態共享記憶體配置,導致緩衝快取之類的結構無法即時調整大小
  • 平行演算法難以實作,效率也不如理想
  • session 與行程緊密綁定

改用執行緒聽起來很有前景,儘管會牽涉隔離、作業系統相容性與資源管理等挑戰。然而其實作需要徹底翻修程式碼、投入數年時間,因此目前保守觀點佔上風:近期不會有這類改變。

背景行程#

伺服器的運作由背景行程(background process)維持,主要有:

行程職責
startup故障後復原系統
autovacuum移除表格與索引中的過期資料
wal writer把 WAL 條目寫入磁碟
checkpointer執行檢查點(checkpoint)
writer把髒頁(dirty page)刷回磁碟
stats collector蒐集實例的使用統計
wal sender把 WAL 條目送往備援節點(replica)
wal receiver在備援節點接收 WAL 條目

其中有些行程在任務完成後即終止,有些一直在背景執行,還有一些可以關閉。

每個行程都由組態參數控制,有時多達數十個。要全面地調校伺服器,你必須理解它的內部運作。但一般性的考量只能幫你選出還算合適的初始值;之後這些設定仍須依監控資料進行微調。

共享記憶體與快取#

為了讓行程能互相溝通,postmaster 會配置共享記憶體(shared memory),供所有行程存取。

由於磁碟(尤其是 HDD,但 SSD 也一樣)比 RAM 慢得多,PostgreSQL 使用快取:把部分共享記憶體保留給最近讀取的頁面,期待它們會被多次需要,藉此降低重複存取磁碟的開銷。修改過的資料也不會立即寫回磁碟,而是延遲一段時間後才刷出。

  • 緩衝快取(buffer cache)佔據共享記憶體的大部分
  • 共享記憶體中還包含其他伺服器用來加速磁碟存取的緩衝區

作業系統也有自己的快取。PostgreSQL(幾乎)從不繞過作業系統機制去做 direct I/O,因此會產生雙重快取(double caching)。

圖 1-4:PostgreSQL 實例的行程與記憶體結構——postmaster、backend、背景行程、共享記憶體中的緩衝快取,以及作業系統快取

故障與 WAL#

發生故障時(例如斷電或作業系統當機),保存在 RAM 中的資料會全部遺失,緩衝快取也不例外。留在磁碟上的檔案,其頁面是在不同時間點寫入的——彼此並不一致。

為了能還原資料一致性,PostgreSQL 在運作期間會維護預寫式日誌(write-ahead log,WAL),讓遺失的操作在必要時能被重做。