17.4. 管理内核资源

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

17.4.1. 共享内存和信号灯

共享内存和信号灯的正确叫法是"SystemV IPC"(还有消息队列,不过与PostgreSQL无关)。 尽管所有现代操作系统都提供这些特性,但并不是所有系统缺省都打开它或者有足够的资源, 尤其是可用的RAM和数据库应用的需求增长。 (对于Windows移植,PostgreSQL自己提供这套机制的替换实现, 所以大多数本条可以忽略不计。)

完全缺少这些机制的表现通常是在服务器启动时的Illegalsystemcall 错误。这时除了重新配置内核以外没什么可做的。PostgreSQL没它们不工作。 然而,在现代的操作系统,这种情况是罕见的。

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

Table 17-1. SystemVIPC参数

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

最重要的共享内存参数是SHMMAX(以字节记的共享内存段可拥有的最大尺寸)。 如果你收到来自shmget的类似"Inval idargument"这样的错误信息, 那么很有可能是你超过限制了。要求的共享内存段数量与若干个 PostgreSQL配置参数相关,如Table 17-2所示。 (任何错误消息,你可能会包括分配请求失败的确切大小。) 因此,作为一种临时的解决方法,你可以降低这些设置来绕过失败。 虽然它有可能获得PostgreSQL运行2MB的SHMMAX的, 你需要相当可接受的性能。可取设置在数百兆到几千兆字节。

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

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

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

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

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

许多设置与"semaphoreundo"(信号灯恢复)有关, 比如SEMMNUSEMUME,这些不影响PostgreSQL

AIX

Atleastasofversion5.1,itshouldnotbenecessarytodo anyspecialconfigurationforsuchparametersas SHMMAX,asitappearsthisisconfiguredto allowallmemorytobeusedassharedmemory.Thatisthe sortofconfigurationcommonlyusedforotherdatabasessuch asDB/2.

Itmight,however,benecessarytomodifytheglobal ulimitinformationin /etc/security/limits,asthedefaulthard limitsforfilesizes(fsize)andnumbersof files(nofiles)mightbetoolow.

BSD/OS

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

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

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

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

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

FreeBSD

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

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

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

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

(loader)setkern.ipc.semmni=256
(loader)setkern.ipc.semmns=512
(loader)setkern.ipc.semmnu=256

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

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

如果通过启用sysctlsecurity.jail.sysvipc_allowed 运行在FreeBSDjail中,那么必须将postmaster 以不同操作系统的用户身份运行在不同的jail中。这样有助于增强安全性, 因为它防止了非root用户干扰不同jail中的共享内存或信号灯, 并且允许PostgreSQLIPC清理代码功能。在FreeBSD6.0及之后的版本中, IPC清理代码并不能正确侦测在其它jail中的进程,因此无法防止其它 jail中的postmaster进程占用相同的端口。

FreeBSD4.0之前的版本类似NetBSDOpenBSD(见下文)。

NetBSD
OpenBSD

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

optionsSYSVSHM
optionsSHMMAXPGS=4096
optionsSHMSEG=256

optionsSYSVSEM
optionsSEMMNI=256
optionsSEMMNS=512
optionsSEMMNU=256
optionsSEMMAP=256

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

HP-UX

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

IPC可以在System AdministrationManager(SAM)下面的Kernel Configuration->ConfigurableParameters配置。 配置完了以后选择CreateANewKernel选项。

Linux

缺省最大段是32MB,对于仅够很小PostgreSQL安装。 1页总是4096字节除了在非通常内核配置使用了"hugepages"( 使用getconfPAGE_SIZE检验)。缺省限制8GB,这个经常是足够的,不是总是这样。 )

通过sysctl接口修改设置的共享内存大小。比如,允许到16GB:

$sysctl-wkernel.shmmax=17179869184
$sysctl-wkernel.shmall=4194304

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

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

$echo17179869184>/proc/sys/kernel/shmmax
$echo4194304>/proc/sys/kernel/shmall

其余的默认值是相当够用的大小,通常不需要更改。

MacOSX

在OSX10.3及以后的版本里,可以创建一个名为/etc/sysctl.conf的文件, 包含如下变量及相应的值:

kern.sysv.shmmax=4194304
kern.sysv.shmmin=1
kern.sysv.shmmni=32
kern.sysv.shmseg=8
kern.sysv.shmall=1024

注意,在版本的OSX里所有五个共享内存参数必须都在 /etc/sysctl.conf中设置,否则将会被忽略。

还要注意最近版本的OSX将拒绝把SHMMAX 的数值设置为非4096的倍数。

在这个平台上,SHMALL是用4KB页来度量的。

