16.4. 管理内核资源

一次大型PostgreSQL安装会很容易耗尽各种操作系统的资源上限。 (在有些系统上,出厂设置低得你都不用一次"大型"安装。) 如果你碰到这类问题,请继续阅读。

16.4.1. 共享内存和信号灯

共享内存和信号灯的正确叫法是"System VIPC" (还有消息队列,不过对于PostgreSQL而言没什么关系。) 尽管所有现代操作系统都提供这个特性,但并不是所有系统缺省都打开它或者有足够的资源,尤其是有 BSD 亲源的系统。 (对于 WindowsQNXBeOS 移植,PostgreSQL自己提供这套机制的替换实现。)

完全缺少这些机制的表现通常是在服务器启动的时候的 Illegal system call错误。 这时除了重新配置内核以外没什么可做的。 PostgreSQL 没它们干不了活。

如果 PostgreSQL 超出了这些IPC 资源的硬限制之一的时候就会拒绝启动并且留下一条相当有启发性的错误信息告诉你它碰到了什么问题以及需要为它做些什么。 (又见 Section 16.3.1。) 相关的内核参数在不同系统之间有着相对固定的术语;Table 16-1 是一个概况。 不过,设置它们的方法却多种多样。不过要注意的是,你可能最好重新启动你的机器,或者还要重新编译内核来修改这些设置。

Table 16-1. System V IPC参数

名字描述合理取值
SHMMAX最大共享内存段尺寸(字节)最少若干兆(见文本)
SHMMIN最小共享内存段尺寸(字节)1
SHMALL可用共享内存的总数量(字节或者页面)如果是字节,就和 SHMMAX 一样;如果是页面,ceil(SHMMAX/PAGE_SIZE)
SHMSEG每进程最大共享内存段数量只需要 1 个段,不过缺省比这高得多。
SHMMNI系统范围最大共享内存段数量类似 SHMSEG 加上用于其他应用的空间
SEMMNI信号灯标识符的最小数量(也就是说,套)至少ceil(max_connections / 16)
SEMMNS系统范围的最大信号灯数量ceil(max_connections / 16) * 17加上用于其他应用的空间
SEMMSL每套信号灯最小信号灯数量至少 17
SEMMAP信号灯映射里的记录数量参阅文本
SEMVMX信号灯的最大值至少 1000 (缺省通常是32767,除非被迫,否则不要修改)

最重要的共享内存参数是 SHMMAX, 以字节记的共享内存段可拥有的最大尺寸。 如果你收到来自shmget的类似Invalid argument 这样的错误信息,那么很有可能是你超过限制了。 要求的共享内存段数量根若干个 PostgreSQL 配置参数相关, 如 Table 16-2 所示。 因此,作为一种临时的解决方法,你可以降低这些设置来绕过失败。 如果粗略地估计,你可以估计所需要的段尺寸是 500kB 加上表里面显示的变量要求的数量。 (任何你得到的错误信息都会包含分配失败的尺寸。) 尽管我们可以在 SHMMAX 小到只有 1 MB 的情况下运行, 你还是需要至少 4MB 才能有起码的性能,而比较合理的设置是以数十兆计的。

有些系统对系统里面共享内存的总数(SHMALL)还有限制。 请注意这个数值必须足够大,大到PostgreSQL 加上其它使用共享内存段的应用的总和。 (注意:SHMALL 在很多系统上是用页面数,而不是字节数来计算的。)

不太可能出问题的是共享内存段的最小尺寸(SHMMIN), 对 PostgreSQL来说大约是 500 kB 左右(通常只是 1), 而系统范围(SHMMNI)或每进程(SHMSEG) 最大共享内存段数量不应该会产生问题,除非你的系统把它们设成零。

