pgfincore: 管理 PostgreSQL 关系页面的缓存

三月 19, 2024

摘要pgfincore模块提供了一组函数,从 PostgreSQL 管理内存中的页面。

本文包含以下部分:

  1. 描述
  2. 示例
  3. 概要
  4. 函数
  5. 调试
  6. 要求
  7. 限制

它有一组函数,使用 mincore 来探索缓存,可用于对关系进行底层管理。

描述

使用 PostgreSQL,每个表或索引会被拆分为 1 GB(通常)的段,每个段在内存中分成页面,然后在文件系统上分成块。

通过这些函数,您可以知道关系中的哪些磁盘块以及有多少个磁盘块,在操作系统的页面缓存中。它可以以 VarBit 的形式提供结果,并且可以存储在表中。然后,使用这个表,可以恢复关系中每个块的页面缓存状态,即使到了另一台服务器中,得益于流复制的机制,也依然可以恢复。

其他函数,用于在整个关系(每个段)上设置 POSIX_FADVISE 标志。更有用的状态标志,可能是 WILLNEEDDONTNEED,它们分别用于在页面缓存中换入和换出关系中每个段的块。

每个函数都至少需要一个表名或索引名(或 oid)作为参数进行调用,并遍历关系的每个段。

示例

以下是一些用法示例。如果您想了解更多详细信息,请转到函数

获取关系的当前状态

这可能有用:

redrock=# select * from pgfincore('pgbench_accounts');
      relpath       | segment | os_page_size | rel_os_pages | pages_mem | group_mem | os_pages_free | databit | pages_dirty | group_dirty
--------------------+---------+--------------+--------------+-----------+-----------+---------------+---------+-------------+-------------
 base/11874/16447   |       0 |         4096 |       262144 |    262144 |         1 |         81016 |         |           0 |           0
 base/11874/16447.1 |       1 |         4096 |        65726 |     65726 |         1 |         81016 |         |           0 |           0
(2 rows)

Time: 31.563 ms

在操作系统页面缓冲区中加载表或索引

您可能希望尝试,将表或索引保留在操作系统页面缓存中,或者在执行一条明显的大查询之前预加载表(以减少查询时间)。

为此,只需执行以下查询:

redrock=# select * from pgfadvise_willneed('pgbench_accounts');
      relpath       | os_page_size | rel_os_pages | os_pages_free 
--------------------+--------------+--------------+---------------
 base/11874/16447   |         4096 |       262144 |        169138
 base/11874/16447.1 |         4096 |        65726 |        103352
(2 rows)

Time: 4462,936 ms
  • os_page_size 报告了页面大小为 4KB。
  • rel_os_pages 是指定文件的页数。
  • os_pages_free 是内存中的空闲页面数(用于缓存)。

对表或索引等对象的操作系统页面缓冲区状态进行快照和还原

您可能希望,将表或索引还原到操作系统页面缓存中,就像进行快照时的状态一样。例如,如果您需要重新启动服务器,那么当 PostgreSQL 启动时,首次执行的查询可能都会变慢,因为 PostgreSQL 和操作系统在各自的缓存中,都没有关于这些首次的查询中涉及的关系页面。

执行一次快照和还原是非常简单的:

-- Snapshot
redrock=# create table pgfincore_snapshot as
            select 'pgbench_accounts'::text as relname, *, now() as date_snapshot
              from pgfincore('pgbench_accounts',true);

-- Restore
redrock=# select * from pgfadvise_loader('pgbench_accounts', 0, true, true,
                       (select databit from  pgfincore_snapshot
                        where relname='pgbench_accounts' and segment = 0));
     relpath      | os_page_size | os_pages_free | pages_loaded | pages_unloaded
------------------+--------------+---------------+--------------+----------------
 base/11874/16447 |         4096 |         80867 |       262144 |              0
(1 row)

Time: 35.349 ms
  • pages_loaded 报告了已读取到内存的页面数(它们可能已经在内存中)
  • pages_unloaded 报告了已从内存中删除的页面数(它们可能尚未在内存中);

概要

pgsysconf(OUT os_page_size bigint, OUT os_pages_free bigint,
          OUT os_total_pages bigint)
  RETURNS record