在较旧的OSX版本,你修改的共享内存参数,将需要重启生效。像10.5在运行中除了SHMMNI外都能修改, 使用sysctl。但仍然最好通过/etc/sysctl.conf设置你首选值,所以在重启后将保留这些值。

/etc/sysctl.conf这个文件只在OSX10.3.9及之后的版本遵循使用。如果你运行在10.3.x之前版本时,你必须编辑 /etc/rc文件并且通过下面的命令修改这些值:

sysctl-wkern.sysv.shmmax
sysctl-wkern.sysv.shmmin
sysctl-wkern.sysv.shmmni
sysctl-wkern.sysv.shmseg
sysctl-wkern.sysv.shmall

请注意OSX系统更新会写覆盖/etc/rc文件,所以,你应想到每个更新后重做这些编辑。

在OSX10.2和较早版本,在/System/Library/StartupItems/SystemTuning/SystemTuning文件里编辑这些命令替换。

SCOOpenServer

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

./configure-ySHMMAX

设置SHMMAX的新值:

./configureSHMMAX=value

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

./link_unix

andreboot.

Solaris

至少到版本2.6为止,共享内存段的缺省最大设置对PostgreSQL 来说是太低了。相关的设置可以在/etc/system里面修改,例如:

setshmsys:shminfo_shmmax=0x2000000
setshmsys:shminfo_shmmin=1
setshmsys:shminfo_shmmni=256
setshmsys:shminfo_shmseg=256

setsemsys:seminfo_semmap=256
setsemsys:seminfo_semmni=512
setsemsys:seminfo_semmns=512
setsemsys:seminfo_semmsl=32

你要重启系统令修改生效。

又见http://sunsite.uakom.sk/sunworldonline/swol-09-1997/swol-09-ins idesolaris.htm 获取关于Solaris里面的共享内存的信息。

UnixWare

UnixWare7上,缺省配置里的最大共享内存段是512kB。 要显示SHMMAX的当前值,运行:

/etc/conf/bin/  idtune-gSHMMAX

就会显示当前缺省的最小和最大值。 要给SHMMAX设置一个新值,运行:

/etc/conf/bin/  idtuneSHMMAXvalue

value是你想设置的新值(单位:字节)。 设置完SHMMAX后,重建内核

/etc/conf/bin/  idbuild-B

然后重启。

Table 17-2. PostgreSQL共享内存的使用

使用近似共享内存所需要的字节数(8.3)
连接数(1800+270*max_locks_per_transaction)*max_connections
自动清理工作者数(1800+270*max_locks_per_transaction)*autovacuum_max_workers
准备事务(770+270*max_locks_per_transaction)*max_prepared_transactions
共享磁盘缓冲区大小(block_size+208)*shared_buffers
WAL缓冲区大小(wal_block_size+8)*wal_buffers
必需的固定空间大小770kB

17.4.2. 资源限制

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

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

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

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

PostgreSQL服务器每个连接都使用一个进程, 所以你应该至少允许和连接数相同的进程数,再加上系统其它部分所需要的数目。 通常这个并不是什么问题,但如果你在一台机器上运行多个服务器,资源使用可能就会紧张。

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

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

17.4.3. Linux内存过量

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

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

OutofMemory:Killedprocess12345(postgres).

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

一个避免这个问题的方法是在一台你确信不会因为其它进程而耗 尽内存的机器上运行PostgreSQL。 如果内存资源紧张,增加交换空间的操作系统可以帮助避免这个问题,因为 内存不足(OOM)的杀手是只有当物理内存和调用交换空间用尽。

在Linux2.6及以后的版本里,一个更好的解决方法是修改内存的行为, 这样它就不会再"过量"内存。 虽然此设置不会阻止OOMkiller被调用 干脆,它会降低的机会显着,因此将导致更强大的系统行为。 这是通过用sysctl选取一个严格的过提交模式实现的:

sysctl-wvm.overcommit_memory=2

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

另一种方法,可以使用或不改变vm.overcommit_memory, 要将postmaster进程的特定进程的oom_adj值设置为-17,从而保证不会成为OOM杀手的目标。 最简单的方法这样来执行

echo-17>/proc/self/oom_adj

在postmaster的启动脚本之前调用postmaster。 请注意必须以root身份,完成此操作,或它将没有任何影响;所以一个拥有root权限的启动脚本最容易做这事。 如果这样做,你可能还要将-DLINUX_OOM_ADJ=0添加到CFLAGS编译PostgreSQL。 这样将导致postmaster子进程以通常oom_adj值为0的运行,所以OOM杀手在需要的时候以它们作为目标。

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