PostgreSQL 每个允许的联接使用一个信号灯(max_connections), 以 16 个为一套。每套信号灯还包含第十七个信号灯, 它里面存储一个"magic number(标志数字)", 以检测和其他应用使用的信号灯集冲突。 系统里的最大信号灯数目是由SEMMNS设置的, 因此这个值应该至少和 max_connections 设置一样大,并且每十六个联接还要另外加一个。 (参阅Table 16-1 里面的公式。) 参数SEMMNI决定系统里一次可以存在的信号灯集的数目。 因此这个参数至少应该为 ceil(max_connections % 16)。 降低允许的联接数目是一个临时的绕开失败的方法,这个启动失败通常被来自函数semget 的错误响应 No space left on device 搞得很让人迷惑。

有时候还可能有必要增大SEMMAP,使之至少按照 SEMMNS配置。这个参数定义信号灯资源映射的尺寸, 可用的每个连续的信号灯块在这个映射中存放一条记录。 每当一套信号灯被释放,那么它要么会加入到该映射中一条相连的已释放的块的入口中,要么注册成一条新的入口。如果映射填满了碎片, 那么被释放的信号灯就丢失了(除非重起)。因此时间长信号灯空间的碎片了会导致可用的信号灯比应该有的信号灯少。

SEMMSL 参数,决定一套信号灯里可以有多少信号灯,对于 PostgreSQL而言应该至少是 17。

许多设置与 "semaphore undo(信号灯恢复)"有关,比如 SEMMNUSEMUME,这些与 PostgreSQL 无关。

BSD/OS

共享内存. 缺省时是只支持 4 MB 的共享内存。请记住共享内存是不能分页的;它是锁在 RAM 里面的。 要增加你的系统支持的共享缓冲区数目,向你的内核配置文件里增加下面的行:

options "SHMALL=8192"
options "SHMMAX=\(SHMALL*PAGE_SIZE\)"

SHMALL 以 4KB 页为单位计算,所以 1024 页面代表 4 M 共享内存。 所以上面的东西把共享内存区域增加到 32 MB。 对于运行 4.3 或者更新版本的人,你可能需要增大 KERNEL_VIRTUAL_MB, 超过缺省的 248。做完上面的修改之后,然后编译内核并重起。

对于运行 4.0 或者更早的版本的, 请用 bpatch 找出当前内核的 sysptsize值。它是启动的时候动态计算的。

$ bpatch -r sysptsize
0x9 = 9

然后,把 SYSPTSIZE修改为在内核配置文件里的一个硬代码值。 用 bpatch 算出来的值,并且为你需要的每个额外的 4 MB 共享内存再加 1。

options "SYSPTSIZE=16"

sysptsize不能用 sysctl 修改。

信号灯. 你可能还需要增加信号灯的数量;系统缺省的总数 60 只能允许大概 50 个 PostgreSQL 连接。 在内核配置文件里设置你需要的值,比如:

options "SEMMNI=40"
options "SEMMNS=240"

FreeBSD

缺省设置只适合于很小的安装(比如,缺省 SHMMAX 是 32 MB)。 我们可以用 sysctl 或者 loader 接口来修改。 下面的参数可以用 sysctl 设置:

$ sysctl -w kern.ipc.shmall=32768
$ sysctl -w kern.ipc.shmmax=134217728
$ sysctl -w kern.ipc.semmap=256

要想让这些设置重启后有效,修改 /etc/sysctl.conf

如果用 sysctl,那么剩下的信号灯设置是只读的, 但是信号灯可以在启动的时候,在 loader 提示符下设置:

(loader) set kern.ipc.semmni=256
(loader) set kern.ipc.semmns=512
(loader) set kern.ipc.semmnu=256

类似的,这些东西可以在 /boot/loader.conf 中保存, 以便重启之后依然有效。

你可能还想配置内核,让把共享内存装载到 RAM 里,避免他们被交换到交换分区中。 这些可以通过使用 sysctl 设置 kern.ipc.shm_use_phys 来实现。

在 4.0 之前的 FreeBSD 版本类似 NetBSD OpenBSD (见下文)。

