有時 PostgreSQL 會耗盡各種作業系統資源限制,特別是在同一系統上執行多個伺服器副本或在大型安裝時。本節解釋 PostgreSQL 使用的核心資源以及解決核心資源消耗相關問題的步驟。
PostgreSQL 需要作業系統提供程序間通訊(IPC)功能,特別是共享記憶體和訊號量。Unix 類系統通常提供““System V””IPC、““POSIX””IPC或兩者都提供。 Windows 有自己的這些功能的實現,此處不討論。
預設情況下,PostgreSQL 分配非常少量的 System V 共享記憶體,以及大量的匿名 mmap
共享記憶體。或者,可以使用單個大的 System V 共享記憶體區域(參見 shared_memory_type)。此外,在伺服器啟動時會建立大量的訊號量,可以是 System V 或 POSIX 風格。當前,Linux 和 FreeBSD 系統使用 POSIX 訊號量,而其他平臺使用 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(num_os_semaphores / 16) 加上其他應用程式的空間 |
SEMMNS |
系統範圍內的最大訊號量數 | ceil(num_os_semaphores / 16) * 17 加上其他應用程式的空間 |
SEMMSL |
每個集合的最大訊號量數 | 至少 17 |
SEMMAP |
訊號量對映中的條目數 | 參見文字 |
SEMVMX |
訊號量的最大值 | 至少 1000(預設值通常為 32767;除非必要,否則不要更改) |
PostgreSQL 為每個伺服器副本需要幾字節的 System V 共享記憶體(通常是 48 位元組,在 64 位平臺上)。在大多數現代作業系統上,可以輕鬆分配此數量。但是,如果您運行了多個伺服器副本或顯式配置伺服器使用大量 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_worker_slots)、允許的 WAL 傳送程序(max_wal_senders)、允許的後臺程序(max_worker_processes)等使用一個訊號量,每組 16 個。執行時計算的引數 num_os_semaphores 報告了所需的訊號量數量。可以使用類似以下命令在啟動伺服器之前檢視此引數:
$ postgres -D $PGDATA -C num_os_semaphores
每組 16 個訊號量還將包含第 17 個訊號量,其中包含一個““魔術數字””,用於檢測與其他應用程式使用的訊號量集是否衝突。系統中的最大訊號量數由 SEMMNS
設定,因此它必須至少等於 num_os_semaphores
加上每組 16 個所需訊號量的一個額外(參見 表 18.1 中的公式)。引數 SEMMNI
決定了系統上可以同時存在的訊號量集的最大數量。因此,此引數至少必須是 ceil(num_os_semaphores / 16)
。減少允許的連線數是解決錯誤的臨時方法,這些錯誤通常是令人困惑的““裝置上空間不足””,來自 semget
函式。
在某些情況下,可能還需要將 SEMMAP
增加到至少與 SEMMNS
同等數量級。如果系統具有此引數(許多系統沒有),則它定義了訊號量資源對映的大小,其中每個連續的可用訊號量塊需要一個條目。當訊號量集被釋放時,它將被新增到現有條目中,該條目緊鄰釋放的塊,或者它被註冊在一個新的對映條目下。如果對映已滿,釋放的訊號量將丟失(直到重啟)。訊號量空間的碎片化可能會隨著時間的推移導致可用訊號量少於應有的數量。
與““訊號量撤銷””相關的各種其他設定,例如 SEMMNU
和 SEMUME
,不會影響 PostgreSQL。
當使用 POSIX 訊號量時,所需的訊號量數量與 System V 相同,即每個允許的連線(max_connections)、允許的自動清理工作程序(autovacuum_worker_slots)、允許的 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 監獄中,您應該將其 sysvshm
引數設定為 new
,以便它擁有自己的獨立 System V 共享記憶體名稱空間。(在 FreeBSD 11.0 之前,有必要啟用從監獄到主機 IPC 名稱空間的共享訪問,並採取措施避免衝突。)
預設的共享記憶體設定通常足夠好,除非您將 shared_memory_type
設定為 sysv
。但是,您需要增加 kern.ipc.semmni
和 kern.ipc.semmns
,因為 NetBSD 的預設設定對於這些是不可行的。
IPC 引數可以使用 sysctl
進行調整,例如:
#
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 的預設設定對於這些是不可行的。
IPC 引數可以使用 sysctl
進行調整,例如:
#
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 頁為單位測量。
可以透過 sysctl 動態更改除 SHMMNI
之外的所有引數。但最好透過 /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
組中執行。無需重啟伺服器。
對於具有大量連線的資料庫伺服器,其他推薦的核心設定更改包括:
project.max-shm-ids=(priv,32768,deny) project.max-sem-ids=(priv,4096,deny) project.max-msg-ids=(priv,4096,deny)
此外,如果您在區域(zone)內執行 PostgreSQL,您可能還需要提高區域資源使用限制。有關 projects
和 prctl
的更多資訊,請參見《系統管理員指南》的“第二章:專案和任務”。
如果正在使用 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 shells)或 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 配置引數設定為限制開啟檔案數的消耗。
當支援大量客戶端連線時,另一個可能需要考慮的核心限制是最大套接字連線佇列長度。如果在極短的時間內到達的連線請求超過此數量,一些請求可能會在 PostgreSQL 伺服器能夠處理請求之前被拒絕,導致客戶端收到無用的連線失敗錯誤,例如““資源暫時不可用””或““連線被拒絕””。在許多平臺上,預設佇列長度限制為 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 殺手鎖定。最簡單的方法是執行:
echo -1000 > /proc/self/oom_score_adj
在 PostgreSQL 啟動指令碼中,在呼叫 postgres
之前。請注意,此操作必須以 root 身份執行,否則將無效;因此,一個由 root 擁有的啟動指令碼是最容易執行此操作的地方。如果您這樣做,您還應該在呼叫 postgres
之前在啟動指令碼中設定這些環境變數:
export PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj export PG_OOM_ADJUST_VALUE=0
這些設定將導致 postmaster 子程序以正常的 OOM 分數調整(零)執行,因此 OOM 殺手仍然可以在需要時將其鎖定。如果您想讓子程序以其他 OOM 分數調整執行,您可以使用 PG_OOM_ADJUST_VALUE
的其他值。(PG_OOM_ADJUST_VALUE
也可以省略,在這種情況下預設為零。)如果您不設定 PG_OOM_ADJUST_FILE
,子程序將以與 postmaster 相同的 OOM 分數調整執行,這是不明智的,因為關鍵是要確保 postmaster 具有優先設定。
使用大頁可以減少使用大塊連續記憶體時的開銷,正如 PostgreSQL 所做的,尤其是在使用較大的 shared_buffers 值時。要在 PostgreSQL 中使用此功能,您需要一個帶有 CONFIG_HUGETLBFS=y
和 CONFIG_HUGETLB_PAGE=y
的核心。您還必須配置作業系統以提供足夠的大頁,並且大小合適。執行時計算的引數 shared_memory_size_in_huge_pages 報告所需的大頁數。可以使用類似以下命令在啟動伺服器之前檢視此引數:
$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
個大頁,但如果機器上的其他程式也需要大頁,則較大的設定會更合適。我們可以透過以下方式設定:
# sysctl -w vm.nr_hugepages=3170
不要忘記將此設定新增到 /etc/sysctl.conf
中,以便在重啟後重新應用。對於非預設的大頁大小,我們可以改用:
# echo 3170 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
還可以透過核心引數(如 hugepagesz=2M hugepages=3170
)在啟動時提供這些設定。
有時由於碎片化,核心無法立即分配所需數量的大頁,因此可能需要重複命令或重啟。(重啟後立即,大部分記憶體都可以轉換為大頁。)要驗證給定大小的大頁分配情況,請使用:
$ 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。
如果您在文件中看到任何不正確、與您在使用特定功能時的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。