文本原文位于https://www.percona.com/blog/2018/08/29/tune-linux-kernel-parameters-for-postgresql-optimization/

对于最优性能,PostgreSQL数据库完全依赖于正确的操作系统参数。配置得差劲的操作系统参数会导致数据库的性能下降。因此,最重要的是,需要根据数据库和负载来配置操作系统参数。在这篇博客里面,我们将会讨论这些影响数据库性能的操作系统参数,以及如何配置这些参数。

SHMMAX / SHMALL

SHMMAX是一个内核参数,这个参数用来确定一个Linux进程可以分配的单个共享内存段的最大尺寸。直到9.2版本,PostgreSQL都是使用了system V的方式来申请共享内存,system V 需要设置SHMMAX。9.2版本之后,PostgreSQL使用了posix共享内存机制。这样的话,就只需要极其少量的system V的共享内存了。
在9.3版本之前,SHMMAX都是最重要的内核参数。SHMMAX的值都是以字节为单位的。
相似地,SHMALL是另外一个内核参数,用来定制操作系统内共享内存总的大小。查看当前的SHMMAX SHMALL的值,可以使用ipcs的命令。
Linux 下查看:

  1. $ ipcs -lm
  2. ------ Shared Memory Limits --------
  3. max number of segments = 4096
  4. max seg size (kbytes) = 1073741824
  5. max total shared memory (kbytes) = 17179869184
  6. min seg size (bytes) = 1

MacOS下查看:

  1. $ ipcs -M
  2. IPC status from as of Thu Aug 16 22:20:35 PKT 2018
  3. shminfo:
  4. shmmax: 16777216 (max shared memory segment size)
  5. shmmin: 1 (min shared memory segment size)
  6. shmmni: 32 (max number of shared memory identifiers)
  7. shmseg: 8 (max shared memory segments per process)
  8. shmall: 1024 (max amount of shared memory in pages)

PostgreSQL使用了System V的IPC来申请共享内存。这些参数是最主要的Linux内核参数之一。如果你遇到下面的错误信息,就意味着你还在使用老版本的PostgreSQL并且SHMMAX的值设置得很小。用户应该根据使用的共享内存大小来调整这些参数。

错误配置可能的错误信息

如果SHMMAX设置不正确,在使用initdb初始化数据库的时候,你将会看到下面的错误信息:

  1. DETAIL: Failed system call was shmget(key=1, size=2072576, 03600).
  2. HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded your kernel's SHMMAX parameter. 
  3. You can either reduce the request size or reconfigure the kernel with larger SHMMAX. To reduce the request size (currently 2072576 bytes),
  4. reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.
  5. If the request size is already small, it's possible that it is less than your kernel's SHMMIN parameter,
  6. in which case raising the request size or reconfiguring SHMMIN is called for.
  7. The PostgreSQL documentation contains more information about shared memory configuration. child process exited with exit code 1

相似地,当使用pg_ctl启动数据库的时候,将会看到下面的错误信息:

  1. DETAIL: Failed system call was shmget(key=5432001, size=14385152, 03600).
  2. HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded your kernel's SHMMAX parameter.
  3. You can either reduce the request size or reconfigure the kernel with larger SHMMAX.; To reduce the request size (currently 14385152 bytes),
  4. reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.
  5. If the request size is already small, it's possible that it is less than your kernel's SHMMIN parameter,
  6. in which case raising the request size or reconfiguring SHMMIN is called for.
  7. The PostgreSQL documentation contains more information about shared memory configuration.

注意不同操作系统下参数定义的区别

Linux和MacOS上,SHMMAX SHALL的定义稍微有点不一样。他们的定义如下:

  • Linux: kernel.shmmax, kernel.shmall
  • MacOS: kern.sysv.shmmax, kern.sysv.shmall
    可以使用sysctl命令来临时改变这些参数的值。为了永久性的设置这些值,可以将它们加入到/etc/sysctl.conf。下面是详细的配置内容:
    MacOS
    1. # Get the value of SHMMAX
    2. sudo sysctl kern.sysv.shmmax
    3. kern.sysv.shmmax: 4096
    4. # Get the value of SHMALL
    5. sudo sysctl kern.sysv.shmall
    6. kern.sysv.shmall: 4096
    7. # Set the value of SHMMAX
    8. sudo sysctl -w kern.sysv.shmmax=16777216
    9. kern.sysv.shmmax: 4096 -> 16777216<br>
    10. # Set the value of SHMALL
    11. sudo sysctl -w kern.sysv.shmall=16777216
    12. kern.sysv.shmall: 4096 -> 16777216
    Linux
    1. # Get the value of SHMMAX
    2. sudo sysctl kernel.shmmax
    3. kernel.shmmax: 4096
    4. # Get the value of SHMALL
    5. sudo sysctl kernel.shmall
    6. kernel.shmall: 4096
    7. # Set the value of SHMMAX
    8. sudo sysctl -w kernel.shmmax=16777216
    9. kernel.shmmax: 4096 -> 16777216<br>
    10. # Set the value of SHMALL
    11. sudo sysctl -w kernel.shmall=16777216
    12. kernel.shmall: 4096 -> 16777216
    注意:为了永久性的设置这些值,将他们写入/etc/sysctl.conf