NetBSD
OpenBSD

编译内核时需要把选项 SYSVSHMSYSVSEM打开。 (缺省是打开的。)共享内存的最大尺寸是由选项SHMMAXPGS(以页计)。 下面显示了一个如何设置这些参数的例子(OpenBSD 使用的是 option):

options         SYSVSHM
options         SHMMAXPGS=4096
options         SHMSEG=256

options         SYSVSEM
options         SEMMNI=256
options         SEMMNS=512
options         SEMMNU=256
options         SEMMAP=256

你可能原意使用 sysctl 设置将共享内存锁在 RAM 中以避免它们被交换出去, 我们可以用 sysctl 设置 kern.ipc.shm_use_phys 来实现这个目的。

HP-UX

缺省设置看来对普通安装是足够的了。 在 HP-UX 10,SEMMNS的出厂缺省是 128, 可能对大的数据库节点来说太小了。

IPC可以在 System Administration Manager(系统管理器)SAM)下面的 Kernel Configuration->Configurable Parameters 配置。你配置完了以后敲 Create A New Kernel选项。

Linux

缺省设置只适合小安装(缺省最大共享内存是 32 MB)。不过,其它的缺省值都相当大, 通常不需要改变。最大的共享内存段设置可以用 sysctl 接口设置。 比如,要允许 128 MB,并且最大的总共享内存数为 2097152 页(缺省):

$ sysctl -w kernel.shmmax=134217728
$ sysctl -w kernel.shmall=2097152

你可以把这些设置放到 /etc/sysctl.conf 里,在重启后保持有效。

老版本里可能没有 sysctl 程序, 但是同样的改变可以通过操作 /proc 文件系统来做:

$ echo 134217728 >/proc/sys/kernel/shmmax
$ echo 2097152 >/proc/sys/kernel/shmall

MacOS X

在 OS X 10.2 以及更造版本里, 编辑文件 /System/Library/StartupItems/SystemTuning/SystemTuning 并且用下列命令修改这些数值:

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

在 OS X 10.3 及以后的版本里,这些命令移动到 /etc/rc 里面去了,必须在那里编辑。 你需要重新启动才能让设置生效。请注意 /etc/rc 通常会被 OS X 更新覆盖 (比如 10.3.6 到 10.3.7),所以每次更新后你可能都需要重新编辑。

在这个平台上,SHMALL 是用 4KB 页来度量的。 还要注意有些版本的 OS X 将拒绝把 SHMMAX 的数值数值为非 4096 的倍数。

SCO OpenServer

缺省配置时,只允许每段 512KB 共享内存。 要增大设置,首先进入 /etc/conf/cf.d目录。 要显示当前的以字节记的 SHMMAX,运行

./configure -y SHMMAX

设置 SHMMAX的新值:

./configure SHMMAX=value

这里 value 是你想设置的以字节记的新值。 设置完了以后SHMMAX重新制作内核

./link_unix

然后重起。

AIX

至少对于版本 5.1 而言,我们有必要为类似 SHMMAX 这样的参数做特殊的配置, 因为这个参数可以配置为所有内容都当作共享内存使用。这就是类似 DB/2 这样的数据库常用的配置。

不过,我们可能有必要在 /etc/security/limits 里面修改全局 ulimit ulimit 信息,因为文件大小的缺省硬限制(fsize)以及文件数(nofiles)可能太低了。

Solaris

至少到版本 2.6 为止,共享内存段的缺省最大设置对 PostgreSQL 来说是太低了。相关的设置可以在/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 里面的共享内存的信息。

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

然后重起。

Table 16-2. 影响 PostgreSQL 的内存使用的配置参数

