17.4. 管理内核资源 #
PostgreSQL有时会达到操作系统的各种资源上限,尤其是在同一系统上运行多个服务器实例,或在非常大型的安装环境中时更是如此。本节解释PostgreSQL使用的内核资源,以及你可以采取哪些步骤来解决与内核资源消耗相关的问题。
17.4.1. 共享内存和信号量 #
共享内存和信号量统称为“System V IPC”(连同消息队列,但消息队列与PostgreSQL无关)。除了在Windows上——PostgreSQL在那里为这些设施提供了自己的替代实现——之外,运行PostgreSQL需要这些设施。
完全缺少这些功能时,通常会在服务器启动时出现 Illegal system call 错误。在这种情况下,除了重新配置内核别无选择。没有它们,PostgreSQL 就无法工作。不过,在现代操作系统中,这种情况很少见。
当PostgreSQL超出各种硬性IPC限制之一时,服务器会拒绝启动,并应留下带有指导性的错误消息,说明问题所在以及应如何处理(另见第 17.3.1 节)。相关内核参数在不同系统上的命名基本一致,表 17.1给出了概览;不过,设置它们的方法却各不相同。下面给出一些平台上的建议。
注意
在 PostgreSQL 9.3 之前,启动服务器所需的 System V 共享内存量大得多。如果你运行的是较旧版本的服务器,请查阅相应服务器版本的文档。
表 17.1. System V IPC参数
| 名称 | 描述 | 合理的值 |
|---|---|---|
SHMMAX | 共享内存段的最大尺寸(字节) | 至少 1kB(如果运行多个服务器副本则需更多) |
SHMMIN | 共享内存段的最小尺寸(字节) | 1 |
SHMALL | 可用共享内存的总量(字节或页面) | 如果是字节,同SHMMAX;如果是页面,为ceil(SHMMAX/PAGE_SIZE) |
SHMSEG | 每个进程的最大共享内存段数目 | 只需要 1 段,但是默认值高很多 |
SHMMNI | 系统范围内的最大共享内存段数目 | 像SHMSEG外加其他应用的空间 |
SEMMNI | 信号量标识符(即,集合)的最大数目 | 至少 ceil((max_connections + autovacuum_max_workers + 后台进程数 + 5) / 16) |
SEMMNS | 系统范围内的最大信号量数目 | ceil((max_connections + autovacuum_max_workers + 后台进程数 + 5) / 16) * 17,并为其他应用保留空间 |
SEMMSL | 每个集合中信号量的最大数目 | 至少 17 |
SEMMAP | 信号量映射中的项数 | 见文本 |
SEMVMX | 信号量的最大值 | 至少 1000(默认值常常是 32767,如非必要不要更改) |
PostgreSQL 的每个服务器实例都需要少量 System V 共享内存(在 64 位平台上通常为 48 字节)。大多数现代操作系统都能轻松分配这个数量。但是,如果运行许多服务器实例,或者其他应用程序也在使用 System V 共享内存,可能需要增大 SHMMAX(共享内存段的最大字节数)或 SHMALL(系统范围内 System V 共享内存的总量)。注意,在许多系统上,SHMALL 以页而非字节为单位。
不太可能出问题的是共享内存段的最小尺寸(SHMMIN),对PostgreSQL来说应该最多大约是 32 字节(通常只是1)。而系统范围(SHMMNI)或每个进程(SHMSEG)的最大共享内存段数目不太可能会导致问题,除非你的系统把它们设成零。
PostgreSQL 为每个允许的连接(max_connections)、自动清理工作进程(autovacuum_max_workers)以及请求共享内存访问的后台进程各使用一个信号量,每 16 个组成一组。每组还包含第 17 个信号量,其中存放一个“魔数”,用来检测与其他应用程序使用的信号量集的冲突。系统中的信号量最大数量由 SEMMNS 设置,因此它必须至少等于 max_connections 和 autovacuum_max_workers 与后台进程数之和,再为每 16 个允许的连接、工作进程和后台进程额外增加一个信号量(参见表 17.1中的公式)。参数 SEMMNI 限制系统中同时存在的信号量集的数量。因此,此参数必须至少为 ceil((max_connections + autovacuum_max_workers + 后台进程数 + 5) / 16)。降低允许的连接数,可以临时规避 semget 函数的失败;此类失败通常会报告令人困惑的 “No space left on device”。
在某些情况下,可能还需要增大SEMMAP,使其至少与SEMMNS处于同一数量级。如果系统提供这个参数(很多系统没有),它定义的是信号量资源映射的大小;映射中每个连续的可用信号量块都需要占用一项。每当一个信号量集合被释放时,它要么会并入与该释放块相邻的现有项,要么会登记为一个新的映射项。如果映射已满,被释放的信号量就会丢失(直到重启)。因此,随着时间推移,信号量空间碎片化可能会导致可用信号量少于应有数量。
SEMMSL 参数决定一个信号量集中可以包含多少个信号量,对于PostgreSQL,它必须至少为 17。
与“信号量撤销”有关的其他各种设置,如SEMMNU和SEMUME,不会影响PostgreSQL。
- AIX
至少从 5.1 版起,应该不需要对
SHMMAX等参数进行任何特殊配置,因为它似乎已配置为允许将全部内存用作共享内存。这也是 DB/2 等其他数据库常用的配置。不过,可能需要修改
/etc/security/limits中的全局ulimit信息,因为文件大小(fsize)和文件数量(nofiles)的默认硬限制可能过低。- FreeBSD
可以使用
sysctl或loader接口更改默认 IPC 配置。下列参数可用sysctl设置:#sysctl kern.ipc.shmall=32768#sysctl kern.ipc.shmmax=134217728要让这些设置在重启之后也保持,请修改
/etc/sysctl.conf。这些信号量相关设置对于
sysctl是只读的;设置它们时,可修改/boot/loader.conf:kern.ipc.semmni=256 kern.ipc.semmns=512 kern.ipc.semmnu=256
修改这些值后需要重启才能使新设置生效。(注意:FreeBSD 不使用
SEMMAP。旧版本会接受但忽略kern.ipc.semmap的设置;新版本则完全拒绝它。)你可能还希望配置内核,把共享内存锁定在 RAM 中,防止其被换出到交换区。这可以通过
sysctl设置kern.ipc.shm_use_phys来实现。如果通过启用 sysctl 的
security.jail.sysvipc_allowed在 FreeBSD jail 中运行,应让不同 jail 中的 postmaster 使用不同的操作系统用户。这能提高安全性,因为它可以防止非 root 用户干扰不同 jail 中的共享内存或信号量,也能让 PostgreSQL 的 IPC 清理代码正常工作。(在 FreeBSD 6.0 及更高版本中,IPC 清理代码无法正确检测其他 jail 中的进程,导致无法在不同 jail 中使用同一端口运行 postmaster。)4.0 之前的 FreeBSD 版本与旧版 OpenBSD 的行为相同(见下文)。
- NetBSD
在 NetBSD 5.0 及更高版本中,可以通过以下命令调整 IPC 参数:
sysctl。例如:$sysctl -w kern.ipc.shmmax=16777216要让这些设置在重启后仍然生效,请修改
/etc/sysctl.conf。你可能还希望配置内核,把共享内存锁定在 RAM 中,防止其被换出到交换区。这可以通过
sysctl设置kern.ipc.shm_use_phys来实现。5.0 之前的 NetBSD 版本与旧版 OpenBSD 的行为相同(见下文),但设置内核参数时应使用关键字
options,而非option。- OpenBSD
编译内核时需要启用选项
SYSVSHM和SYSVSEM(默认启用)。共享内存的最大尺寸由选项SHMMAXPGS(以页计)决定。下面展示了如何设置各种参数的一个示例:option SYSVSHM option SHMMAXPGS=4096 option SHMSEG=256 option SYSVSEM option SEMMNI=256 option SEMMNS=512 option SEMMNU=256
- HP-UX
默认设置通常足以满足普通安装的需求。在 HP-UX 10 上,
SEMMNS的出厂默认值为 128,对较大的数据库站点来说可能过低。可以在 System Administration Manager(SAM)的 → 中设置 IPC 参数。完成后选择 。
- Linux
默认最大段大小为 32 MB,默认最大总大小为 2097152 页。除使用“大页”的特殊内核配置外,页大小几乎总是 4096 字节(使用
getconf PAGE_SIZE验证)。可以通过以下接口更改共享内存大小设置:
sysctl。例如,要允许 16 GB:$sysctl -w kernel.shmmax=17179869184$sysctl -w kernel.shmall=4194304此外,可以将这些设置保存在以下文件中,使其在重启后仍然生效:
/etc/sysctl.conf。强烈建议这样做。很旧的发行版可能没有
sysctl程序,但可以通过操作/proc文件系统进行等效更改:$echo 17179869184 >/proc/sys/kernel/shmmax$echo 4194304 >/proc/sys/kernel/shmall其他默认值都相当充裕,通常不需要更改。
- OS X
在 OS X 中配置共享内存的推荐方法是创建一个名为
/etc/sysctl.conf的文件,其中包含这样的变量赋值:kern.sysv.shmmax=4194304 kern.sysv.shmmin=1 kern.sysv.shmmni=32 kern.sysv.shmseg=8 kern.sysv.shmall=1024
注意,在某些 OS X 版本中,全部五个共享内存参数都必须在
/etc/sysctl.conf中设置,否则这些值会被忽略。请注意,近期 OS X 版本会忽略将
SHMMAX设为非 4096 整数倍的值的尝试。在这个平台上,
SHMALL以 4 kB 的页为单位。在较旧的 OS X 版本中,必须重启才能使共享内存参数的变更生效。从 10.5 起,除了
SHMMNI,都可以使用 sysctl 在线更改。但最好仍通过/etc/sysctl.conf设置所需的值,以便重启后保留这些值。文件
/etc/sysctl.conf只在 OS X 10.3.9 及更高版本中生效。如果运行的是更早的 10.3.x 版本,必须编辑文件/etc/rc并更改以下命令中的值:sysctl -w kern.sysv.shmmax sysctl -w kern.sysv.shmmin sysctl -w kern.sysv.shmmni sysctl -w kern.sysv.shmseg sysctl -w kern.sysv.shmall
注意,
/etc/rc通常会被 OS X 系统更新覆盖,因此每次更新后可能都需要重新进行这些编辑。在 OS X 10.2 及更早版本中,请改为编辑
/System/Library/StartupItems/SystemTuning/SystemTuning文件中的这些命令。- SCO OpenServer
在默认配置中,每个共享内存段只允许 512 kB。要提高此设置,首先切换到目录
/etc/conf/cf.d。要显示SHMMAX的当前值,运行:./configure -y SHMMAX
要为
SHMMAX设置新值,运行:./configure SHMMAX=
value其中
value是你要使用的新值(以字节为单位)。设置SHMMAX后,重新构建内核:./link_unix
然后重启。
- Solaris 2.6 至 2.9(Solaris 6 至 Solaris 9)
可以在以下文件中更改相关设置:
/etc/system。例如:set shmsys:shminfo_shmmax=0x2000000 set shmsys:shminfo_shmmin=1 set shmsys:shminfo_shmmni=256 set shmsys:shminfo_shmseg=256 set semsys:seminfo_semmap=256 set semsys:seminfo_semmni=512 set semsys:seminfo_semmns=512 set semsys:seminfo_semmsl=32
需要重启才能使变更生效。另请参见http://sunsite.uakom.sk/sunworldonline/swol-09-1997/swol-09-insidesolaris.html,了解旧版 Solaris 中共享内存的信息。
- Solaris 2.10(Solaris 10)及更高版本
OpenSolaris 在 Solaris 10 及更高版本和 OpenSolaris 中,默认共享内存和信号量设置足以满足大多数 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的更多信息,请参见System Administrator's Guide中的 "Chapter 2: Projects and Tasks"。- UnixWare
在 UnixWare 7 上,默认配置中共享内存段的最大尺寸为 512 kB。要显示
SHMMAX的当前值,运行:/etc/conf/bin/idtune -g SHMMAX
这会显示当前值、默认值、最小值和最大值。要为
SHMMAX设置新值,运行:/etc/conf/bin/idtune SHMMAX
value其中
value是你要使用的新值(以字节为单位)。设置SHMMAX后,重新构建内核:/etc/conf/bin/idbuild -B
然后重启。
17.4.2. systemd RemoveIPC #
如果使用systemd,必须注意不要让操作系统过早移除 IPC 资源(共享内存和信号量)。这在从源码安装 PostgreSQL 时尤其值得关注。使用发行版软件包的用户较不容易受到影响,因为此时postgres用户通常会被创建为系统用户。
logind.conf 中的 RemoveIPC 设置控制当用户完全注销时是否移除 IPC 对象。系统用户不受此限制。原版 systemd 默认开启该设置,但某些操作系统发行版默认将其关闭。
当此设置开启时,一个常见现象是,PostgreSQL 服务器使用的信号量对象会在看似随机的时间被删除,导致服务器崩溃并在日志中留下类似下面的消息:
LOG: semctl(1234567890, 0, IPC_RMID, ...) failed: Invalid argument
不同类型的 IPC 对象(共享内存与信号量、System V 与 POSIX)在 systemd 中的处理略有不同,因此你可能会发现某些 IPC 资源不会像其他资源那样被删除。但依赖这些细微差别并不可取。
“用户注销”可能会作为维护作业的一部分发生,也可能在管理员以postgres用户登录或执行类似操作时手工触发,因此通常很难彻底防止。
什么算作“系统用户”,是在编译 systemd 时依据 /etc/login.defs 中的 SYS_UID_MAX 设置确定的。
打包和部署脚本应当谨慎地使用useradd -r、adduser --system或其等价方式,把postgres用户创建为系统用户。
或者,如果该用户账户创建得不正确,或无法更改,建议在/etc/systemd/logind.conf或其他适当的配置文件中设置
RemoveIPC=no
小心
上述两件事至少要确保做到其中之一,否则 PostgreSQL 服务器会变得非常不可靠。
17.4.3. 资源限制
类 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上,内核参数
/proc/sys/fs/file-max确定内核支持的最大打开文件数。可以向该文件写入一个不同的数字来修改它,也可以在/etc/sysctl.conf中添加相应赋值。每个进程的文件数上限是在内核编译时固定的;请参阅/usr/src/linux/Documentation/proc.txt获取更多信息。
PostgreSQL服务器为每个连接使用一个进程,因此你至少应提供与允许连接数相同数量的进程,再加上系统其他部分所需的进程数。通常这不是问题,但如果你在一台机器上运行多个服务器,资源可能就会变得紧张。
打开文件数的出厂默认限制通常被设为“对共享环境友好”的值,也就是允许许多用户在一台机器上共存,而不会占用不成比例的系统资源。如果你在一台机器上运行很多服务器,这也许正合适;但在专用服务器上,你可能会希望提高这个限制。
另一方面,有些系统允许单个进程打开非常多的文件;如果不止少数几个进程都这么做,就很容易超过系统范围的限制。如果你遇到这种情况,又不想修改系统范围的限制,可以设置PostgreSQL的max_files_per_process配置参数来限制其打开文件的消耗。
17.4.4. Linux 内存过量分配 #
在 Linux 2.4 及更高版本中,默认的虚拟内存行为对 PostgreSQL 并非最佳。由于内核实现内存过量分配的方式,如果 PostgreSQL 或其他进程的内存需求导致系统耗尽虚拟内存,内核可能会终止 PostgreSQL 的 postmaster(主管服务器进程)。
如果发生这种情况,你会看到类似下面的内核消息(至于应到哪里查看此类消息,请参阅你的系统文档和配置):
Out of Memory: Killed process 12345 (postgres).
这表示 postgres 进程因内存压力而被终止。尽管现有数据库连接仍会继续正常运行,但新连接将不再被接受。要恢复服务,必须重启PostgreSQL。
避免该问题的一种办法,是让PostgreSQL运行在一台你能确定不会被其他进程耗尽内存的机器上。如果内存紧张,增加操作系统交换空间也有助于避免这个问题,因为内存不足(OOM)杀手只有在物理内存和交换空间都耗尽时才会被触发。
如果导致系统耗尽内存的正是 PostgreSQL 自身,那么你可以通过调整配置来避免该问题。在某些情况下,调低与内存相关的配置参数会有帮助,尤其是 shared_buffers 和 work_mem。在其他情况下,允许数据库服务器接受过多连接也可能导致这一问题。很多时候,更好的做法可能是减小 max_connections,并转而使用外部连接池软件。
在 Linux 2.6 及更高版本中,可以修改内核的行为,使其不会“过量分配”内存。虽然此设置无法完全阻止 OOM 杀手被调用,但会显著降低发生概率,从而使系统行为更健壮。方法是选择严格的过量分配模式,使用以下命令:sysctl:
sysctl -w vm.overcommit_memory=2
或者在以下文件中加入等效条目:/etc/sysctl.conf。你可能还希望修改相关设置 vm.overcommit_ratio。详细信息参见内核文档文件 Documentation/vm/overcommit-accounting。
另一种方法可在修改或不修改 vm.overcommit_memory 的情况下使用:把 postmaster 进程专属的oom_score_adj值设为 -1000,从而保证它不会成为 OOM 杀手的目标。最简单的做法是在 postmaster
启动脚本中、调用 postmaster 之前执行:
echo -1000 > /proc/self/oom_score_adj
请注意,这个操作必须以 root 身份完成,否则不会生效;因此,由 root 拥有的启动脚本是最容易执行该操作的位置。如果这样做,还可以在构建 PostgreSQL 时向 CPPFLAGS 添加
-DLINUX_OOM_SCORE_ADJ=0。这会使 postmaster 子进程以常规的
oom_score_adj值零运行,以便 OOM 杀手在需要时仍可将它们作为目标。
较旧的 Linux 内核不提供 /proc/self/oom_score_adj,但可能提供名为 /proc/self/oom_adj 的同类功能的早期版本。其工作方式相同,只是禁用值为 -17 而非 -1000。PostgreSQL相应的构建标志是 -DLINUX_OOM_ADJ=0。
注意
据报告,某些厂商的 Linux 2.4 内核包含 2.6 版过量分配 sysctl 参数的早期实现。但是,在没有相关代码的 2.4 内核上将 vm.overcommit_memory 设为 2,会使情况更糟,而非更好。建议在 2.4 安装上尝试之前,检查实际内核源码(参见文件 mm/mmap.c 中的函数 vm_enough_memory),确认内核支持的功能。存在 overcommit-accounting 文档文件,不能作为该功能已存在的证据。如有任何疑问,请咨询内核专家或内核供应商。