pgsysconf_pretty(OUT os_page_size text, OUT os_pages_free text,
                 OUT os_total_pages text)
  RETURNS record

pgfadvise(IN relname regclass, IN fork text, IN action int,
          OUT relpath text, OUT os_page_size bigint,
          OUT rel_os_pages bigint, OUT os_pages_free bigint)
  RETURNS setof record

pgfadvise_willneed(IN relname regclass,
                   OUT relpath text, OUT os_page_size bigint,
                   OUT rel_os_pages bigint, OUT os_pages_free bigint)
  RETURNS setof record

pgfadvise_dontneed(IN relname regclass,
                   OUT relpath text, OUT os_page_size bigint,
                   OUT rel_os_pages bigint, OUT os_pages_free bigint)
  RETURNS setof record

pgfadvise_normal(IN relname regclass,
                 OUT relpath text, OUT os_page_size bigint,
                 OUT rel_os_pages bigint, OUT os_pages_free bigint)
  RETURNS setof record

pgfadvise_sequential(IN relname regclass,
                     OUT relpath text, OUT os_page_size bigint,
                     OUT rel_os_pages bigint, OUT os_pages_free bigint)
  RETURNS setof record

pgfadvise_random(IN relname regclass,
                 OUT relpath text, OUT os_page_size bigint,
                 OUT rel_os_pages bigint, OUT os_pages_free bigint)
  RETURNS setof record

pgfadvise_loader(IN relname regclass, IN fork text, IN segment int,
                 IN load bool, IN unload bool, IN databit varbit,
                 OUT relpath text, OUT os_page_size bigint,
                 OUT os_pages_free bigint, OUT pages_loaded bigint,
                 OUT pages_unloaded bigint)
  RETURNS setof record

pgfadvise_loader(IN relname regclass, IN segment int,
                 IN load bool, IN unload bool, IN databit varbit,
                 OUT relpath text, OUT os_page_size bigint,
                 OUT os_pages_free bigint, OUT pages_loaded bigint,
                 OUT pages_unloaded bigint)
  RETURNS setof record

pgfincore(IN relname regclass, IN fork text, IN getdatabit bool,
          OUT relpath text, OUT segment int, OUT os_page_size bigint,
          OUT rel_os_pages bigint, OUT pages_mem bigint,
          OUT group_mem bigint, OUT os_pages_free bigint,
          OUT databit varbit, OUT pages_dirty bigint,
          OUT group_dirty bigint)
  RETURNS setof record

pgfincore(IN relname regclass, IN getdatabit bool,
          OUT relpath text, OUT segment int, OUT os_page_size bigint,
          OUT rel_os_pages bigint, OUT pages_mem bigint,
          OUT group_mem bigint, OUT os_pages_free bigint,
          OUT databit varbit, OUT pages_dirty bigint,
          OUT group_dirty bigint)
 RETURNS setof record

pgfincore(IN relname regclass,
          OUT relpath text, OUT segment int, OUT os_page_size bigint,
          OUT rel_os_pages bigint, OUT pages_mem bigint,
          OUT group_mem bigint, OUT os_pages_free bigint,
          OUT databit varbit, OUT pages_dirty bigint,
          OUT group_dirty bigint)
  RETURNS setof record

函数

pgsysconf

此函数输出操作系统层面的块大小,和操作系统页面缓冲区中的空闲页面数。

redrock=# select * from pgsysconf();
 os_page_size | os_pages_free | os_total_pages 
--------------+---------------+----------------
         4096 |         80431 |        4094174

pgsysconf_pretty

与上面相同,但输出进行了美化。

redrock=# select * from pgsysconf_pretty();
 os_page_size | os_pages_free | os_total_pages 
--------------+---------------+----------------
4096 bytes   | 314 MB        | 16 GB

pgfadvise_WILLNEED

此函数在当前关系上设置 WILLNEED 标志。这意味着操作系统将尝试对关系加载尽可能多的页面。主要思想是,在服务器启动时预加载文件,也许是根据缓存命中率,或者根据关系/索引的访问需要。

redrock=# select * from pgfadvise_willneed('pgbench_accounts');
      relpath       | os_page_size | rel_os_pages | os_pages_free 