名称近似倍率(每次增加的字节数)
max_connections400 + 220 * max_locks_per_transaction
max_prepared_transactions600 + 220 * max_locks_per_transaction
shared_buffers8300 (假设 8K 的BLCKSZ
wal_buffers8200 (假设 8K 的BLCKSZ
max_fsm_relations70
max_fsm_pages6

16.4.2. 资源限制

Unix 类系统强制了许多资源限制,这些限制可能干扰你的 PostgreSQL 服务器的运行。 这里尤其重要是对每个用户的进程数目的限制,每个进程打开文件数目, 以及每个进程可用的内存。 这些限制中每个都有一个"硬"限制和一个"软"限制。 软限制实际是管用的,但用户可以自己修改成最大为硬限制的数目。 而硬限制是只能由 root 用户修改的限制。 系统调用 setrlimit 负责设置这些参数。 shell 的内建命令 ulimit(Bourne shells) 或limitcsh) 就是用于在命令行上控制资源限制的。 在 BSD 衍生的系统上,文件/etc/login.conf 控制在登录时对各种资源设置什么样的限制数值。参阅操作系统文档获取细节。 相关的参数是 maxprocopenfiles,和 datasize。 比如:

default:\
...
        :datasize-cur=256M:\
        :maxproc-cur=256:\
        :openfiles-cur=256:\
...

-cur 是软限制,后面附加 -max 就可以设置硬限制。)

内核通常也有一些系统范围的资源限制。

PostgreSQL 服务器每个联接都使用一个进程, 所以你应该至少允许和联接数相同的进程数,再加上你的系统其它部分所需要的数目。 通常这个并不是什么问题,但如果你在一台机器上运行多个服务器,那你就要把事情理清楚。

打开文件数目的出厂缺省设置通常设置为"社会友好"数值,就是说允许许多用户共存于一台机器, 而不会导致只使用系统资源的不当比例。如果你在一台机器上运行许多服务器,这也许就是你想要的,但是在特殊的服务器上,你可能需要提高这个限制。

问题的另外一边,一些系统允许独立的进程打开非常多的文件;如果有那么几个进程这么干,那系统范围的上限就很容易达到。 如果你发现这样的现象,并且不想修改系统范围的限制, 你就可以把 PostgreSQLmax_files_per_process 配置参数来限制打开文件数的消耗。

16.4.3. Linux 内存过提交

在 Linux 2.4 以及之后的版本里,缺省的虚拟内存的行为不是对 PostgreSQL 最优的。 原因事内核实现内存过提交的方法,如果其它进程的内存请求导致系统用光虚拟内存, 那么内核可能会终止 PostgreSQL 服务器(postmaster进程)。

如果发生了这样的事情,你会看到想下面这样的内核信息(参考你的系统文档和配置,看看在哪里能看到这样的信息):

Out of Memory: Killed process 12345 (postmaster).

这就表明 postmaster 因为内存压力而终止了。 尽管现有的数据连接将继续正常运转,但是新的连接将无法接受。 要想恢复,你应该重启 PostgreSQL

一个避免这个问题的方法是在一台你确信不会因为其它进程而耗尽内存的机器上运行 PostgreSQL

在 Linux 2.6 以及以后的版本里,一个更好的解决方法是修改内存的行为, 这样它就不会再"过提交"内存。这是通过用 sysctl 选取一个严格的过提交模式实现的:

sysctl -w vm.overcommit_memory=2

或者在 /etc/sysctl.conf 里放一个等效的条目。 你可能还希望修改相关的设置 vm.overcommit_ratio。 详细信息请参阅内核文档文件 Documentation/vm/overcommit-accounting

有些供应商的 Linux 2.4 内核有着早期 2.6 过提交的 sysctl。 不过,在没有相关代码的内核里设置 vm.overcommit_memory 为 2 只会让事情更糟,而不是更好。 我们建议你检查一下实际的内核源代码(参阅文件 mm/mmap.c 里面的 vm_enough_memory 函数), 核实一下这个是在你的版本里存在的,然后再在 2.4 内核里使用这个特性。 文档文件 overcommit-accounting 的存在能当作是这个特性存在的证明。 如果有问题,请询问你的内核供应商的专家。