为PostgreSQL优化调整Linux内核参数

发布时间 2023-11-25 19:26:44作者: jl1771

为了获得最佳性能,PostgreSQL数据库依赖于正确定义的操作系统参数。配置不当的操作系统内核参数可能导致数据库服务器性能下降。因此,必须根据数据库服务器及其工作负载来配置这些参数。在这篇文章中,我们将讨论一些可能影响数据库服务器性能的重要Linux内核参数,以及如何调优这些参数。

SHMMAX / SHMALL

SHMMAX 是一个内核参数,用于定义Linux进程可以分配的单个共享内存段的最大长度。直到9.2版本,PostgreSQL使用的System V (SysV)需要设置 SHMMAX。在9.2之后,PostgreSQL切换到POSIX共享内存。所以现在它需要更少的System V共享内存。

在9.3之前,SHMMAX 是最重要的内核参数。SHMMAX 的值以字节为单位。

类似地,SHMALL 是另一个内核参数,用于定义系统范围内共享内存页的总量。需要查看 SHMMAXSHMALLSHMMIN的当前值,使用ipc命令。

$ ipcs -lm

------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 1073741824
max total shared memory (kbytes) = 17179869184
min seg size (bytes) = 1

PostgreSQL使用 System V IPC 来分配共享内存。该参数是最重要的内核参数之一。当你得到以下错误消息时,这意味着你的PostgreSQL版本较旧,并且 SHMMAX 值很低。用户需要根据他们将要使用的共享内存来调整和增加这个值。

可能的配置错误
如果SHMMAX 配置错误,在尝试使用initdb命令初始化PostgreSQL集群时可能会出现错误。

DETAIL: Failed system call was shmget(key=1, size=2072576, 03600).

HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded your kernel's SHMMAX parameter. 
You can either reduce the request size or reconfigure the kernel with larger SHMMAX. To reduce the request size (currently 2072576 bytes),
reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.

If the request size is already small, it's possible that it is less than your kernel's SHMMIN parameter,
in which case raising the request size or reconfiguring SHMMIN is called for.

The PostgreSQL documentation contains more information about shared memory configuration. child process exited with exit code 1

类似地,使用pg_ctl命令启动PostgreSQL服务器时也会出现错误。

DETAIL: Failed system call was shmget(key=5432001, size=14385152, 03600).

HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded your kernel's SHMMAX parameter.
You can either reduce the request size or reconfigure the kernel with larger SHMMAX.; To reduce the request size (currently 14385152 bytes),
reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.

If the request size is already small, it's possible that it is less than your kernel's SHMMIN parameter,
in which case raising the request size or reconfiguring SHMMIN is called for.

The PostgreSQL documentation contains more information about shared memory configuration.

可以使用sysctl命令临时修改该值。要永久设置该值,请在/etc/sysctl.conf中添加一个条目。详情如下。

# Get the value of SHMMAX
sudo sysctl kernel.shmmax
kernel.shmmax: 4096

# Get the value of SHMALL
sudo sysctl kernel.shmall
kernel.shmall: 4096

# Set the value of SHMMAX
sudo sysctl -w kernel.shmmax=16777216
kernel.shmmax: 4096 -> 16777216

# Set the value of SHMALL 
sudo sysctl -w kernel.shmall=16777216
kernel.shmall: 4096 -> 16777216

要使更改永久生效,请将这些值添加到 /etc/sysctl.conf

Huge Pages

默认情况下,Linux使用4K内存页,BSD有超级页,而Windows有大页。页是分配给进程的一块物理内存。根据进程的内存需求,进程可以拥有多个页。进程需要的内存越多,分配给它的页就越多。操作系统维护了一个进程页分配表。页面越小,表越大,在页表中查找一页所需的时间就越多。因此,大页使得使用大量内存的同时减少开销成为可能。更少的页查找,更少的页错误,通过更大的缓冲区进行更快的读/写操作。这提高了性能。

PostgreSQL仅在Linux上支持更大的页面。默认情况下,Linux使用4K的内存页,因此在内存操作太多的情况下,需要设置更大的页。通过使用大小为2 MB到1 GB的大页,可以观察到性能的提升。超大页的大小可以设置启动时间。您可以使用 cat /proc/meminfo | grep -i huge 命令在您的Linux机器上轻松检查大页的设置和利用率。

Note: This is only for Linux, for other OS this operation is ignored$ cat /proc/meminfo | grep -i huge
AnonHugePages:   0 kB
ShmemHugePages:  0 kB
HugePages_Total: 0
HugePages_Free:  0
HugePages_Rsvd:  0
HugePages_Surp:  0
Hugepagesize:	2048 kB

在本例中,尽管大页大小设置为2048 (2 MB),但巨型页的总数为0。这表示禁用巨大的页面。

用于量化大页的脚本

这是一个简单的脚本,返回所需的巨大页面的数量。当PostgreSQL运行时,在Linux系统上执行这个脚本。确保 $PGDATA环境变量设置为PostgreSQL的数据目录。

#!/bin/bash
pid=`head -1 $PGDATA/postmaster.pid`
echo "Pid:     $pid"
peak=`grep ^VmPeak /proc/$pid/status | awk '{ print $2 }'`
echo "VmPeak:     $peak kB"
hps=`grep ^Hugepagesize /proc/meminfo | awk '{ print $2 }'`
echo "Hugepagesize:   $hps kB"
hp=$((peak/hps))
echo Set Huge Pages:     $hp

脚本的输出如下所示:

Pid:            12737
VmPeak:         180932 kB
Hugepagesize:   2048 kB
Set Huge Pages: 88

推荐的大页数为88,因此应该将该值设置为88。

sysctl -w vm.nr_hugepages= 88

现在检查大页,您将看到没有大页正在使用(HugePages_Free = HugePages_Total)。

$ cat /proc/meminfo | grep -i huge
AnonHugePages:        0 kB
ShmemHugePages:       0 kB
HugePages_Total:      88
HugePages_Free:       88
HugePages_Rsvd:       0
HugePages_Surp:       0
Hugepagesize:         2048 kB

现在在 $PGDATA/postgresql.conf 中设置参数huge_pages为"on"并重启服务器。

$ cat /proc/meminfo | grep -i huge
AnonHugePages:        0 kB
ShmemHugePages:       0 kB
HugePages_Total:      88
HugePages_Free:       81
HugePages_Rsvd:       64
HugePages_Surp:       0
Hugepagesize:         2048 kB

现在你可以看到只有很少一部分大页面被使用。现在让我们尝试向数据库中添加一些数据。

postgres=# CREATE TABLE foo(a INTEGER);
CREATE TABLE
postgres=# INSERT INTO foo VALUES(generate_Series(1,10000000));
INSERT 0 10000000

让我们看看现在是否比以前使用了更多的大页面。

$ cat /proc/meminfo | grep -i huge
AnonHugePages:        0 kB
ShmemHugePages:       0 kB
HugePages_Total:      88
HugePages_Free:       18
HugePages_Rsvd:       1
HugePages_Surp:       0
Hugepagesize:         2048 kB

现在可以看到,大多数大页面都在使用中。

这里使用的 HugePages的示例值非常低,这不是大型生产机器的正常值。请评估您的系统所需的页面数量,并根据您的系统工作负载和资源进行相应的设置。

vm.swappiness

vm.swappiness 是影响数据库性能的另一个内核参数。该参数用于控制Linux系统上页交换(在交换内存和物理内存之间交换页)的行为。取值范围是0 ~ 100。它控制有多少内存会被换出或换出。0表示禁用交换,100表示积极的交换。

你可以通过设置较低的值来获得较好的性能。

在较新的内核中设置值为0可能会导致OOM Killer (Linux中的内存不足Killer进程)终止该进程。因此,为了安全起见,如果希望最小化交换,可以将该值设置为1。Linux系统的默认值为60。值越高,MMU(内存管理单元)利用的交换空间就越多,而值越低,在内存中保存的数据/代码就越多。

较小的值有助于提高PostgreSQL的性能。

vm.overcommit_memory / vm.overcommit_ratio

应用程序获取内存,并在不再需要时释放内存。但在某些情况下,应用程序获取了太多内存而不释放它。这可以调用OOM killer。下面是vm的可能值。vm. overcommit_memory参数,每个参数都有描述:

  1. 启发式过度使用,智能使用(默认);基于核的启发式算法
  2. 无论如何,允许过度使用
  3. 不要过度使用超过了过度使用的比率。

参考: https://www.kernel.org/doc/Documentation/vm/overcommit-accounting

vm.overcommit_ratio是可用于超额分配的内存的百分比。在具有2 GB RAM的系统上,50%的值可能会提交多达3 GB的RAM。

vm.overcommit_memory 为2为PostgreSQL带来了更好的性能。这个值最大化了服务器进程的RAM利用率,而不会有被OOM killer进程杀死的任何重大风险。应用程序将能够超额使用,但只能在超额使用比率内,从而减少OOM killer杀死进程的风险。因此,值为2比默认值0的性能更好。但是,通过确保超出允许范围的内存不会被过度使用,可以提高可靠性。它避免了进程被OOM-killer杀死的风险。

在没有交换的系统上,在 vm. overcommit_memory 的值为2时可能会遇到问题。

参考: https://www.postgresql.org/docs/current/static/kernel-resources.html#LINUX-MEMORY-OVERCOMMIT

vm.dirty_background_ratio / vm.dirty_background_bytes

vm.dirty_background_ratio是内存中填充了需要刷出到磁盘的脏页的百分比。冲洗在后台完成。该参数的取值范围是0 ~ 100;但是,小于5的值可能是无效的,一些内核内部不支持它。大多数Linux系统的默认值为10。您可以以较低的比率提高写密集型操作的性能,这意味着Linux在后台刷写脏页。

您需要根据磁盘速度设置 vm.dirty_background_bytes 的值。

这两个参数没有“好的”值,因为它们都依赖于硬件。但是,设置vm.dirty_background_ratio为5和 vm.dirty_background_bytes设置为磁盘速度的25%,在大多数情况下可以提高高达25%的性能。

vm.dirty_ratio / dirty_bytes

这和 vm.dirty_background_ratio / dirty_background_bytes 是一样的,只是刷出是在前台完成的,会阻塞应用程序。所以 dirty_ratio 应该比 vm.dirty_background_ratio 高。这将确保后台进程在前台进程之前启动,尽可能避免阻塞应用程序。您可以根据磁盘IO负载调整这两个比率之间的差异。

总结

您可以调优其他性能参数,但性能提升的幅度可能很小。我们必须记住,并非所有参数都与所有应用程序类型相关。有些应用程序通过调整一些参数来实现更好的性能,有些则不然。您需要根据预期的应用程序工作负载和类型在这些参数配置之间找到一个良好的平衡,在进行调整时还必须记住操作系统的行为。调优内核参数不像调优数据库参数那么容易:它更难以规定。