--------------------+--------------+--------------+---------------
 base/11874/16447   |         4096 |       262144 |         80650
 base/11874/16447.1 |         4096 |        65726 |         80650

pgfadvise_DONTNEED

此函数在当前关系上设置 DONTNEED 标志。这意味着如果操作系统需要释放一些内存,它将首先换出该文件的页面。主要思想是在文件不再有用时,换出文件(而不是可能更有意义的页面)

redrock=# select * from pgfadvise_dontneed('pgbench_accounts');
      relpath       | os_page_size | rel_os_pages | os_pages_free
--------------------+--------------+--------------+---------------
 base/11874/16447   |         4096 |       262144 |        342071
 base/11874/16447.1 |         4096 |        65726 |        408103

pgfadvise_NORMAL

此函数在当前关系上设置 NORMAL 标志。

pgfadvise_SEQUENTIAL

此函数在当前关系上设置 SEQUENTIAL 标志。

pgfadvise_RANDOM

此函数在当前关系上设置 RANDOM 标志。

pgfadvise_loader

此函数允许直接和页面缓存交互。它可以用于,根据用来换入换出的表示页面映射的 varbit,从内存中对页面进行换入/换出。

使用关系 pgbench_accounts,0 号段,任意 varbit 映射:

-- Loading and Unloading
redrock=# select * from pgfadvise_loader('pgbench_accounts', 0, true, true, B'111000');
     relpath      | os_page_size | os_pages_free | pages_loaded | pages_unloaded 
------------------+--------------+---------------+--------------+----------------
 base/11874/16447 |         4096 |        408376 |            3 |              3
 
-- Loading
redrock=# select * from pgfadvise_loader('pgbench_accounts', 0, true, false, B'111000');
     relpath      | os_page_size | os_pages_free | pages_loaded | pages_unloaded 
------------------+--------------+---------------+--------------+----------------
 base/11874/16447 |         4096 |        408370 |            3 |              0
 
-- Unloading
redrock=# select * from pgfadvise_loader('pgbench_accounts', 0, false, true, B'111000');
    relpath      | os_page_size | os_pages_free | pages_loaded | pages_unloaded 
------------------+--------------+---------------+--------------+----------------
 base/11874/16447 |         4096 |        408370 |            0 |              3

pgfincore

此函数提供有关文件系统缓存(页面缓存)的信息。

redrock=# select * from pgfincore('pgbench_accounts');
      relpath       | segment | os_page_size | rel_os_pages | pages_mem | group_mem | os_pages_free | databit | pages_dirty | group_dirty  
--------------------+---------+--------------+--------------+-----------+-----------+---------------+---------+-------------+-------------
 base/11874/16447   |       0 |         4096 |       262144 |         3 |         1 |        408444 |         |           0 |           0
 base/11874/16447.1 |       1 |         4096 |        65726 |         0 |         0 |        408444 |         |           0 |           0

对于指定的关系,它返回:

  • relpath:关系的文件路径
  • segment:分析的段号
  • os_page_size:单页大小
  • rel_os_pages:关系中的总页面数
  • pages_mem:在页面缓存中关系的页面总数。(不是 PostgreSQL 中的共享缓冲区,而是操作系统缓存)
  • group_mem:相邻的 pages_mem 分组数
  • os_page_free:操作系统页面缓存中的空闲页面数
  • databit:文件的 varbit 映射,由于其大小,输出没有意义。 使用pgfincore('pgbench_accounts', true)激活它。
  • pages_dirty:如果定义了 HAVE_FINCORE 常量,并且平台有提供相关信息,像 pages_mem 但只对于脏页面。
  • group_dirty:如果定义了 HAVE_FINCORE 常量,并且平台有提供相关信息,像 group_mem 但只对于脏页面。

调试

您可以使用以下错误级别来调试 PgFincore:DEBUG1DEBUG5

例如:

SET client_min_messages TO debug1; -- debug5 is only usefull to trace each block

要求

PgFincore 需要用到 mincore() 或 fincore() 和 POSIX_FADVISE。

限制

  • 当平台不支持 POSIX_FADVISE 时,PgFincore 有一种受限模式。

  • PgFincore 需要 PostgreSQL 版本 >= 8.3

  • PgFincore 不支持 Windows 系统。