大页

Linux 默认使用了4K大小的页面,BSD有Super pageS, Windows有Lage Pages。一页(page)是分配给进程的一块连续内存。一个进程可能会拥有不止一个页,具体多少取决于需求。进程需要的内存越多,分配的页就越多。操作系统会维护一个表(TLB),记录页面的分配信息。页面的尺寸越小,这张表就越大,在这张表里面查找页面需要的时间就越长。因此,当使用大量内存的情况下,大页可以降低开销:更少量的页面查找,更少的缺页,通过大的缓冲区可以带来更快的读写操作。因此,这样会提高性能。
PostgreSQL只在Linux操作系统上支持更大的页。默认的,Linux使用4k大小的内存页面,在内存操作非常多的情况下,有必要使用更大的页。通过使用2M以及1G的大页,可以看到性能有了明显的提升。可以在在操作系统启动的时候设置页的大小。在Linux操作系统上,通过cat /proc/meminfo | grep -i huge 可以很容易查看大页的配置和使用情况。

  1. cat /proc/meminfo | grep -i huge
  2. Note: This is only for Linux, for other OS this operation is ignored$ cat /proc/meminfo | grep -i huge
  3. AnonHugePages: 0 kB
  4. ShmemHugePages: 0 kB
  5. HugePages_Total: 0
  6. HugePages_Free: 0
  7. HugePages_Rsvd: 0
  8. HugePages_Surp: 0
  9. Hugepagesize: 2048 kB

在上面的例子中,尽管大页的大小设置成了2M,但是因为大页的总量是0,因此大页还是被关闭的。

使用脚本来确定大页的配置

下面是一个简单的脚本来确定需要的大页总量。当PG运行时,在Linux操作系统上运行这个脚本。确保 $PGDATA 环境变量指向了数据库目录。

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

这个脚本的输出如下:

  1. Pid: 12737
  2. VmPeak: 180932 kB
  3. Hugepagesize: 2048 kB
  4. Set Huge Pages: 88

建议使用88个大页,因此你应该用下面的命令来把vm.nr_hugepages设置为88:

  1. sysctl -w vm.nr_hugepages= 88

