17.4. 管理内核资源
PostgreSQL有时可能耗尽各种操作系统的资源上限,尤其是多个服务器副本在同一个系统上运行时, 或者在一个非常大的安装时。这种情况说明了PostgreSQL 使用的内核资源和解决问题可以采取的步骤都和内核资源消耗有关。
17.4.1. 共享内存和信号灯
共享内存和信号灯的正确叫法是"System V IPC" (还有消息队列,不过与PostgreSQL无关)。, PostgreSQL只在Windows上自己提供这套机制的替换实现, 要运行PostgreSQL这些机制是必需的。
完全缺少这些机制的表现通常是在服务器启动时的Illegal system call错误。 这时除了重新配置内核外别无选择。PostgreSQL没它们干不了活。 这种情况很少见,但是,在现代操作系统上会出现。
如果PostgreSQL超出了这些IPC资源的硬限制之一的时候就会拒绝启动, 并且留下一条相当有启发性的错误信息,描述问题以及需要为它做些什么 (又见Section 17.3.1)。相关的内核参数在不同系统之间有着相对固定的术语; Table 17-1是一个概况。不过,设置它们的方法却多种多样。 下面给出一些平台的建议。
Note: PostgreSQL 9.3之前,System V共享内存的数量需要启动的服务器大得多。 如果你运行更老的服务器版本,请参考你的服务器版本的文档。
Table 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 个连接和工作还要另外加一个(参阅Table 17-1里面的公式)。 参数SEMMNI
决定系统里一次可以存在的信号灯集的数目。 因此这个参数至少应该为ceil((max_connections + autovacuum_max_workers + 4) / 16)
。 降低允许的连接数目是一个临时的绕开失败的方法,这个启动失败通常被来自函数semget
的错误响应"No space left on device"搞得很让人迷惑。
有时候还可能有必要增大SEMMAP
,使之至少按照SEMMNS
配置。 这个参数定义信号灯资源映射的尺寸,可用的每个连续的信号灯块在这个映射中存放一条记录。 每当一套信号灯被释放,那么它要么会加入到该映射中一条相连的已释放块的入口中, 要么注册成一条新的入口。如果映射填满了碎片,那么被释放的信号灯就丢失了(除非重启)。 因此信号灯空间的碎片时间长了会导致可用的信号灯比应该有的信号灯少。
SEMMSL
参数决定一套信号灯里可以有多少信号灯, 对于PostgreSQL而言应该至少是 17 。
许多设置与"semaphore undo"(信号灯恢复)有关,比如SEMMNU
和SEMUME
, 这些与PostgreSQL无关。
AIX
至少对于版本 5.1 而言,我们没有必要为类似SHMMAX
这样的参数做特殊的配置, 因为这个参数可以配置为所有内容都当作共享内存使用。这就是类似DB/2 这样的数据库常用的配置。
不过,我们可能有必要在/etc/security/limits
里面修改全局ulimit
信息, 因为文件大小的缺省硬限制(fsize
)以及文件数(nofiles
)可能太低了。
FreeBSD
缺省设置可以用sysctl
或loader
接口来修改。 下面的参数可以用sysctl
设置:
<samp class="literal">#</samp> <kbd class="literal">sysctl kern.ipc.shmall=32768</kbd>
<samp class="literal">#</samp> <kbd class="literal">sysctl kern.ipc.shmmax=134217728</kbd>
<samp class="literal">#</samp> <kbd class="literal">sysctl kern.ipc.semmap=256</kbd>
要想让这些设置重启后有效,修改/etc/sysctl.conf
文件。
如果用sysctl
,那么剩下的信号灯设置是只读的, 但是可以在/boot/loader.conf
里设置:
kern.ipc.semmni=256
kern.ipc.semmns=512
kern.ipc.semmnu=256
修改完这些值以后需要重启以使新的设置生效。
你可能还想配置内核,把共享内存锁到 RAM 里,避免他们被交换到交换分区中。 这些可以通过使用sysctl
设置kern.ipc.shm_use_phys
来完成。
如果通过启用sysctl的security.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
调整,例如:
<samp class="literal">$</samp> <kbd class="literal">sysctl -w kern.ipc.shmmax=16777216</kbd>
要想让这些设置重启后有效,修改/etc/sysctl.conf
文件。
你可能还想配置内核,把共享内存锁到 RAM 里,避免他们被交换到交换分区中。 这些可以通过使用sysctl
设置kern.ipc.shm_use_phys
来完成。
NetBSD 4.0 之前的版本类似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
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:
<samp class="literal">$</samp> <kbd class="literal">sysctl -w kernel.shmmax=17179869184</kbd>
<samp class="literal">$</samp> <kbd class="literal">sysctl -w kernel.shmall=4194304</kbd>
为了这些设置在重启后保持有效,将这些设置放到/etc/sysctl.conf
里。这样做是高度推荐的。
老版本里可能没有sysctl
程序,但是同样的改变可以通过操作/proc
文件系统来做:
<samp class="literal">$</samp> <kbd class="literal">echo 17179869184 >/proc/sys/kernel/shmmax</kbd>
<samp class="literal">$</samp> <kbd class="literal">echo 4194304 >/proc/sys/kernel/shmall</kbd>
剩下的缺省是相当宽松的大小,通常不需要改变。
Mac 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" 获取更多关于projects
和prctl
的信息。
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
就可以设置硬限制。
内核通常也有一些系统范围的资源限制。
- 在Linux上,
/proc/sys/fs/file-max
决定内核可以支持的最大文件数。你可以通过往该文件写入一个不同的数值修改此值, 或者在/etc/sysctl.conf
里增加一个赋值。 每个进程的最大打开文件限制是在编译内核的时候固定的; 参阅/usr/src/linux/Documentation/proc.txt
获取更多信息。
PostgreSQL服务器每个连接都使用一个进程, 所以你应该至少允许和连接数相同的进程数,再加上系统其它部分所需要的数目。 通常这个并不是什么问题,但如果你在一台机器上运行多个服务器,那你就要把事情理清楚。
打开文件数目的出厂缺省设置通常设置为"社会友好"数值, 就是说允许许多用户共存一台机器,而不会导致系统资源使用的不当比例。 如果你在一台机器上运行许多服务器,这也许就是你想要的,但是在特殊的服务器上, 你可能需要提高这个限制。
问题的另外一边,一些系统允许独立的进程打开非常多的文件;如果有几个进程这么干, 那系统范围的上限就很容易达到。如果你发现这样的现像,并且不想修改系统范围的限制, 你就可以设置PostgreSQL的max_files_per_process 配置参数来限制打开文件数的消耗。
17.4.3. Linux 内存过提交
在Linux 2.4 以及之后的版本里,缺省的虚拟内存的行为不是对PostgreSQL最优的。 原因在于内核实现内存过提交的方法,如果其它进程的内存请求导致系统用光虚拟内存, 那么内核可能会终止PostgreSQL主服务器进程。
如果发生了这样的事情,你会看到像下面这样的内核信息(参考你的系统文档和配置, 看看在哪里能看到这样的信息):
Out of Memory: Killed process 12345 (postgres).
这表明postgres
因为内存压力而终止了。尽管现有的数据连接将继续正常运转, 但是新的连接将无法接受。要想恢复,你应该重启PostgreSQL。
一个避免这个问题的方法是在一台你确信不会因为其它进程而耗尽内存的机器上运行PostgreSQL。
如果PostgreSQL本身是系统耗尽内存的原因,你可以通过改变你的配置来避免这个问题。 在某些情况下,这样可能帮助降低内存相关的配置参数,尤其是shared_buffers
和work_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
都可以使用, 设置与进程相关的oom_score_adj
值为主进程-1000
, 从而保证它不会成为OOM killer的目标。 最简单的方法是在调用postmaster之前在postmaster的启动脚本执行:
echo -1000 > /proc/self/oom_score_adj
请注意,这个操作必须由root来做,否则将不会有任何作用;所以一个root所有的启动脚本是做这个最简单的地方。 如果你这样做,你可能也希望用-DLINUX_OOM_SCORE_ADJ=0
添加到CPPFLAGS
来建立PostgreSQL。这将导致主子进程以标准oom_score_adj
值0来运行, 所以OOM killer仍然可以在需要时以它们为目标。
老版本的Linux内核不提供/proc/self/oom_score_adj
,但是可能有相同功能的名为 /proc/self/oom_adj
的以前的版本。除了禁用值为-17
而不是-1000
外,它们做相同的工作。相应的PostgreSQL的建立标识为-DLINUX_OOM_ADJ=0
。
Note: 有些供应商的 Linux 2.4 内核有着早期 2.6 过提交的
sysctl
。不过, 在没有相关代码的2.4内核里设置vm.overcommit_memory
为 2 只会让事情更糟, 而不是更好。我们建议你检查一下实际的内核源代码(参阅文件mm/mmap.c
里面的vm_enough_memory
函数),核实一下这个是在你的内核里存在的, 然后再在 2.4 内核里使用这个特性。文档文件overcommit-accounting
的存在不能当作是这个特性存在的证明。如果有问题,请询问你的内核供应商的专家。