支持的版本: 当前 (17) / 16 / 15 / 14 / 13
开发版本: 开发版
不支持的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

18.4. 管理内核资源 #

PostgreSQL 有时会耗尽各种操作系统资源限制,尤其是在同一系统上运行多个服务器副本时,或者在非常大的安装中。本节解释 PostgreSQL 使用的内核资源以及您可以采取的步骤来解决与内核资源消耗相关的问题。

18.4.1. 共享内存和信号量 #

PostgreSQL 要求操作系统提供进程间通信(IPC)功能,特别是共享内存和信号量。类 Unix 系统通常提供 System VIPCPOSIXIPC或两者兼而有之。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_typedynamic_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_connectionsautovacuum_max_workersmax_wal_sendersmax_worker_processes 再加上每个 16 个允许的连接和工作进程的一个额外信号量一样高(请参阅 表 18.1 中的公式)。参数 SEMMNI 确定系统一次可以存在的信号量集数量的限制。因此,此参数必须至少为 ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 7) / 16)。减少允许的连接数是解决失败的临时方法,这些失败通常来自函数 semget,其措辞令人困惑地为 设备上没有剩余空间

在某些情况下,可能还需要增加 SEMMAP 以至少与 SEMMNS 数量级相同。如果系统具有此参数(许多系统没有),则它定义信号量资源映射的大小,其中每个可用的连续信号量块都需要一个条目。当释放一个信号量集时,它会被添加到与释放块相邻的现有条目中,或者它会被注册在新的映射条目下。如果映射已满,则释放的信号量会丢失(直到重新启动)。随着时间的推移,信号量空间碎片化可能会导致可用信号量少于应有的数量。

信号量撤销相关的各种其他设置,例如 SEMMNUSEMUME,不会影响 PostgreSQL

当使用 POSIX 信号量时,所需的信号量数量与 System V 相同,即每个允许的连接(max_connections)、允许的自动清理工作进程(autovacuum_max_workers)、允许的 WAL 发送器进程(max_wal_senders)和允许的后台进程(max_worker_processes)各需要一个信号量。在首选此选项的平台上,POSIX 信号量的数量没有特定的内核限制。

FreeBSD

默认的共享内存设置通常足够好,除非您已将 shared_memory_type 设置为 sysv。此平台上不使用 System V 信号量。

可以使用 sysctlloader 接口更改默认的 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 之前,必须启用从 jail 对主机 IPC 命名空间的共享访问,并采取措施避免冲突。)

NetBSD

默认的共享内存设置通常足够好,除非您已将 shared_memory_type 设置为 sysv。您通常需要增加 kern.ipc.semmnikern.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 来完成。

OpenBSD

默认的共享内存设置通常足够好,除非您已将 shared_memory_type 设置为 sysv。您通常需要增加 kern.seminfo.semmnikern.seminfo.semmns,因为 OpenBSD 的这些默认设置非常小。

可以使用 sysctl 调整 IPC 参数,例如

# sysctl kern.seminfo.semmni=100

要使这些设置在重启后仍然有效,请修改 /etc/sysctl.conf

Linux

默认的共享内存设置通常足够好,除非您已将 shared_memory_type 设置为 sysv,即使如此,也仅在默认值较低的旧内核版本上才需要调整。此平台上不使用 System V 信号量。

可以通过 sysctl 接口更改共享内存大小设置。例如,要允许 16 GB

$ sysctl -w kernel.shmmax=17179869184
$ sysctl -w kernel.shmall=4194304

要使这些设置在重启后仍然有效,请参阅 /etc/sysctl.conf

macOS

默认的共享内存和信号量设置通常足够好,除非您已将 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 设置您的首选值,以便这些值在重启后仍然有效。

Solaris
illumos

默认的共享内存和信号量设置对于大多数 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(而不是重新加载)时生效。以上假设 PostgreSQLpostgres 组中的 postgres 用户运行。不需要服务器重启。

对于具有大量连接的数据库服务器,其他建议的内核设置更改为

project.max-shm-ids=(priv,32768,deny)
project.max-sem-ids=(priv,4096,deny)
project.max-msg-ids=(priv,4096,deny)

此外,如果您在区域内运行 PostgreSQL,您可能还需要提高区域资源使用限制。有关 projectsprctl 的更多信息,请参阅《系统管理员指南》中的“第 2 章:项目和任务”。

18.4.2. systemd RemoveIPC #

如果正在使用 systemd,则必须注意操作系统不会过早删除 IPC 资源(包括共享内存)。从源代码安装 PostgreSQL 时尤其如此。PostgreSQL 发行版软件包的用户不太可能受到影响,因为 postgres 用户通常被创建为系统用户。

logind.conf 中的设置 RemoveIPC 控制着当用户完全注销时是否删除 IPC 对象。系统用户是豁免的。此设置在 stock systemd 中默认为启用,但某些操作系统发行版将其默认为关闭。

当此设置启用时,一个典型的观察到的影响是,用于并行查询执行的共享内存对象会在看似随机的时间被删除,从而在尝试打开和删除它们时导致错误和警告,例如

WARNING:  could not remove shared memory segment "/PostgreSQL.1450751626": No such file or directory

