Postgresql 中文操作指南
19.4. Managing Kernel Resources #
PostgreSQL 有时会耗尽各种操作系统资源限制,尤其是在同一系统上运行多个服务器副本时,或者在非常大的安装中。本节说明了 PostgreSQL 使用的内核资源以及为解决与内核资源消耗相关的问题而可以采取的步骤。
19.4.1. Shared Memory and Semaphores #
PostgreSQL 需要操作系统来提供进程间通信(IPC)功能,尤其是共享内存和信号灯。源自 Unix 的系统通常提供“System V”IPC,“POSIX”IPC 或二者兼具。Windows 有自己的实现功能,此处不在讨论范围。
默认情况下,PostgreSQL 分配极少量的 System V 共享内存,以及更大数量的匿名 mmap 共享内存。或者,可以使用单个较大的 System V 共享内存区域(参见 shared_memory_type)。此外,在服务器启动时会创建大量信号量,这些信号量可以是 System V 样式,也可以是 POSIX 样式。目前,POSIX 信号量用在 Linux 和 FreeBSD 系统上,而其他平台使用 System V 信号量。
System V IPC 功能通常受到系统级分配限制的约束。当 PostgreSQL 超出其中一个限制时,服务器将拒绝启动,并且会留下一条有指导性的错误消息来描述问题和解决办法。(另请参见 Section 19.3.1。)相关内核参数在不同的系统之间始终如一地命名; Table 19.1 提供了概述。然而,设置它们的方法各不相同。下面给出了一些平台的建议。
Table 19.1. System V IPC Parameters
Name |
Description |
运行一个 PostgreSQL 实例所需的值 |
SHMMAX |
共享内存段的最大大小(字节) |
\>1 kB,但默认值通常会高很多 |
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 + 5) / 16) ,外加给其他应用程序的空间 |
SEMMNS |
整个系统内允许拥有的最大信号量数 |
ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16) * 17 外加给其他应用程序的空间 |
SEMMSL |
每个信号集内允许拥有的最大信号量数 |
at least 17 |
SEMMAP |
信号地图内的条目数 |
see text |
SEMVMX |
Maximum value of semaphore |
至少 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)的最大段数设置为零,否则不太可能造成问题。
当使用系统V信号量时,PostgreSQL每允许一个连接( max_connections)、允许一个自动清理工作程序( autovacuum_max_workers)和允许一个后台程序( max_worker_processes),以16个一组的方式使用一个信号量。每16个信号量还包含一个包含“魔数”的第17个信号量,用于检查与其他应用程序使用的信号量冲突。系统中信号量的最大数量由_SEMMNS_设置,因此它必定至少与_max_connections_加上_autovacuum_max_workers_加上_max_wal_senders_加上_max_worker_processes_加上针对每个允许的16个连接和工作程序新增的一个相等(请参见 Table 19.1中的公式)。参数_SEMMNI_用于确定系统一次可以使用的信号量数目限制。因此此参数必须至少为_ceil((max_connections + autovacuum_max_workers + max_wal_senders + max_worker_processes + 5) / 16)_。降低允许连接的数量是一种临时解决方法,可用于处理通常用令人困惑的表达“设备无可用空间”来表述的故障(来自_semget_函数)。
在某些情况下,可能还需要将 SEMMAP 至少增加到 SEMMNS 的数量级。如果系统具有该参数(许多系统没有),它将定义信号灯资源映射的大小,其中每一块连续的可用信号灯组都需要一个条目。当释放信号灯组时,它将被添加到与释放块相邻的现有条目,或者在新的映射条目下注册。如果映射已满,释放的信号灯将丢失(直至重新启动)。信号灯空间的碎片化可能会导致可用信号灯少于应有数量。
与“信号灯撤消”相关的其他各种设置,例如 SEMMNU 和 SEMUME,不会影响 PostgreSQL。
当使用POSIX信号量时,所需的信号量数量与系统V相同,也就是说,每允许一个连接( max_connections)、允许一个自动清理工作程序( autovacuum_max_workers)和允许一个后台程序( max_worker_processes),都使用一个信号量。在更偏好此选项的平台上,没有对POSIX信号量的数量设置特定的内核限制。
-
AIX
-
此类参数(如 SHMMAX)不必进行任何特殊配置,因为它似乎已配置为允许将所有内存用作共享内存。这是通常用于其他数据库(例如 DB/2)的配置类型。
-
然而,可能需要修改 /etc/security/limits 中的全局 ulimit 信息,因为文件大小(fsize)和文件数(nofiles)的默认硬限制可能太低。
-
-
FreeBSD
-
除非您已将 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 名称空间的访问権,并采取措施以避免冲突。)
-
NetBSD
-
-
默认的共享内存设置通常已经足够,除非你将 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 来实现此目的。
-
OpenBSD
-
-
默认的共享内存设置通常已经足够,除非你将 shared_memory_type 设置为 sysv 。你通常会希望增加 kern.seminfo.semmni 和 kern.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 版本中,all five 共享内存参数必须在 /etc/sysctl.conf 中设置,否则这些值会被忽略。
-
SHMMAX 只能设置为 4096 的倍数。
-
SHMALL 在此平台上以 4 kB 页面为单位进行测量。
-
可以使用 sysctl 即时修改除了 SHMMNI 之外的所有值。但最理想的情况还是通过 /etc/sysctl.conf 设置你首选的值,以便在重新启动后这些值也能保留。
-
Solarisillumos
-
-
默认共享内存和信号量设置通常对于大多数 PostgreSQL 应用程序来说是足够好的。Solaris 的默认值为系统 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(而不是重新加载)时生效。上述假设是在 postgres 组中由 postgres 用户运行 PostgreSQL。无需重新启动服务器。
-
为具有大量连接的数据库服务器推荐的其他内核设置更改包括:
project.max-shm-ids=(priv,32768,deny)
project.max-sem-ids=(priv,4096,deny)
project.max-msg-ids=(priv,4096,deny)
-
此外,如果你在某个区域内运行 PostgreSQL,你可能也需要提高区域资源使用限制。有关 projects 和 prctl 的更多信息,请参见 System Administrator’s Guide 中的“第 2 章:项目和任务”。
19.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
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 或其他适当的配置文件中。
19.4.3. Resource Limits #
类 Unix 操作系统强制执行各种类型的资源限制,这些限制可能会干扰 PostgreSQL 服务器的操作。特别重要的是对每个用户可用的进程数、每个进程可打开的文件数以及每个进程可用的内存量进行限制。这些限制都具有“硬”限制和“软”限制。软限制是实际计算的,但用户可以将其更改到硬限制。硬限制只能由 root 用户更改。系统调用 setrlimit 负责设置这些参数。外壳内置命令 ulimit(Bourne 外壳)或 limit(csh)用于从命令行控制资源限制。在 BSD 派生系统上,文件 /etc/login.conf 控制登录期间设置的各种资源限制。有关详细信息,请参见操作系统文档。相关参数是 maxproc、openfiles 和 datasize。例如:
default:\
...
:datasize-cur=256M:\
:maxproc-cur=256:\
:openfiles-cur=256:\
...
(-cur 是软限制。附加 -max 以设置硬限制。)
内核还可以在某些资源上设置系统范围内的限制。
PostgreSQL 服务器每个连接使用一个进程,因此除了系统其他部分所需的进程外,还应至少提供与允许的连接一样多的进程。这通常不成问题,但如果你在同一台机器上运行多个服务器,情况可能会变得紧张。
打开文件数的出厂默认限制通常设置为“社交友好”值,这允许许多用户在一台机器上共存而不会使用不适当数量的系统资源。如果你在同一台机器上运行多个服务器,这可能就是你想要的,但在专用服务器上,你可能希望提高此限制。
另一方面,某些系统允许单个处理打开大量文件;如果有多个处理这样做,那么系统限制就很可能被超出。如果您发现发生了这种情况,并且不想更改系统限制,您可以设置PostgreSQL的 max_files_per_process配置参数,以对打开文件的使用进行限制。
在支持大量客户端连接时,可能另一个需要注意的内核限制是最大套接字连接队列长度。如果在非常短的时间内到达多于此数量的连接请求,一些连接可能会在 PostgreSQL 服务器可以处理请求之前被拒绝,这些客户端会收到无用的连接失败错误,例如“资源暂时不可用”或“连接被拒绝”。在许多平台上,默认队列长度限制为 128。要提高此限制,请通过 sysctl 调整适当的内核参数,然后重新启动 PostgreSQL 服务器。该参数在 Linux 上的不同名称是 net.core.somaxconn,在较新的 FreeBSD 上的不同名称是 kern.ipc.soacceptqueue,在 macOS 和其他 BSD 变体上不同名称是 kern.ipc.somaxconn。
19.4.4. Linux Memory Overcommit #
Linux 上的默认虚拟内存行为对于 PostgreSQL 来说并不是最优的。由于内核实现内存超量提交的方式,如果 PostgreSQL 或其他进程的内存需求导致系统耗尽虚拟内存,内核可能会终止 PostgreSQL 的后端(监督服务器进程)。
如果发生这种情况,你会看到类似这样的一条内核消息(请查阅系统文档和配置以了解在哪里查找此类消息):
Out of Memory: Killed process 12345 (postgres).
这意味着 postgres 进程因内存压力而终止。现有数据库连接将继续正常运行,但不会接受新连接。为了恢复,需要重新启动 PostgreSQL。
避免这个问题的一种方法是在一台机器上运行 PostgreSQL,你可以在该机器上确保其他进程不会使机器耗尽内存。如果内存紧张,增加操作系统的交换空间可以帮助避免这个问题,因为只有在物理内存和交换空间耗尽时,才会调用内存不足 (OOM) 终止程序。
如果 PostgreSQL 本身是系统用尽内存的原因,则可以更改配置来避免此问题。在某些情况下,它可能有助于降低与内存相关的配置参数,尤其是 shared_buffers 、 work_mem 和 hash_mem_multiplier 。在其他情况下,问题可能是允许太多的连接到数据库服务器本身。在很多情况下,也许最好是减少 max_connections ,而改为使用外部连接池软件。
可以通过修改内核行为来使其不会“过度提交”内存。虽然此设置并不能彻底防止调用 OOM killer,但它会降低调用的频率,从而带来更稳健的系统行为。可以通过_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 时使用,是将后处理程序的进程特定的 OOM score adjustment 值设置为 -1000,从而保证它不会成为 OOM 终止的目标。执行此操作的最简单方法是在调用 postgres 之前在 PostgreSQL 启动脚本中执行
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
这些设置将导致 postmaster 子进程以常规 OOM 分数调整零运行,以便 OOM 终止器仍然可以在需要时将其作为目标。如果您希望子进程以其他一些 OOM 分数调整运行,则可以使用 PG_OOM_ADJUST_VALUE 的其他一些值。(也可以省略 PG_OOM_ADJUST_VALUE,在这种情况下,它默认为零。)如果您没有设置 PG_OOM_ADJUST_FILE,则子进程将以与 postmaster 相同的 OOM 分数调整运行,这是不明智的,因为其重点是确保 postmaster 具有首选设置。
19.4.5. Linux Huge Pages #
使用大页面可减少在使用大块连续内存(例如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。