PostgreSQL:レプリケーションされないテーブルの犯人はUnlogged Tableでした

2023.12.18

本記事は PostgreSQL Advent Calendar 2023 の18日目です。

昨日は@discus_hamburg さんによる「Mac De Oracle: 帰ってきた! 標準はあるにはあるが癖の多いSQL #8、Hash Joinさせるにも癖が出る」でした。

論理レプリケートされないテーブルがある!

某日、PostgreSQLのメジャーアップグレードに関わる機会がありました。

ことなるメジャーバージョン間で論理レプリケーションを構築し、各テーブルのレコード数を比較すると、全くレプリケートされていないテーブルが見つかりました。

当初は権限の問題を疑いましたが、問題のテーブルはWALにログを書き込まない UNLOGGED TABLE だったのが原因でした。

本記事では、UNLOGGED TABLEとは何か、そして、PostgreSQL 15からはUNLOGGEDシーケンスにも対応したことについて解説します。

そもそもログとは?

データ更新時には、該当するトランザクションログのWAL(write-ahead log)への同期書き込み(永続化)をもってコミットが成功したと見なされ、その後データファイルにも書き込まれます。 このようにして、コミットに伴う同期処理が軽減され、トランザクションログを効率的にまとめてデータファイルに非同期に書き込み、データベースがクラッシュしてもトランザクションログを適用することでリカバリが可能です。

PostgreSQLに限らず、一般的なDBMSも同様の仕組みを有しています。

UNLOGGED Tableとは?

PostgreSQLのドキュメントによると、UNLOGGED TABLEの場合、テーブル更新時にはWALには書き込まれないとあります(Data written to unlogged tables is not written to the write-ahead log)。耐久性を犠牲に、更新処理が高速になります。

たとえば、ETL処理のバルクロードのターゲットをUNLOGGEDテーブルにすると、処理時間が短縮したり(参考)、WALファイルのサイズが小さくするといったことが期待されます。

crunchydata による pgbench を使ったベンチマークでは、INSERT で2倍、UPDATE で3倍速くなったと報告されています。

一方で、WALに依存する以下のような問題が起こります。

  • システムクラッシュ時にテーブルがトランケートされる
  • ストリーミングレプリケーションされない
  • 論理レプリケーションされない

UNLOGGED テーブル・シーケンスの作り方

UNLOGGED TABLEの作り方は簡単です。

CREATE TABLE 時に UNLOGGED キーワードをつけるだけです。

CREATE UNLOGGED TABLE tbl (id int);

PostgreSQL 15 からはシーケンスにも対応し、テーブルと同じく UNLOGGED キーワードをつけるだけです。

CREATE UNLOGGED SEQUENCE seq;

さらには、UNLOGGED テーブルとともに identityserial で作成されたシーケンスも UNLOGGED 扱いです。

CREATE UNLOGGED TABLE test_unlogged (
  id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
  data text);

試しに LOGGED バージョンと比較してみましょう。

-- logged table
CREATE TABLE test_logged (
    id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    data text);

-- unlogged table
CREATE UNLOGGED TABLE test_unlogged (
    id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    data text);

SELECT relname, relpersistence
FROM pg_class
WHERE relname LIKE 'test_%'
ORDER BY relname;

       relname        | relpersistence
----------------------+----------------
 test_logged          | p
 test_logged_id_seq   | p
 test_logged_pkey     | p
 test_unlogged        | u
 test_unlogged_id_seq | u
 test_unlogged_pkey   | u
(6 rows)

pg_class テーブルの relpersistence カラムを確認すると、通常のテーブル・シーケンスの場合、p(=permanent)UNLOGGED テーブル・シーケンスの場合、u(= unlogged) となっていますね。なお、セッションに閉じた一時テーブル・シーケンスの場合、 t(=temporary) となります。

最後に

PostgreSQLにはトランザクションログを生成しない(UNLOGGED)テーブルやシーケンスを作成でき、データの永続性を犠牲にデータ更新を高速に行えます。

ただし、 UNLOGGED テーブルやシーケンスはWALに依存した機能を使えない点にはご注意ください。

参考