PostgreSQL 有時會耗盡各種作業系統資源限制,尤其是在同一個系統上執行多個伺服器副本,或是在非常大型的安裝中。本節說明 PostgreSQL 使用的核心資源,以及您可以採取哪些步驟來解決與核心資源消耗相關的問題。
PostgreSQL 需要作業系統提供行程間通訊(IPC)功能,特別是共享記憶體和信號量。衍生自 Unix 的系統通常提供 「System V」IPC、「POSIX」IPC或兩者兼具。Windows 具有這些功能的自有實作,此處不討論。
預設情況下,PostgreSQL 會配置非常少量的 System V 共享記憶體,以及大量的匿名 mmap
共享記憶體。 或者,可以使用單個大型 System V 共享記憶體區域(請參閱 shared_memory_type)。 此外,在伺服器啟動時會建立大量的信號量,可以是 System V 或 POSIX 樣式。 目前,POSIX 信號量用於 Linux 和 FreeBSD 系統,而其他平台則使用 System V 信號量。
System VIPC功能通常受到系統範圍配置限制的約束。 當 PostgreSQL 超過其中一個限制時,伺服器將拒絕啟動,並且應該留下一個說明性錯誤訊息,描述問題以及如何解決它。 (另請參閱 第 18.3.1 節。) 相關的核心參數在不同的系統中名稱一致; 表 18.1 概述了這些參數。 但是,設定它們的方法各不相同。 下面提供了一些平台的建議。
表 18.1. System VIPC參數
名稱 | 描述 | 執行一個 PostgreSQL 實例所需的值 |
---|---|---|
SHMMAX |
共享記憶體區段的最大大小(位元組) | 至少 1kB,但預設值通常高得多 |
SHMMIN |
共享記憶體區段的最小大小(位元組) | 1 |
SHMALL |
可用的共享記憶體總量(位元組或頁面) | 如果為位元組,則與 SHMMAX 相同;如果為頁面,則與 ceil(SHMMAX/PAGE_SIZE) 相同,再加上其他應用程式的空間 |
SHMSEG |
每個行程的最大共享記憶體區段數 | 只需要 1 個區段,但預設值高得多 |
SHMMNI |
系統範圍內的最大共享記憶體區段數 | 類似於 SHMSEG ,再加上其他應用程式的空間 |
SEMMNI |
信號量識別符號(即,集合)的最大數量 | 至少 ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 7) / 16) ,再加上其他應用程式的空間 |
SEMMNS |
系統範圍內的信號量最大數量 | ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 7) / 16) * 17 ,再加上其他應用程式的空間 |
SEMMSL |
每個集合的信號量最大數量 | 至少 17 |
SEMMAP |
信號量對應中的條目數 | 請參閱文字 |
SEMVMX |
信號量的最大值 | 至少 1000 (預設值通常為 32767;除非必要,否則請勿變更) |
PostgreSQL 伺服器的每個副本需要少量的 System V 共享記憶體(在 64 位元平台上通常為 48 位元組)。在大多數現代作業系統上,這個數量很容易分配。但是,如果您運行許多伺服器副本,或者您明確地將伺服器配置為使用大量的 System V 共享記憶體(請參閱 shared_memory_type 和 dynamic_shared_memory_type),則可能需要增加 SHMALL
,這是整個系統範圍內的 System V 共享記憶體總量。請注意,在許多系統上,SHMALL
是以頁面而不是位元組為單位來衡量的。
不太可能引起問題的是共享記憶體區段的最小大小(SHMMIN
),對於 PostgreSQL 來說,它最多應該大約是 32 位元組(通常只有 1)。除非您的系統將系統範圍內的區段最大數量 (SHMMNI
) 或每個進程的區段最大數量 (SHMSEG
) 設置為零,否則不太可能引起問題。
當使用 System V 號誌時,PostgreSQL 為每個允許的連線 (max_connections)、允許的自動清理工作程序 (autovacuum_max_workers)、允許的 WAL 發送程序 (max_wal_senders) 和允許的背景程序 (max_worker_processes) 使用一個號誌,每 16 個一組。每個這樣的集合還將包含第 17 個號誌,其中包含一個「魔術數字」,用於檢測與其他應用程式使用的號誌集合的衝突。系統中號誌的最大數量由 SEMMNS
設置,因此必須至少與 max_connections
加上 autovacuum_max_workers
加上 max_wal_senders
,加上 max_worker_processes
一樣高,再加上每 16 個允許的連線加上工作程序的一個額外號誌(請參閱表格 18.1. System V IPC 參數中的公式)。參數 SEMMNI
決定了系統上一次可以存在的號誌集合的數量限制。因此,此參數必須至少為 ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 7) / 16)
。降低允許的連線數量是故障的臨時解決方案,通常會從函數 semget
中得到令人困惑的「裝置上沒有剩餘空間」錯誤訊息。
在某些情況下,也可能需要增加 SEMMAP
,使其至少與 SEMMNS
的數量級相同。如果系統具有此參數(許多系統沒有),它定義了號誌資源映射的大小,其中每個連續的可用號誌區塊都需要一個條目。當釋放號誌集時,它會被添加到與已釋放區塊相鄰的現有條目中,或者在新的映射條目下註冊。如果映射已滿,則釋放的號誌將遺失(直到重新啟動)。號誌空間的碎片化可能會隨著時間的推移導致可用號誌少於應有的數量。
與「號誌撤銷」相關的各種其他設定,例如 SEMMNU
和 SEMUME
,不會影響 PostgreSQL。
當使用 POSIX 號誌時,所需的號誌數量與 System V 相同,即每個允許的連線 (max_connections)、允許的自動清理工作程序 (autovacuum_max_workers)、允許的 WAL 發送程序 (max_wal_senders) 和允許的背景程序 (max_worker_processes) 使用一個號誌。在這個選項優先的平台上,對於 POSIX 號誌的數量沒有特定的核心限制。
預設的共享記憶體設定通常就足夠了,除非您已將 shared_memory_type
設為 sysv
。System V 號誌未在此平台上使用。
可以使用 sysctl
或 loader
介面更改預設的 IPC 設定。可以使用 sysctl
設置以下參數
#
sysctl kern.ipc.shmall=32768
#
sysctl kern.ipc.shmmax=134217728
為了使這些設定在重新啟動後仍然存在,請修改 /etc/sysctl.conf
。
如果您已將 shared_memory_type
設為 sysv
,您可能還希望將核心配置為將 System V 共享記憶體鎖定到 RAM 中,並防止將其分頁到交換分區。可以使用 sysctl
設定 kern.ipc.shm_use_phys
來完成此操作。
如果在 FreeBSD jail 中運行,您應該將其 sysvshm
參數設定為 new
,以便它擁有自己獨立的 System V 共享記憶體命名空間。(在 FreeBSD 11.0 之前,必須允許從 jails 共享對主機的 IPC 命名空間的存取權限,並採取措施避免衝突。)
預設的共享記憶體設定通常就足夠了,除非您已將 shared_memory_type
設為 sysv
。通常您需要增加 kern.ipc.semmni
和 kern.ipc.semmns
,因為 NetBSD 對這些的預設設定太小了。
可以使用 sysctl
調整 IPC 參數,例如
#
sysctl -w kern.ipc.semmni=100
為了使這些設定在重新啟動後仍然存在,請修改 /etc/sysctl.conf
。
如果您已將 shared_memory_type
設為 sysv
,您可能還希望將核心配置為將 System V 共享記憶體鎖定到 RAM 中,並防止將其分頁到交換分區。可以使用 sysctl
設定 kern.ipc.shm_use_phys
來完成此操作。
預設的共享記憶體設定通常就足夠了,除非您已將 shared_memory_type
設為 sysv
。通常您需要增加 kern.seminfo.semmni
和 kern.seminfo.semmns
,因為 OpenBSD 對這些的預設設定太小了。
可以使用 sysctl
調整 IPC 參數,例如
#
sysctl kern.seminfo.semmni=100
為了使這些設定在重新啟動後仍然存在,請修改 /etc/sysctl.conf
。
預設的共享記憶體設定通常就足夠了,除非您已將 shared_memory_type
設為 sysv
,即使在那種情況下,也僅限於使用低預設值的舊核心版本。System V 號誌未在此平台上使用。
可以使用 sysctl
介面更改共享記憶體大小設定。例如,允許 16 GB
$
sysctl -w kernel.shmmax=17179869184
$
sysctl -w kernel.shmall=4194304
為了使這些設定在重新啟動後仍然存在,請參閱 /etc/sysctl.conf
。
預設的共享記憶體和號誌設定通常就足夠了,除非您已將 shared_memory_type
設為 sysv
。
在 macOS 中配置共享記憶體的建議方法是建立一個名為 /etc/sysctl.conf
的檔案,其中包含變數賦值,例如
kern.sysv.shmmax=4194304 kern.sysv.shmmin=1 kern.sysv.shmmni=32 kern.sysv.shmseg=8 kern.sysv.shmall=1024
請注意,在某些 macOS 版本中,所有五個共享記憶體參數都必須在 /etc/sysctl.conf
中設定,否則這些值將被忽略。
SHMMAX
只能設定為 4096 的倍數。
SHMALL
在此平台上以 4 kB 的頁面為單位計算。
除了 SHMMNI
之外,其他所有參數都可以在執行時使用 sysctl 進行更改。但最好還是透過 /etc/sysctl.conf
設定您偏好的值,以便這些值可以在重新啟動後保留。
預設的共享記憶體和訊號燈設定通常對大多數 PostgreSQL 應用程式來說已足夠。Solaris 預設的 SHMMAX
為系統的四分之一RAM。要進一步調整此設定,請使用與 postgres
使用者相關聯的專案設定。例如,以 root
身分執行以下命令
projadd -c "PostgreSQL DB User" -K "project.max-shm-memory=(privileged,8GB,deny)" -U postgres -G postgres user.postgres
此命令會新增 user.postgres
專案,並將 postgres
使用者的共享記憶體上限設定為 8GB,並在使用者下次登入或重新啟動 PostgreSQL (而非重新載入) 時生效。上述假設 PostgreSQL 由 postgres
群組中的 postgres
使用者執行。不需要重新啟動伺服器。
對於具有大量連線的資料庫伺服器,其他建議的 Kernel 設定變更包括
project.max-shm-ids=(priv,32768,deny) project.max-sem-ids=(priv,4096,deny) project.max-msg-ids=(priv,4096,deny)
此外,如果您在一個 Zone 內執行 PostgreSQL,您可能還需要提高 Zone 的資源使用限制。有關 projects
和 prctl
的更多資訊,請參閱 System Administrator's Guide 中的 "Chapter2: Projects and Tasks"。
如果正在使用 systemd,則必須小心,確保 IPC 資源(包括共享記憶體)不會被作業系統過早移除。從原始碼安裝 PostgreSQL 時,這尤其重要。PostgreSQL 發行套件的使用者受影響的可能性較小,因為 postgres
使用者通常會建立為系統使用者。
logind.conf
中的設定 RemoveIPC
控制是否在使用者完全登出時移除 IPC 物件。系統使用者不受此限制。在預設的 systemd 中,此設定預設為開啟,但某些作業系統發行版本預設為關閉。
當此設定開啟時,通常觀察到的影響是,用於平行查詢執行的共享記憶體物件會在看似隨機的時間被移除,導致在嘗試開啟和移除它們時出現錯誤和警告,例如
WARNING: could not remove shared memory segment "/PostgreSQL.1450751626": No such file or directory
systemd 對於不同類型的 IPC 物件(共享記憶體與訊號燈、System V 與 POSIX)的處理方式略有不同,因此可能會觀察到某些 IPC 資源未以與其他資源相同的方式移除。但不建議依賴這些細微的差異。
“使用者登出” 可能會作為維護作業的一部分發生,或者在管理員以 postgres
使用者或其他類似身分登入時手動發生,因此通常很難避免。
“系統使用者” 的判斷在 systemd 編譯時,從 /etc/login.defs
中的 SYS_UID_MAX
設定決定。
封裝和部署腳本應小心使用 useradd -r
、adduser --system
或等效命令將 postgres
使用者建立為系統使用者。
或者,如果使用者帳戶建立不正確或無法更改,建議設定
RemoveIPC=no
在 /etc/systemd/logind.conf
或其他適當的設定檔中。
必須確保至少滿足這兩件事之一,否則 PostgreSQL 伺服器將非常不可靠。
類 Unix 作業系統會強制執行各種資源限制,這些限制可能會干擾 PostgreSQL 伺服器的運作。特別重要的是每個使用者的進程數、每個進程的開啟檔案數以及每個進程可用的記憶體量。這些都有一個 “硬性” 限制和一個 “軟性” 限制。實際上起作用的是軟性限制,但使用者可以將其更改為最高達到硬性限制。硬性限制只能由 root 使用者更改。系統呼叫 setrlimit
負責設定這些參數。Shell 的內建命令 ulimit
(Bourne Shell) 或 limit
(csh) 用於從命令列控制資源限制。在 BSD 衍生的系統上,檔案 /etc/login.conf
控制登入期間設定的各種資源限制。詳情請參閱作業系統文件。相關參數為 maxproc
、openfiles
和 datasize
。例如
default:\ ... :datasize-cur=256M:\ :maxproc-cur=256:\ :openfiles-cur=256:\ ...
(-cur
是軟性限制。附加 -max
來設定硬性限制。)
核心也可能對某些資源有系統範圍的限制。
在 Linux 上,核心參數 fs.file-max
決定核心將支援的最大開啟檔案數。可以使用 sysctl -w fs.file-max=
更改它。要使設定在重新啟動後持續存在,請在 N
/etc/sysctl.conf
中新增一個指定值。每個進程的檔案最大限制在編譯核心時已固定;請參閱 /usr/src/linux/Documentation/proc.txt
以獲取更多資訊。
PostgreSQL 伺服器每個連線使用一個進程,因此您應該提供至少與允許的連線一樣多的進程,以及系統其餘部分所需的進程。這通常不是問題,但如果您在一台機器上執行多個伺服器,則情況可能會變得緊張。
開啟檔案的預設限制通常設定為 “對社會友善” 的值,允許許多使用者在一台機器上共存,而不會使用系統資源的不適當比例。如果您在一台機器上執行多個伺服器,這可能就是您想要的,但在專用伺服器上,您可能想要提高此限制。
另一方面,某些系統允許個別進程開啟大量檔案;如果超過幾個進程這樣做,則很容易超過系統範圍的限制。如果您發現這種情況發生,並且您不想更改系統範圍的限制,則可以設定 PostgreSQL 的 max_files_per_process 組態參數來限制開啟檔案的消耗。
另一個在支援大量用戶端連線時可能需要注意的核心限制是最大 Socket 連線佇列長度。如果在很短的時間內收到超過這個數量的連線請求,某些請求可能會在 PostgreSQL 伺服器處理請求之前就被拒絕,導致這些用戶端收到無用的連線失敗錯誤訊息,例如 “Resource temporarily unavailable”(資源暫時不可用)或 “Connection refused”(連線拒絕)。在許多平台上,預設的佇列長度限制為 128。要提高這個限制,請透過 sysctl 調整適當的核心參數,然後重新啟動 PostgreSQL 伺服器。這個參數在 Linux 上通常被命名為 net.core.somaxconn
,在較新的 FreeBSD 上為 kern.ipc.soacceptqueue
,在 macOS 和其他 BSD 變種上為 kern.ipc.somaxconn
。
Linux 上的預設虛擬記憶體行為對於 PostgreSQL 並非最佳。由於核心實作記憶體過度配置的方式,如果 PostgreSQL 或其他程序的記憶體需求導致系統耗盡虛擬記憶體,核心可能會終止 PostgreSQL 的 postmaster(管理伺服器程序)。
如果發生這種情況,您會看到類似這樣的核心訊息(請查閱您的系統文件和配置,以了解在哪裡可以找到這樣的訊息)
Out of Memory: Killed process 12345 (postgres).
這表示 postgres
程序由於記憶體壓力而被終止。雖然現有的資料庫連線將繼續正常運作,但不會接受新的連線。要恢復,需要重新啟動 PostgreSQL。
避免這個問題的一個方法是在一台可以確保其他程序不會耗盡機器記憶體的機器上運行 PostgreSQL。如果記憶體緊張,增加作業系統的交換空間可以幫助避免這個問題,因為只有在實體記憶體和交換空間都耗盡時,才會調用記憶體不足(OOM)終止程序。
如果 PostgreSQL 本身是導致系統耗盡記憶體的原因,您可以透過更改您的配置來避免這個問題。在某些情況下,降低與記憶體相關的配置參數可能會有所幫助,特別是 shared_buffers
、work_mem
和 hash_mem_multiplier
。在其他情況下,問題可能是由於允許過多的連線連到資料庫伺服器本身造成的。在許多情況下,減少 max_connections
並改用外部連線池軟體可能更好。
可以修改核心的行為,使其不會 “過度配置” 記憶體。雖然這個設定不會完全阻止 OOM終止程序 被調用,但它會顯著降低機會,因此會帶來更穩健的系統行為。這是通過 sysctl
選擇嚴格的過度配置模式來完成的
sysctl -w vm.overcommit_memory=2
或將等效的條目放置在 /etc/sysctl.conf
中。您可能還希望修改相關設定 vm.overcommit_ratio
。有關詳細資訊,請參閱核心文件 https://www.kernel.org/doc/Documentation/vm/overcommit-accounting。
另一種方法,可以與修改或不修改 vm.overcommit_memory
一起使用,是將 postmaster 程序的特定程序 OOM 分數調整 值設定為 -1000
,從而保證它不會成為 OOM 終止程序的目標。最簡單的方法是在調用 postgres
之前,在 PostgreSQL 啟動腳本中執行
echo -1000 > /proc/self/oom_score_adj
請注意,此操作必須以 root 身份完成,否則無效;因此,root 擁有的啟動腳本是執行此操作的最簡單的地方。如果這樣做,您還應該在啟動腳本中設定這些環境變數,然後再調用 postgres
export PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj export PG_OOM_ADJUST_VALUE=0
這些設定將導致 postmaster 子程序以正常的 OOM 分數調整值 0 運行,以便 OOM 終止程序仍然可以在需要時將它們作為目標。如果您希望子程序以其他 OOM 分數調整值運行,則可以使用 PG_OOM_ADJUST_VALUE
的其他值。(PG_OOM_ADJUST_VALUE
也可以省略,在這種情況下,預設值為零。)如果您未設定 PG_OOM_ADJUST_FILE
,則子程序將以與 postmaster 相同的 OOM 分數調整值運行,這是不明智的,因為重點是確保 postmaster 具有優先設定。
使用 Huge Pages 可以減少在使用大型連續記憶體塊時的開銷,正如 PostgreSQL 所做的那樣,尤其是在使用大的 shared_buffers 值時。要在 PostgreSQL 中使用此功能,您需要一個具有 CONFIG_HUGETLBFS=y
和 CONFIG_HUGETLB_PAGE=y
的核心。您還必須配置作業系統以提供足夠所需大小的 Huge Pages。運行時計算的參數 shared_memory_size_in_huge_pages 報告所需的 Huge Pages 數量。可以使用類似以下的 postgres
命令在啟動伺服器之前查看此參數
$postgres -D $PGDATA -C shared_memory_size_in_huge_pages
3170 $grep ^Hugepagesize /proc/meminfo
Hugepagesize: 2048 kB $ls /sys/kernel/mm/hugepages
hugepages-1048576kB hugepages-2048kB
在此範例中,預設值為 2MB,但您也可以透過 huge_page_size 明確請求 2MB 或 1GB,以調整 shared_memory_size_in_huge_pages
計算的頁面數。雖然在此範例中我們至少需要 3170
個 Huge Pages,但如果機器上的其他程式也需要 Huge Pages,則更大的設定將是合適的。我們可以透過以下方式設定它
# sysctl -w vm.nr_hugepages=3170
不要忘記將此設定新增到 /etc/sysctl.conf
,以便在重新啟動後重新應用它。對於非預設的 Huge Page 大小,我們可以改用
# echo 3170 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
也可以在啟動時使用核心參數(例如 hugepagesz=2M hugepages=3170
)提供這些設定。
有時核心可能無法立即分配所需數量的 Huge Pages,因為存在碎片,因此可能需要重複該命令或重新啟動。(重新啟動後,機器的大部分記憶體應該可用於轉換為 Huge Pages。)要驗證給定大小的 Huge Page 分配情況,請使用
$ cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
可能還需要透過 sysctl 設定 vm.hugetlb_shm_group
,授予資料庫伺服器的作業系統使用者使用巨頁的權限,並且/或者透過 ulimit -l
授予鎖定記憶體的權限。
PostgreSQL 中巨頁的預設行為是在可能的情況下使用它們,採用系統預設的巨頁大小,並且在失敗時回退到普通頁面。 要強制使用巨頁,您可以在 postgresql.conf
中將 huge_pages 設定為 on
。 請注意,使用此設定,如果沒有足夠的巨頁可用,PostgreSQL 將無法啟動。
有關 Linux 巨頁功能的詳細說明,請參閱 https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt。
如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的地方,請使用此表單報告文件問題。