不同类型的 IPC 对象(共享内存与信号量、System V 与 POSIX)在 systemd 中得到略微不同的处理,因此可能会观察到某些 IPC 资源没有以与其他资源相同的方式被删除。但是,不建议依赖这些细微的差异。

用户注销可能会在维护作业中发生,或者当管理员以 postgres 用户或类似身份登录时手动发生,因此通常很难防止。

系统用户是在 systemd 编译时从 /etc/login.defs 中的 SYS_UID_MAX 设置确定的。

打包和部署脚本应注意通过使用 useradd -radduser --system 或等效项将 postgres 用户创建为系统用户。

或者,如果用户帐户创建不正确或无法更改,建议设置

RemoveIPC=no

/etc/systemd/logind.conf 或其他适当的配置文件中。

警告

必须确保这两个方面中的至少一个,否则 PostgreSQL 服务器将非常不可靠。

18.4.3. 资源限制 #

类 Unix 操作系统强制执行各种资源限制,这些限制可能会干扰您的 PostgreSQL 服务器的运行。尤其重要的是每个用户的进程数限制、每个进程的打开文件数限制以及每个进程可用的内存量限制。这些都有一个 限制和一个 限制。软限制是实际计算的限制,但用户可以将其更改为硬限制。硬限制只能由 root 用户更改。系统调用 setrlimit 负责设置这些参数。shell 的内置命令 ulimit(Bourne shells)或 limit (csh) 用于从命令行控制资源限制。在基于 BSD 的系统上,文件 /etc/login.conf 控制登录期间设置的各种资源限制。有关详细信息,请参阅操作系统文档。相关的参数是 maxprocopenfilesdatasize。例如

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 服务器每个连接使用一个进程,因此您应提供至少与允许的连接数一样多的进程,以及系统其余部分所需的进程。这通常不是问题,但如果您在一台机器上运行多个服务器,情况可能会变得紧张。

打开文件的出厂默认限制通常设置为 对社会友好 的值,允许许多用户在一台机器上共存,而不会使用系统中不适当的一部分资源。如果您在一台机器上运行多个服务器,这可能就是您想要的,但在专用服务器上,您可能需要提高此限制。

另一方面,某些系统允许单个进程打开大量文件;如果超过几个进程这样做,则很容易超过系统范围的限制。如果您发现这种情况发生,并且不想更改系统范围的限制,则可以设置 PostgreSQLmax_files_per_process 配置参数以限制打开文件的消耗。

在支持大量客户端连接时,另一个可能需要关注的内核限制是最大套接字连接队列长度。如果在此时间内到达的连接请求数量超过该值,则在 PostgreSQL 服务器可以服务请求之前,某些请求可能会被拒绝,并且这些客户端会收到无益的连接失败错误,例如 资源暂时不可用连接被拒绝。在许多平台上,默认队列长度限制为 128。要提高它,请通过 sysctl 调整相应的内核参数,然后重启 PostgreSQL 服务器。该参数在 Linux 上名为 net.core.somaxconn,在较新的 FreeBSD 上名为 kern.ipc.soacceptqueue,在 macOS 和其他 BSD 变体上名为 kern.ipc.somaxconn

18.4.4. Linux 内存过度提交 #

在 Linux 上,默认的虚拟内存行为对于 PostgreSQL 来说并非最佳。由于内核实现内存过度提交的方式,如果 PostgreSQL 或其他进程的内存需求导致系统虚拟内存耗尽,内核可能会终止 PostgreSQL 的 postmaster(主管服务器进程)。

如果发生这种情况,您会看到类似如下的内核消息(请查阅您的系统文档和配置,以了解在哪里查找此类消息):

Out of Memory: Killed process 12345 (postgres).

这表明 postgres 进程由于内存压力而被终止。尽管现有的数据库连接将继续正常运行,但不会接受新的连接。要恢复,需要重新启动 PostgreSQL

避免此问题的一种方法是在您可以确保其他进程不会使机器内存耗尽的机器上运行 PostgreSQL。如果内存紧张,增加操作系统的交换空间可以帮助避免此问题,因为只有当物理内存和交换空间都耗尽时,才会调用内存不足(OOM)杀手。

如果 PostgreSQL 本身是导致系统内存耗尽的原因,您可以通过更改配置来避免此问题。在某些情况下,降低与内存相关的配置参数可能会有所帮助,特别是 shared_bufferswork_memhash_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

在调用 postgres 之前的 PostgreSQL 启动脚本中。请注意,此操作必须以 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 具有优先设置。

18.4.5. Linux 大页 #

使用大页可以减少使用大块连续内存时的开销,就像 PostgreSQL 一样,尤其是在使用较大的 shared_buffers 值时。要在 PostgreSQL 中使用此功能,您需要一个带有 CONFIG_HUGETLBFS=yCONFIG_HUGETLB_PAGE=y 的内核。您还需要配置操作系统以提供足够多的所需大小的大页。运行时计算的参数 shared_memory_size_in_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 个大页,但如果机器上的其他程序也需要大页,则较大的设置将是合适的。我们可以通过以下方式设置它

# 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

提交更正

如果您在文档中发现任何不正确、与您对特定功能的体验不符或需要进一步澄清的内容,请使用 此表单 报告文档问题。