这个问题偶尔会在不同的地方出现——PostgreSQL使用了太多的内存,这是为什么,要怎么减少?
在进行“优化”之前,我们应该先理解问题。我们的做法是:标准工具 ps
和top
。如何做、为何这么做?让我们来看看。
这是一个非常简单的PostgreSQL实例,配置了4 GB
的共享缓冲区和100MB
的work_mem
。运行之后,ps显示如下:
=$ ps -u pgdba ufUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDpgdba 32324 0.0 0.0 79788 1900 ? S 14:26 0:00 sshd: pgdba@pts/13pgdba 32325 0.0 0.0 25844 5788 pts/13 Ss+ 14:26 0:00 \_ -bashpgdba 27502 0.0 0.8 4344112 109724 ? S 14:18 0:00 home/pgdba/work/bin/postgrespgdba 27506 0.0 0.0 24792 620 ? Ss 14:18 0:00 \_ postgres: logger processpgdba 27508 1.5 34.7 4346688 4274752 ? Ss 14:18 0:14 \_ postgres: checkpointer processpgdba 27509 0.2 12.1 4346164 1495780 ? Ss 14:18 0:02 \_ postgres: writer processpgdba 27510 0.3 0.1 4346164 17292 ? Ss 14:18 0:03 \_ postgres: wal writer processpgdba 27511 0.0 0.0 4347168 2408 ? Ss 14:18 0:00 \_ postgres: autovacuum launcher processpgdba 27512 0.0 0.0 26888 856 ? Ss 14:18 0:00 \_ postgres: archiver process last was 00000001000000060000004Dpgdba 27513 0.0 0.0 27184 1160 ? Ss 14:18 0:00 \_ postgres: stats collector processpgdba 27713 5.6 34.8 4347268 4285716 ? Ss 14:19 0:51 \_ postgres: depesz depesz [local] idlepgdba 27722 2.6 3.1 4347412 392704 ? Ss 14:19 0:23 \_ postgres: depesz depesz [local] idlepgdba 27726 15.8 35.0 4352560 4309776 ? Ss 14:19 2:25 \_ postgres: depesz depesz [local] idle
top
显示的数据基本相同。
这很可疑,因为free
显示:
=$ freetotal used free shared buffers cachedMem: 12296140 12144356 151784 0 26828 10644460-/+ buffers/cache: 1473068 10823072Swap: 0 0 0
也就是只使用了1.5GB
的内存(还有大约10GB
用作磁盘缓存——如果有应用程序需要更多的内存,可以释放)。
为什么会在ps
中看到这么大的数字呢?
首先,我们需要忽略VSZ
列。最重要的是RSS
。但它仍然不是很有用:
=$ ps -u pgdba o pid,rss:8,cmd | awk 'NR>1 {A+=$2} {print} END{print "Total RSS: " A}'PID RSS CMD27502 109724 /home/pgdba/work/bin/postgres27506 620 postgres: logger process27508 4274752 postgres: checkpointer process27509 1755420 postgres: writer process27510 17292 postgres: wal writer process27511 2408 postgres: autovacuum launcher process27512 856 postgres: archiver process last was 00000001000000060000004D27513 1160 postgres: stats collector process27713 4285716 postgres: depesz depesz [local] idle27722 392700 postgres: depesz depesz [local] idle27726 4309776 postgres: depesz depesz [local] idle32324 1900 sshd: pgdba@pts/1332325 5788 -bashTotal RSS: 15158112
可以看到这个时刻的总内存是15GB
,比我拥有的内存更大。那么,真正的内存使用情况是什么呢?比如:如果我杀了Pg
的进程,我能获得多少内存?
我们可以从/proc
目录的/filesystem/mountpoint/fairy
查看内存的确切用途。
Linux上的每个进程在/proc
中都有一个目录。在这个目录中有许多文件和目录。不要被记录的文件大小所迷惑——它们都只有0
字节,但是它们确实包含信息。
我们感兴趣的一个文件是smaps
。
它的内容是这样的:
=$ sudo head -n 20 /proc/27713/smaps00400000-00914000 r-xp 00000000 09:00 3545633 /home/pgdba/work/bin/postgresSize: 5200 kBRss: 964 kBPss: 214 kBShared_Clean: 964 kBShared_Dirty: 0 kBPrivate_Clean: 0 kBPrivate_Dirty: 0 kBReferenced: 964 kBAnonymous: 0 kBAnonHugePages: 0 kBSwap: 0 kBKernelPageSize: 4 kBMMUPageSize: 4 kBLocked: 0 kB00b13000-00b14000 r--p 00513000 09:00 3545633 /home/pgdba/work/bin/postgresSize: 4 kBRss: 4 kBPss: 0 kBShared_Clean: 0 kB...
27713
进程的详细说明在smaps
文件里超过2000
行,所以没有完全展示它。
不管怎样,进程27713
根据ps
使用4285716 KB
的内存。为什么这么大?grep
一下,我们看到:
=$ sudo grep -B1 -E '^Size: *[0-9]{6}' /proc/27713/smaps7fde8dacc000-7fdf952d6000 rw-s 00000000 00:04 232882235 /SYSV005a5501 (deleted)Size: 4317224 kB
只有一个block
的大小超过100MB
,而且它的大小非常接近整个进程的大小。
关于它的完整信息:
7fde8dacc000-7fdf952d6000 rw-s 00000000 00:04 232882235 /SYSV005a5501 (deleted)Size: 4317224 kBRss: 4280924 kBPss: 1245734 kBShared_Clean: 0 kBShared_Dirty: 4280924 kBPrivate_Clean: 0 kBPrivate_Dirty: 0 kBReferenced: 4280924 kBAnonymous: 0 kBAnonHugePages: 0 kBSwap: 0 kBKernelPageSize: 4 kBMMUPageSize: 4 kBLocked: 0 kB
大多数信息或多或少是含义模糊的,但我们看到了一些东西:
它是共享内存(第一行包含
rw-s
,s
说明是共享的)通过(
/SYSV… deleted
,看起来共享内存是使用mmaping
删除文件来完成的——因此在free
输出中,这些内存将在Cached
中,而不是Used
列。共享块的大小为
4317224
,其中4280924
实际上是固定在内存中的
因为这是共享缓冲区。但问题是,大多数的后端进程都使用了共享缓冲区。更糟糕的是,它们也不总是在同一范围上。例如,进程27722
有相同的共享缓冲区数据:
=$ sudo grep -A14 7fde8dacc000-7fdf952d6000 /proc/27722/smaps7fde8dacc000-7fdf952d6000 rw-s 00000000 00:04 232882235 /SYSV005a5501 (deleted)Size: 4317224 kBRss: 388652 kBPss: 95756 kBShared_Clean: 0 kBShared_Dirty: 388652 kBPrivate_Clean: 0 kBPrivate_Dirty: 0 kBReferenced: 388652 kBAnonymous: 0 kBAnonHugePages: 0 kBSwap: 0 kBKernelPageSize: 4 kBMMUPageSize: 4 kBLocked: 0 kB
在这里,我们看到这个进程只请求/使用了388MB
的内存。
因此计算将是复杂的。例如——我们可能会有两个进程,每个使用400 MB
的shared_buffers
,但它并没有告诉我们它实际上是使用多少内存。因为可能是:他们使用100 MB
的相同的缓冲区,和300 MB
的不同缓冲区,所以在总内存使用量是700 MB
。
我们知道这个shared_buffers
块的总大小是4317224
。但是其他东西呢?例如库——它们可以由内核在多个进程之间共享。
幸运的是,2007年Fengguang Wu(之前也写过)为内核发送了一个非常酷的补丁——它为smaps
添加了“Pss”信息。
基本上,Pss
最多是Rss
,但是如果多个进程使用相同的内存页,Pss
会减少。
这就是为什么上面的Pss
比Rss
的值低得多。例如,在上一个例子中。Rss
提供了388652
,但是Pss
只有95756
,这意味着这个后端使用的大多数页面也被其他3个后端使用。
所以,现在,了解了Pss
,我们终于可以得到一个正在运行的pg集群的实际内存使用情况:
=$ ps -u pgdba o pid= | sed 's#.*#/proc/&/smaps#' | xargs sudo grep ^Pss: | awk '{A+=$2} END{print A}'4329040
如果你只是说“天啊,他跑了什么?”,让我解释一下。第一个命令:
=$ ps -u pgdba o pid=27502...32325
返回pgdba
用户的pids
(通常您想要的是postgres
,但我不一样,我是以pgdba
的身份运行PostgreSQL)。
第二- sed
将pids
更改为smaps
文件的路径:
=$ ps -u pgdba o pid= | sed 's#.*#/proc/&/smaps#'/proc/27502/smaps.../proc/32325/smaps
然后我简单的从sed
中过滤以Pss
开头的行。结果返回很多行,比如:
/proc/32325/smaps:Pss: 0 kB/proc/32325/smaps:Pss: 4 kB/proc/32325/smaps:Pss: 4 kB
然后awk
统计第二列(即大小)。得到的数字是4329040
,单位是千字节。
因此,理论上,如果我停止Pg,我将回收那么多内存。让我们看看这是不是真的:
=$ free; pg_ctl -m immediate stop; freetotal used free shared buffers cachedMem: 12296140 12145424 150716 0 40708 10640968-/+ buffers/cache: 1463748 10832392Swap: 0 0 0waiting FOR server TO shut down.... doneserver stoppedtotal used free shared buffers cachedMem: 12296140 7781960 4514180 0 40856 6325092-/+ buffers/cache: 1416012 10880128Swap: 0 0 0
使用的内存从12145424
下降到7781960
——这意味着我得到了4363464 kB
的内存。这甚至比预期的4329040
略高,但已经足够接近了。正如预期的那样,它大部分来自磁盘缓存,因为它用于shared_buffers
。
这个方法可以用来估计通过杀死单个后端进程可以回收多少内存吗?
也可以说是也可以说不是。关闭整个Pg意味着正在使用的共享内存可以被释放。在正常环境中,当您终止后端进程时,您最终只释放该后端私有的内存。这个数字通常很低。
例如,在另一台机器上,有更令人印象深刻的硬件环境:
=> ps uxf | grep USER.db_name | sort -nk6 | tail -n 1 | tee >( cat - >&2) | awk '{system("cat /proc/"$2"/smaps")}' | grep ^Private | awk '{A+=$2} END{print A}'postgres 5278 8.2 0.3 107465132 1727408 ? Ss 13:21 0:03 \_ postgres: USER db_name aa.bbb.cc.dd(eeeee) idle52580
也就是说,该进程有1.7GB
的RSS
(在ps
输出中可见),但是只有52MB
是私有内存,如果它被杀死,私有内存将被释放。
所以在这儿你不能使用Pss
,但你可以使用来自smaps
的Private_*
数据来获取私有内存。
总而言之——PostgreSQL使用的内存比乍一看要少得多,虽然可以得到非常准确的数字——但需要执行一些shell脚本来获得它们。
本文翻译自:https://www.depesz.com/2012/06/09/how-much-ram-is-postgresql-using/




