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((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 中的公式)。参数 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 监狱中运行,您应该将其 sysvshm
参数设置为 new
,以便它拥有自己的独立 System V 共享内存命名空间。(在 FreeBSD 11.0 之前,必须从监狱中启用对主机 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 页面为单位测量。
可以使用 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)
此外,如果您在区域中运行 PostgreSQL,您可能还需要提高区域资源使用限制。有关 projects
和 prctl
的更多信息,请参见 系统管理员指南 中的“第 2 章:项目和任务”。
如果使用 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 配置参数设置为限制打开文件的使用。
在支持大量客户端连接时,另一个可能令人担忧的内核限制是最大套接字连接队列长度。如果在非常短的时间内收到超过该数量的连接请求,有些请求可能会在 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。
另一种方法是设置 postmaster 进程的特定于进程的 OOM 分数调整 值为 -1000
,从而保证它不会成为 OOM 杀手的目标,这种方法无论是否更改 vm.overcommit_memory
都可以使用。最简单的方法是执行
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
的内核。您还必须配置操作系统以提供足够数量的所需大小的巨页。要确定所需的巨页数量,请使用 postgres
命令查看 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。
如果您在文档中发现任何不正确的内容、与您对特定功能的体验不符或需要进一步说明的内容,请使用 此表格 报告文档问题。