有时 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://linuxkernel.org.cn/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://linuxkernel.org.cn/doc/Documentation/vm/hugetlbpage.txt。
如果您在文档中看到任何不正确、与您在使用特定功能时的体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。