17.4. 管理内核资源

PostgreSQL有时可能耗尽各种操作系统的资源上限,尤其是多个服务器副本在同一个系统上运行时, 或者在一个非常大的安装时。这种情况说明了PostgreSQL 使用的内核资源和解决问题可以采取的步骤都和内核资源消耗有关。

17.4.1. 共享内存和信号灯

共享内存和信号灯的正确叫法是"System V IPC" (还有消息队列,不过与PostgreSQL无关)。, PostgreSQL只在Windows上自己提供这套机制的替换实现, 要运行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 + 4) / 16)
SEMMNS系统范围的最大信号灯数量ceil((max_connections + autovacuum_max_workers + 4) / 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工作进程(autovacuum_max_workers),以 16 个为一套。 每套信号灯还包含第 17 个信号灯,它里面存储一个"magic number", 以检测和其它应用使用的信号灯集冲突。系统里的最大信号灯数目是由SEMMNS设置的, 因此这个值应该至少和max_connections加上autovacuum_max_workers设置一样大, 并且每 16 个连接和工作还要另外加一个(参阅表 17-1里面的公式)。 参数SEMMNI决定系统里一次可以存在的信号灯集的数目。 因此这个参数至少应该为ceil((max_connections + autovacuum_max_workers + 4) / 16)。 降低允许的连接数目是一个临时的绕开失败的方法,这个启动失败通常被来自函数semget 的错误响应"No space left on device"搞得很让人迷惑。

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

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

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

AIX

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

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

FreeBSD

缺省设置可以用sysctlloader接口来修改。 下面的参数可以用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来完成。

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

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

NetBSD

NetBSD 5.0及以后,IPC参数可以用sysctl调整,例如:

$ sysctl -w kern.ipc.shmmax=16777216

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

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

NetBSD 4.0 之前的版本类似OpenBSD(见下文), 除了参数应该用关键字options而不是option来设置。

OpenBSD

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

option        SYSVSHM
option        SHMMAXPGS=4096
option        SHMSEG=256

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

你可能还想配置内核,把共享内存锁在 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

缺省最大段大小为32MB,缺省的最大总字节为2097152页。一页通常是4096字节, 除了带有"huge pages"的不寻常的内核配置(使用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是用 4KB 页来度量的。

在老的OS X版本里,你将需要重启以使共享内存配置的改变生效。自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

缺省配置时,只允许每段 512KB 共享内存。要增大设置,首先进入/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)

此外,如果你在一个区域里面运行PostgreSQL,你可能也需要提高区域资源使用的限制。 参阅System Administrator's Guide里面的"Chapter2: Projects and Tasks" 获取更多关于projectsprctl的信息。

UnixWare

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

/etc/conf/bin/idtune -g SHMMAX

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

/etc/conf/bin/idtune SHMMAX value

value是你想设置的以字节记的新值。设置完SHMMAX后,重建内核:

/etc/conf/bin/idbuild -B

然后重启。

17.4.2. 资源限制

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

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

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

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

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

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

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

17.4.3. Linux 内存过提交

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

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

Out of Memory: Killed process 12345 (postgres).

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

一个避免这个问题的方法是在一台你确信不会因为其它进程而耗尽内存的机器上运行PostgreSQL。 如果内存紧张,增加操作系统的交换空间有助于避免这个问题,因为OOM(out-of-memory) killer只有在物理内存和交换空间都耗尽时才会被调用。

如果PostgreSQL本身是系统内存耗尽的原因,你可以通过改变你的配置来避免这个问题。 在某些情况下,调低内存相关的配置参数可能有所帮助,尤其是shared_bufferswork_mem。其他情况下, 这个问题可能是由于允许太多的连接连到数据库服务器引起的。在许多情况下, 减小max_connections并且改为使用外部连接池软件会更好。

在 Linux 2.6 以及以后的版本里,可以修改内存的行为,这样它就不会再"过提交"内存。 尽管这个设置将不会完全阻止OOM killer 被调用,然是它将显著地减少并且将因此导致更稳健的系统行为。 这通过用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 killer的目标。 最简单的方法是在postmaster的启动脚本里在调用postmaster之前执行:

echo -1000 > /proc/self/oom_score_adj

请注意,这个操作必须由root来做,否则将不会有任何作用;所以一个root所有的启动脚本是做这个最简单的地方。 如果你这样做,你可能也希望把-DLINUX_OOM_SCORE_ADJ=0添加到CPPFLAGS 来编译PostgreSQL。这将导致postmaster子进程以标准oom_score_adj值0来运行, 所以OOM killer仍然可以在需要时把它们作为目标。

老版本的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 只会让事情更糟, 而不是更好。我们建议你检查一下实际的内核源代码(参阅文件mm/mmap.c 里面的vm_enough_memory函数),核实一下这个是在你的内核里存在的, 然后再在 2.4 内核里使用这个特性。文档文件overcommit-accounting 的存在不能当作是这个特性存在的证明。如果有问题,请询问你的内核供应商的专家。

17.4.4. Linux huge pages

当使用大量连续内存块时,使用huge pages减少开销,就像PostgreSQL那样。 要在PostgreSQL中启用这个特性, 需要内核CONFIG_HUGETLBFS=yCONFIG_HUGETLB_PAGE=y。 也需要调整系统设置vm.nr_hugepages。 要估计不启用huge pages启动PostgreSQL 所需的huge pages的数量和检查程序文件系统中VmPeak的值:

$ head -1 /path/to/data/directory/postmaster.pid
4170
$ grep ^VmPeak /proc/4170/status
VmPeak:  6490428 kB

6490428 / 2048 (在这种情况下PAGE_SIZE2MB) 大约是3169.154 huge pages,所以将至少需要 3170 huge pages:

$ sysctl -w vm.nr_hugepages=3170

有时内核并不能分配所需数量的huge pages,所以需要重复该命令或者重启。 不要忘记添加条目到/etc/sysctl.conf中, 以在重启后保持该设置。

PostgreSQL中huge pages的缺省行为是在可能时使用它们, 并且在失败时回到普通页面。要强制使用huge pages,可以设置 huge_pageson。请注意,在这种情况下, 如果没有足够的huge pages可用,PostgreSQL将会启动失败。

Linux huge pages的详细描述请查看 https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt