一次大型 PostgreSQL 安装会很容易耗尽各种操作系统的资源上限。甚至在有些系统上,出厂设置低得你都不用一次"大型"安装。如果你碰到这类问题,请继续阅读。
共享内存和信号灯的正确叫法是"System V IPC"(还有消息队列,不过与 PostgreSQL 无关)。尽管所有现代操作系统都提供这个特性,但并不是所有系统缺省都打开它或者有足够的资源,尤其是有 BSD 血统的系统。对于 Windows 移植,PostgreSQL 自己提供这套机制的替换实现。
完全缺少这些机制的表现通常是在服务器启动时的 Illegal system call 错误。这时除了重新配置内核以外没什么可做的。PostgreSQL 没它们干不了活。
如果 PostgreSQL 超出了这些 IPC 资源的硬限制之一的时候就会拒绝启动,并且留下一条相当有启发性的错误信息,告诉你它碰到了什么问题以及需要为它做些什么(又见节16.3.1)。相关的内核参数在不同系统之间有着相对固定的术语;表16-1是一个概况。不过,设置它们的方法却多种多样。不过要注意的是,你可能最好重新启动机器,或者还要重新编译内核来修改这些设置。
表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 配置参数相关,如表16-2所示。因此,作为一种临时的解决方法,你可以降低这些设置来绕过失败。如果粗略地估计,你可以估计所需要的段尺寸是 500kB 加上表里面显示的变量要求的数量(任何你得到的错误信息都会包含分配失败的尺寸)。尽管我们可以在 SHMMAX 小到只有 1MB 的情况下运行,你还是需要至少 4MB 才能有起码的性能,而比较合理的设置是以数十兆计的。
有些系统对系统里面共享内存的总数(SHMALL)还有限制。请注意这个数值必须足够大,大到 PostgreSQL 加上其它使用共享内存段的应用的总和。注意:SHMALL 在很多系统上是用页面数,而不是字节数来计算的。
不太可能出问题的是共享内存段的最小尺寸(SHMMIN),对 PostgreSQL 来说大约是 500kB 左右(通常只是 1),而系统范围(SHMMNI)或每进程(SHMSEG)最大共享内存段数量不应该会产生问题,除非你的系统把它们设成零。
PostgreSQL 每个允许的连接使用一个信号灯(max_connections),以 16 个为一套。每套信号灯还包含第 17 个信号灯,它里面存储一个"magic number",以检测和其它应用使用的信号灯集冲突。系统里的最大信号灯数目是由 SEMMNS 设置的,因此这个值应该至少和 max_connections 设置一样大,并且每 16 个连接还要另外加一个(参阅表16-1里面的公式)。参数 SEMMNI 决定系统里一次可以存在的信号灯集的数目。因此这个参数至少应该为 ceil(max_connections / 16) 。降低允许的连接数目是一个临时的绕开失败的方法,这个启动失败通常被来自函数 semget
的错误响应 No space left on device 搞得很让人迷惑。
有时候还可能有必要增大 SEMMAP ,使之至少按照 SEMMNS 配置。这个参数定义信号灯资源映射的尺寸,可用的每个连续的信号灯块在这个映射中存放一条记录。每当一套信号灯被释放,那么它要么会加入到该映射中一条相连的已释放块的入口中,要么注册成一条新的入口。如果映射填满了碎片,那么被释放的信号灯就丢失了(除非重启)。因此信号灯空间的碎片时间长了会导致可用的信号灯比应该有的信号灯少。
SEMMSL 参数决定一套信号灯里可以有多少信号灯,对于 PostgreSQL 而言应该至少是 17 。
许多设置与"semaphore undo"(信号灯恢复)有关,比如 SEMMNU 和 SEMUME ,这些与 PostgreSQL 无关。
【共享内存】缺省时只支持 4MB 的共享内存。请记住共享内存是不能分页的;它是锁在 RAM 里面的。要增加系统支持的共享缓冲区数目,向内核配置文件里增加下面的行:
options "SHMALL=8192" options "SHMMAX=\(SHMALL*PAGE_SIZE\)"
SHMALL 以 4KB 页为单位计算,所以 1024 页面代表 4M 共享内存。所以上面的东西把共享内存区域增加到 32MB 。对于运行 4.3 或者更新版本的人,你可能需要增大 KERNEL_VIRTUAL_MB ,超过缺省的 248 。做完上面的修改之后,编译内核并重启。
对于运行 4.0 或者更早的版本的,请用 bpatch 找出当前内核的 sysptsize 值。它是启动的时候动态计算的。
$ bpatch -r sysptsize 0x9 = 9
然后,把 SYSPTSIZE 修改为在内核配置文件里的一个硬代码值。用 bpatch 算出来的值,并且为你需要的每个额外的 4MB 共享内存再加 1 。
options "SYSPTSIZE=16"
sysptsize 不能用 sysctl 修改。
【信号灯】你可能还需要增加信号灯的数量;系统缺省的总数 60 只能允许大概 50 个 PostgreSQL 连接。在内核配置文件里设置你需要的值,比如:
options "SEMMNI=40" options "SEMMNS=240"
缺省设置只适合于很小的安装(比如,缺省 SHMMAX 是 32MB)。我们可以用 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 来实现。
如果通过启用 sysctl 的 security.jail.sysvipc_allowed 运行在 FreeBSD jail 中,那么必须将 postmaster 以不同的用户身份运行在不同的 jail 中。这样有助于增强安全性,因为它防止了非 root 用户干扰不同 jail 中的共享内存或信号灯,并且允许 PostgreSQL IPC 清理代码功能。在 FreeBSD 6.0 及之后的版本中,IPC 清理代码并不能正确侦测在其它 jail 中的进程,因此无法防止其它 jail 中的 postmaster 进程占用相同的端口。
FreeBSD 4.0 之前的版本类似 NetBSD 和 OpenBSD (见下文)。
编译内核时需要把选项 SYSVSHM 和 SYSVSEM 打开(缺省是打开的)。共享内存的最大尺寸是由选项 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
你可能希望将共享内存锁在 RAM 中以避免它们被交换出去,我们可以用 sysctl 设置 kern.ipc.shm_use_phys 来实现这个目的。
缺省设置看来对普通安装是足够的了。对于 HP-UX 10 ,SEMMNS 的出厂缺省是 128 ,可能对大的数据库节点来说太小了。
IPC 可以在 System Administration Manager 下面的 Kernel Configuration->Configurable Parameters 配置。配置完了以后敲 Create A New Kernel 选项。
缺省设置只适合小安装(缺省最大共享内存是 32MB)。不过,其它的缺省值都相当大,通常不需要改变。最大的共享内存段设置可以用 sysctl 接口设置。比如,要允许 128MB ,并且最大的总共享内存数为 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
在 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),所以每次更新后你可能都需要重新编辑。
在 OS X 10.3 及以后的版本里,除了编辑 /etc/rc 之外,你还可以创建一个名为 /etc/sysctl.conf 的文件,包含如下变量及相应的值:
kern.sysv.shmmax=4194304 kern.sysv.shmmin=1 kern.sysv.shmmni=32 kern.sysv.shmseg=8 kern.sysv.shmall=1024
这个方法比编辑 /etc/rc 文件要好,因为这些设置在系统升级的时候能够得以保留。注意,所有五个共享内存参数必须都在 /etc/sysctl.conf 中设置,否则将会被忽略。
还要注意最近版本的 OS X 将拒绝把 SHMMAX 的数值设置为非 4096 的倍数。
在这个平台上,SHMALL 是用 4KB 页来度量的。
所有 OS X 的版本都需要重启以后才能让这些共享内存的参数设置生效。
缺省配置时,只允许每段 512KB 共享内存。要增大设置,首先进入 /etc/conf/cf.d 目录。要显示当前以字节记的 SHMMAX ,运行
./configure -y SHMMAX
设置 SHMMAX 的新值:
./configure SHMMAX=value
这里 value 是你想设置的以字节记的新值。设置完 SHMMAX 以后重新编译内核
./link_unix
然后重启。
至少对于版本 5.1 而言,我们有必要为类似 SHMMAX 这样的参数做特殊的配置,因为这个参数可以配置为所有内容都当作共享内存使用。这就是类似 DB/2 这样的数据库常用的配置。
不过,我们可能有必要在 /etc/security/limits 里面修改全局 ulimit 信息,因为文件大小的缺省硬限制(fsize)以及文件数(nofiles)可能太低了。
至少到版本 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 里面的共享内存的信息。
在 Unix Ware 7 上,缺省配置里的最大共享内存段是 512kB 。要显示 SHMMAX 的当前值,运行
/etc/conf/bin/idtune -g SHMMAX
就会显示以字节记的当前的缺省的最小和最大值。要给 SHMMAX 设置一个新值,运行:
/etc/conf/bin/idtune SHMMAX value
value 是你想设置的以字节记的新值。设置完 SHMMAX 后,重建内核
/etc/conf/bin/idbuild -B
然后重启。
表16-2. 影响 PostgreSQL 的内存使用的配置参数
名字 | 近似倍率(每次增加的字节数) |
---|---|
max_connections | 400 + 270 * max_locks_per_transaction |
max_prepared_transactions | 600 + 270 * max_locks_per_transaction |
shared_buffers | 8300 (假设 BLCKSZ 为 8K) |
wal_buffers | 8200 (假设 XLOG_BLCKSZ 为 8K) |
max_fsm_relations | 70 |
max_fsm_pages | 6 |
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 配置参数来限制打开文件数的消耗。
在Linux 2.4 以及之后的版本里,缺省的虚拟内存的行为不是对 PostgreSQL 最优的。原因在于内核实现内存过提交的方法,如果其它进程的内存请求导致系统用光虚拟内存,那么内核可能会终止 PostgreSQL 服务器进程。
如果发生了这样的事情,你会看到像下面这样的内核信息(参考你的系统文档和配置,看看在哪里能看到这样的信息):
Out of Memory: Killed process 12345 (postgres).
这表明 postgres 因为内存压力而终止了。尽管现有的数据连接将继续正常运转,但是新的连接将无法接受。要想恢复,你应该重启 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 的存在不能当作是这个特性存在的证明。如果有问题,请询问你的内核供应商的专家。