1. ホーム
  2. データベース
  3. ポストグレスキュー

PostgreSQLでバッファキャッシュにデータを読み込む方法

2022-01-19 19:40:25

データのアクセスはディスク上よりもキャッシュ上の方が圧倒的に速いことは周知の事実ですが、Oracleのインメモリとやや似ているpgで、指定したデータをキャッシュにロードするにはどうすればよいのでしょう。

もちろん、メモリにデータをロードすることが常に良いとは限らないことに注意する必要があります。メモリはディスクに比べて常に制限されているので、多くの人はアクセスを高速化するために特別な場合にだけ必要なデータをメモリにロードしています。

pg_prewarmプラグインを使用して、与えられたテーブルをOS Bufferまたはpg共有バッファにロードすることができます。

インストール方法

bill=# create extension pg_prewarm ;
CREATE EXTENSION

パフォーマンステストです。

テストテーブルt1,t2を構築し、それぞれ1000Wのテストデータを挿入する。

bill=# create table t1(id int,info text);
CREATE TABLE
bill=# create table t2(id int,info text);
CREATE TABLE
bill=# insert into t1 select generate_series(1,10000000),md5(random()::text);
INSERT 0 10000000
bill=# insert into t2 select generate_series(1,10000000),md5(random()::text);
INSERT 0 10000000

テストの前にshared_bufferを空にします。以下のSQLでshared_bufferの使用量を確認できます。
pg_buffercache プラグインをインストールします。

bill=# create extension pg_buffercache;
CREATE EXTENSION

shared_buffer の使用状況を問い合わせる。

SELECT
    c.relname,
    count(*) AS buffers
FROM pg_buffercache b
INNER JOIN pg_class c
   ON b.relfilenode = pg_relation_filenode(c.oid)
    AND b.reldatabase IN (0, (SELECT oid FROM pg_database
WHERE datname = current_database()))
GROUP BY c.relname
ORDER BY 2 DESC;
                 relname | buffers
-----------------------------------------+---------
 pg_attribute | 36
 pg_proc | 27
 pg_class | 15
 pg_operator | 14
 pg_depend_reference_index | 13
 pg_depend | 11
 pg_attribute_relid_attnum_index | 10
 pg_proc_proname_args_nsp_index | 9
......

t1テーブルとt2テーブルの両方がshared_bufferにないことがわかりますので、手動でt2テーブルをshared_bufferにロードしてみましょう。

bill=# SELECT pg_prewarm('t2');
 pg_prewarm
------------
      83334
(1 row)

パフォーマンステストです。

t2テーブルのフルテーブルスキャンのパフォーマンスが格段に向上していることがお分かりいただけると思います。

bill=# explain analyze select * from t1;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Seq Scan on t1 (cost=0.00..183334.80 rows=10000080 width=37) (actual time=0.060..772.902 rows=10000000 loops=1)
 Planning Time: 0.294 ms
 Execution Time: 1044.922 ms
(3 rows)

Time: 1045.722 ms (00:01.046)

bill=# explain analyze select * from t2;
                                                    QUERY PLAN
------------------------------------------------------------------------------------------------------------------
 Seq Scan on t2 (cost=0.00..183334.80 rows=10000080 width=37) (actual time=0.012..519.691 rows=10000000 loops=1)
 Planning Time: 0.280 ms
 Execution Time: 790.607 ms
(3 rows)

Time: 791.314 ms

pg_prewarmのその他の説明です。

pg_prewarm 関数について説明します。
この関数は、以下の記述で作成されます。

CREATE FUNCTION pg_prewarm(regclass,
mode text default buffer,
fork text default main,
first_block int8 default null,
last_block int8 default null)
RETURNS int8
AS MODULE_PATHNAME, pg_prewarm
LANGUAGE C

パラメータは以下の通りです。

  • レジクラス : プリウォームで使用するテーブルの名前
  • モード プリフェッチはOSキャッシュへの非同期プリフェッチ、リードは同期プリフェッチ、バッファはPGの共有バッファへの同期リードを意味します。
  • フォーク : 関係フォークのタイプ。通常はmainが使用され、その他にvisibilitymapやfsmがあります。
  • first_block & last_block : 開始ブロック番号と終了ブロック番号。テーブルはfirst_block=0であり、last_blockはpg_classのrelpagesフィールドから取得することができます。
  • RETURNS int8 : この関数は、pg_prewarm が処理したブロック数 (整数) を返します。

テーブルを選択し、テーブル全体を照会してデータをキャッシュに読み込むことができるのに、なぜpg_prewarmを使用する必要があるのでしょうか?と思う人もいるかもしれません。その理由は、shared_buffer/4より大きいテーブルのフルテーブルスキャンでは、一般的にpgはshared_bufferのすべてを使用せず、shared_bufferの一部のみを使用するからです。

参考リンク

https://www.postgresql.org/docs/13/pgprewarm.html
https://www.postgresql.org/docs/13/pgbuffercache.html

PostgreSQLのバッファキャッシュへのデータロードについては、この記事がすべてです。PostgreSQLのバッファキャッシュへのデータロードについては、Script Houseの過去の記事を検索するか、引き続き以下の関連記事を参照してください。