PostgreSQL 教程: 计算内存使用量

四月 23, 2026

摘要:在本教程中,您将学习如何计算 PostgreSQL 的内存使用量。

目录

PostgreSQL 架构

PostgreSQL 的架构基于三个基本部分:进程、内存和磁盘。

内存可分为两类:

  • 本地内存:由每个后端进程加载,供自己用于查询处理。它分为以下子类:
    • 工作内存:工作内存用于按 ORDER BY 和 DISTINCT 操作对元组进行排序,以及连接表。
    • 临时缓冲区:用于存储临时表。
    • 本地缓存:用于缓存执行计划、关系和系统表信息。
  • 共享内存:由 PostgreSQL 服务器在启动时分配,供所有进程使用。它分为以下子类:
    • 共享缓冲池:PostgreSQL 从磁盘加载包含表和索引的页面,以直接从内存工作,从而减少磁盘访问。
    • WAL 缓冲区:WAL 数据是 PostgreSQL 中的事务日志,包含了在数据库中进行的更改。WAL 缓冲区是在将 WAL 数据写入磁盘到 WAL 文件之前临时存储的区域。在提交事务的时候,PostgreSQL 会刷写 WAL 数据到磁盘。这对于避免在服务器发生故障时丢失信息非常重要。

Linux 进程 smaps 文件

/proc/PID/smaps 是一个基于 maps 文件的扩展,它会显示每个进程映射的内存消耗。对于每个映射(又名虚拟内存区域或 VMA),有一系列行,如下所示:

00400000-00914000 r-xp 00000000 09:00 3545633                            /usr/pgsql/bin/postgres
Size:               5200 kB
Rss:                 964 kB
Pss:                 214 kB
Shared_Clean:        964 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:         0 kB
Referenced:          964 kB
Anonymous:             0 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Locked:                0 kB
00b13000-00b14000 r--p 00513000 09:00 3545633                            /usr/pgsql/bin/postgres
Size:                  4 kB
Rss:                   4 kB
Pss:                   0 kB
Shared_Clean:          0 kB
...

其中第一行显示的信息与 /proc/PID/maps 中显示的映射信息相同。以下几行显示了映射的大小(size);支持 VMA 时分配的每个页面的大小(KernelPageSize),通常与页表条目中的大小相同;支持 VMA 时 MMU 使用的页面大小(在大多数情况下,与 KernelPageSize 相同);当前驻留在 RAM 中的映射量(RSS);该进程在此映射中的比例份额(PSS);以及映射中干净和弄脏的共享页面和私有页面的数量。

一个进程的 “比例集大小”(PSS)是它在内存中的页面数,其中每个页除以共享它的进程数。因此,如果一个进程有 1000 个私有页面,还有 1000 个页面与另外一个进程共享,则其 PSS 将为 1500。“Private_Dirty” 是 PSS 中由脏页组成的部分。

内存使用量计算

我们先创建一个简单的存储过程,用于收集数据库进程的内存使用量:

CREATE OR REPLACE PROCEDURE gather_proc_memstat()
AS $$
DECLARE
  pid integer;
BEGIN
  DROP TABLE IF EXISTS proc_smaps;
  -- 显式保留行,以便 COMMIT 不会清空表
  CREATE TEMP TABLE proc_smaps (
    pid  integer,
    type varchar,
    size bigint,
    unit varchar
  ) ON COMMIT PRESERVE ROWS;
  COMMIT;

  FOR pid IN
    SELECT pg_stat_get_backend_pid(s.backendid)
      FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s
  LOOP
    EXECUTE format($sql$
      COPY proc_smaps FROM PROGRAM
        $shell$ awk '$1 ~ /:/ {print %s, $1, $2, $3}' /proc/%s/smaps $shell$
        WITH (DELIMITER ' ', ON_ERROR ignore)
	  $sql$, pid, pid);
  END LOOP;
END $$ LANGUAGE plpgsql;

要在 Linux 上计算 PostgreSQL 进程的内存使用量,请执行以下操作:

CALL gather_proc_memstat();

SELECT pid, sum(size) AS uss_in_kb, backend_type
  FROM proc_smaps JOIN pg_stat_activity USING (pid)
  WHERE type LIKE 'Private%'
  GROUP BY pid, backend_type
  ORDER BY uss_in_kb DESC;
 pid | uss_in_kb |         backend_type
-----+-----------+------------------------------
  21 |     12760 | checkpointer
  78 |      5888 | client backend
  80 |      5880 | client backend
  79 |      5868 | client backend
  77 |      5768 | client backend
  82 |      5708 | client backend
  83 |      5652 | client backend
  81 |      5632 | client backend
  74 |      5616 | client backend
  76 |      5548 | client backend
  75 |      5436 | client backend
  90 |      4340 | client backend
  18 |      3116 | io worker
  27 |      1340 | undo launcher
  25 |       588 | autovacuum launcher
  26 |       532 | logical replication launcher
  19 |       468 | io worker
  20 |       404 | io worker
  24 |       284 | walwriter
  22 |       276 | background writer
(20 rows)
  • 首先,获取 PostgreSQL 中运行的所有进程的进程 PID
  • 然后获取每个进程的相应smaps文件(/proc/PID/smaps)的名称
  • 然后从该文件中获取Private行,Private_CleanPrivate_Dirty表示进程的私有、不可共享的已清理(Clean)和已脏(Dirty)内存大小
  • 最后,将数字相加

了解更多

PostgreSQL 教程: 检查后端进程的内存使用情况

PostgreSQL 教程: 处理服务器可用内存不足的问题

PostgreSQL 教程: 启用 Linux 大页 Huge Pages

PostgreSQL 监控