现在检查一下大页,你会发现大页依然没有使用((HugePages_Free = HugePages_Total):

  1. $ cat /proc/meminfo | grep -i huge
  2. AnonHugePages: 0 kB
  3. ShmemHugePages: 0 kB
  4. HugePages_Total: 88
  5. HugePages_Free: 88
  6. HugePages_Rsvd: 0
  7. HugePages_Surp: 0
  8. Hugepagesize: 2048 kB

现在,将$PGDATA/postgresql.conf 中的huge_pages设置为On,并重启数据库:

  1. $ cat /proc/meminfo | grep -i huge
  2. AnonHugePages: 0 kB
  3. ShmemHugePages: 0 kB
  4. HugePages_Total: 88
  5. HugePages_Free: 81
  6. HugePages_Rsvd: 64
  7. HugePages_Surp: 0
  8. Hugepagesize: 2048 kB

现在,可以看到已经使用了少量的大页(88-81)了。我们往数据库里面加点数据:

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

我们再看看是不是是否使用了更多的大页:

  1. $ cat /proc/meminfo | grep -i huge
  2. AnonHugePages: 0 kB
  3. ShmemHugePages: 0 kB
  4. HugePages_Total: 88
  5. HugePages_Free: 18
  6. HugePages_Rsvd: 1
  7. HugePages_Surp: 0
  8. Hugepagesize: 2048 kB

现在,绝大部分大页(88-18=70)被使用了。
注意:这个示例中的大页的配置都是很低的,在实际的大的数据库的生产环境中,是不会这样设置的。请根据自己的资源和实际的负载,来评估并设置这些大页的配置参数。

vm.swappiness

vm.swappiness是另外一个可以影响数据库性能的操作系统内核参数。这个参数可以控制Linux操作系统的sawp操作的行为。这个参数可以设置的值的范围是0到100.这个参数控制了多少内存将会写出到交换分区。0意味着禁止被写入交换分区,100表明可以进行激进的swap操作。
将这个参数设置成一个比较低的值可以获得较好的性能。
在新版的内核中,将这个参数设置为0会导致OOM killer进程将进程强杀掉。因此,如果为了安全考虑,你想将swap操作最小化,你可以将这个参数设置为1。Linux操作系统上,这个参数的默认值是60。更高的值,会导致MMU(内存管理单元)使用比RAM还要多的交换分区,而更低的值,就会将更多的数据和代码保留在内存中。
为了PosgreSQL能够获得更好的性能,将这个参数设置成一个比较低的值,是一个不错的选择。

vm.overcommit_memory / vm.overcommit_ratio

应用程序会申请内存,在不需要的时候会释放内存。但是,在某些情况下,一个应用程序会申请很多内存,并不释放掉这些内存。这就会被OOM killer给杀掉。下面是vm.overcommit_memory 参数的值以及相应的描述:

  • 0,Heuristic overcommit,允许过量申请内存,内核中默认采用的算法(解释一下,就是采用某种试探的算法,来估计本次申请是否合理,如果不合理,就会拒绝)来处理。
  • 1,允许过量使用内存,来者不拒。
  • 2,禁止过量使用的内存超过vm.overcommit_ratio。
    参考:https://www.kernel.org/doc/Documentation/vm/overcommit-accounting
    vm.overcommit_ratio 是一个可以过量食用的内存的比例。50%意味着一个2G内存的系统可以使用多达3G的内存。
    vm.overcommit_memory的值设置为2,会让PostgreSQL获得更好的 性能。将这个值设置为2,会使得服务进程的内存使用最大化的同时,没有被OOM killer进程杀掉的风险。将其设置为2会比默认的值0获得更好的数据库性能。但是,确保运内存就算超过了运行范围,也不超过过量使用规定的上限,是可以提供更高的可靠性的。这样就避免了被OOM killer干掉的风险。
    在没有交换分区的系统上,如果将设置vm.overcommit_memory
    为2,会遇到问题。参见:
    https://www.postgresql.org/docs/current/kernel-resources.html#LINUX-MEMORY-OVERCOMMIT

    vm.dirty_background_ratio / vm.dirty_background_bytes

    vm.dirty_background_ratio是脏页与内存的比例值,当到达这个比例时,脏页将会被刷回磁盘。刷回磁盘的操作是操作系统后台执行的。这个参数的值的范围是从0到100;但是这个值低于5可能不会生效,并且有些内核并不支持。在大多数Linux操作系统上,默认值是10。在写密集的操作中,将这个参数值设置的比较小可以获得比较好的性能,因为操作系统在后台会将脏的页面刷回磁盘。
    对于 vm.dirty_background_bytes,你需要根据磁盘的写入速度来进行设置。
    对于这两个参数来说,并没有最优的值,因为实际最优的值取决于硬件的。但是,在大多数情况下,将vm.dirty_background_ratio设置成5,将vm.dirty_background_bytes设置成25%,都可以将性能提升大约25%。

    vm.dirty_ratio / dirty_bytes

    这两个参数和前面说的s vm.dirty_background_ratio / dirty_background_bytes相似,除了这两个参数控制的刷脏页会在前台进行,会阻塞应用。因此,vm.dirty_ratio要设置的比vm.dirty_background_ratio高。这就确保后台刷脏页的进程尽可能在前台刷脏页的进程之前启动,从而避免阻塞应用程序。你可以调整vm.dirty_ratio与vm.dirty_background_ratio之间的差,这个取决于你的磁盘IO负载。

    总结

    当然,在性能调优的时候,你也可以调整其他参数,但是调整其他参数带来的性能提高很可能是十分微小的。我们必须知道: 对于所有类型的应用程序来说,并不是所有有的参数都是和性能相关的。对于预期的应用程序负载和类型,我们必须在所有的操作系统配置参数有一个好的折中,并且在调整参数时,对于操作系统的行为也必须了然于心。调整操作系统内核的参数并不像调整数据库的参数那样简单:因为调整操作系统内核参数是很难约定俗成的。
    我的下一个博客里面,我将会研究怎么调整PostgreSQL的配置参数。
0 评论  
添加一条新评论