受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

18.4. 管理内核资源 #

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

18.4.1. 共享内存和信号量 #

PostgreSQL要求操作系统提供进程间通信(IPC)机制,特别是共享内存和信号量。Unix 派生系统通常提供System V IPCPOSIX IPC,或者两者兼有。Windows对这些特性有自己的实现,这里不作讨论。

默认情况下,PostgreSQL会分配极少量的 System V 共享内存,以及大量匿名的 mmap 共享内存。或者,也可以使用单个大型 System V 共享内存区域(见shared_memory_type)。此外,服务器启动时还会创建大量信号量,这些信号量可以是 System V 风格或 POSIX 风格。目前,Linux 和 FreeBSD 系统使用 POSIX 信号量,而其他平台使用 System V 信号量。

System V IPC 功能通常受系统范围的分配限制约束。当PostgreSQL超出这些限制之一时,服务器会拒绝启动,并留下带有指导性的错误消息,说明问题所在以及应如何处理(另见Section 18.3.1)。相关内核参数在不同系统上的命名基本一致,Table 18.1给出了概览;不过,设置它们的方法却各不相同。下面给出一些平台上的建议。

Table 18.1. System V IPC参数

名称 描述 运行一个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 共享内存(在 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 个一组。运行期计算参数num_os_semaphores会报告所需信号量数;可在启动服务器前用如下命令查看:

$ postgres -D $PGDATA -C num_os_semaphores

每组 16 个信号量还包含第 17 个信号量,用于存放magic number,以检测与其他应用信号量集合的冲突。系统最大信号量数由 SEMMNS 设定,因此至少应为 num_os_semaphores 加上每组 16 个所需信号量的一个附加量(见Table 18.1中的公式)。SEMMNI决定系统中同一时刻可存在的信号量集合数上限,因此至少应为 ceil(num_os_semaphores / 16)。降低允许连接数是对通常带有误导措辞设备上没有剩余空间(来自semget)这类失败的临时缓解手段。

在某些情况下,可能还需要增大SEMMAP,使其至少与SEMMNS处于同一数量级。如果系统提供这个参数(很多系统没有),它定义的是信号量资源映射的大小;映射中每个连续的可用信号量块都需要占用一项。每当一个信号量集合被释放时,它要么会并入与该释放块相邻的现有项,要么会登记为一个新的映射项。如果映射已满,被释放的信号量就会丢失(直到重启)。因此,随着时间推移,信号量空间碎片化可能会导致可用信号量少于应有数量。

semaphore undo有关的其他各种设置,如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以 4kB 的页面度量。

SHMMNI 之外,其他设置都可以使用 sysctl 动态更改。不过,最好还是通过/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)

此外,如果你正在某个区(zone)中运行PostgreSQL,可能也需要提高该区的资源使用限制。关于projectsprctl的更多信息,请参见System Administrator's Guide中的 "Chapter 2: Projects and Tasks"。

18.4.2. systemd RemoveIPC #

如果使用systemd,必须注意不要让操作系统过早移除 IPC 资源(包括共享内存)。这在从源码安装 PostgreSQL 时尤其值得关注。使用发行版软件包的用户较不容易受到影响,因为此时postgres用户通常会被创建为系统用户。

logind.conf 中的 RemoveIPC 设置控制当用户完全注销时是否移除 IPC 对象。系统用户不受此限制。原版 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用户创建为系统用户。

或者,如果该用户账户创建得不正确,或无法更改,建议在/etc/systemd/logind.conf或其他适当的配置文件中设置

RemoveIPC=no

Caution

上述两件事至少要确保做到其中之一,否则 PostgreSQL 服务器会变得非常不可靠。

18.4.3. 资源限制 #

类 Unix 操作系统会施加多种资源限制,这些限制可能干扰 PostgreSQL 服务器的运行。其中尤其重要的是:每个用户可用的进程数限制、每个进程可打开文件数限制,以及每个进程可用内存量限制。每一种限制都有限制和限制。实际生效的是软限制,但用户可以自行把它调高到不超过硬限制;硬限制则只能由 root 用户修改。系统调用setrlimit负责设置这些参数。shell 的内置命令ulimit(Bourne shell)或limitcsh)可用于在命令行控制资源限制。在 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服务器来得及处理之前就被拒绝,于是客户端会收到不太有帮助的连接失败错误,例如Resource temporarily unavailableConnection refused。在许多平台上,默认队列长度限制是 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 的情况下使用:把 PostgreSQL 主进程的 OOM score adjustment 值设为 -1000,从而保证它不会成为 OOM 杀手的目标。最简单的做法是在 PostgreSQL 启动脚本中、调用 postgres 之前执行以下命令:

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

这些设置会使 postgres 子进程以默认值零的 OOM score adjustment 运行,因此 OOM 杀手在需要时仍可将它们作为目标。如果你希望子进程使用其他 OOM score adjustment 值,也可以为 PG_OOM_ADJUST_VALUE 指定其他数值(也可省略,此时默认为零)。如果不设置 PG_OOM_ADJUST_FILE,子进程就会和主进程使用相同的 OOM score adjustment,这并不明智,因为这样做的目的正是为了确保主进程拥有优先保护设置。

18.4.5. Linux 大页 #

使用大页可以减少PostgreSQL这类会使用大块连续内存的软件所带来的开销,尤其是在shared_buffers取值较大时。要在PostgreSQL中使用这一特性,你需要一个启用了CONFIG_HUGETLBFS=yCONFIG_HUGETLB_PAGE=y的内核。你还需要把操作系统配置为提供足够多、且大小合适的大页。要估算所需的大页数量,可以先在不启用大页的情况下启动PostgreSQL,然后使用/proc/sys文件系统检查 postmaster 的匿名共享内存段大小,以及系统默认和支持的大页大小。操作可能如下:

$ head -1 $PGDATA/postmaster.pid
4170
$ pmap 4170 | awk '/rw-s/ && /zero/ {print $2}'
6490428K
$ grep ^Hugepagesize /proc/meminfo
Hugepagesize:       2048 kB
$ ls /sys/kernel/mm/hugepages
hugepages-1048576kB  hugepages-2048kB

在此示例中,默认大小是 2MB,但你也可以使用huge_page_size显式请求 2MB 或 1GB。 假设使用2MB大页,6490428 / 2048大约等于3169.154,因此在这个示例中我们至少需要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

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。