1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PostgreSQLの裏側を学びたい(Step7:WAL Writer(WALライター)を学ぶ)

1
Posted at

はじめに

前回までの記事の続きです。今回はWAL Writer(WALライター)について学んでいきます。
今回も座学の部分はChatGPTに解説してもらいます。

🧩 一言でいうと

データの変更履歴(ログ)を安全にディスクへ書き込む人。

PostgreSQLの中では、「変更内容を記録するログ(WAL)」が存在します。
このログを確実にディスクに書き込むのが WAL Writer の仕事です。


💡 WAL(Write-Ahead Logging)とは?

「Write-Ahead(=先に書く)」という名前の通り、
データベースの変更を実行する前に、まず“ログ”に書くという考え方です。


なぜ“先にログ”を書くのか?

理由はシンプルで:

💥 サーバが落ちても復旧できるようにするため。

データの実体(テーブルなど)を書き換える前に、
「何をどう変える予定か」をWALログに残しておくと、
クラッシュしても そのログを使ってやり直しができます。


⚙️ WAL Writerの役割(ざっくり構造)

🧱 WAL Writerがやっていること

内容 詳細
🔁 タイミング 数百ミリ秒ごとに自動で実行(デフォルト200ms)
💾 書く対象 WALバッファ(メモリ上にあるログの一時領域)
📦 書き込み先 ディスク上の pg_wal/ ディレクトリ
🔒 目的 クラッシュしても変更を再現できるようにする(耐障害性)
⚙️ 関連設定 wal_writer_delay(間隔), wal_sync_method(同期方式)

📁 WALファイルはどこにある?

PostgreSQLのデータディレクトリ内にあります。

$ ls /var/lib/postgresql/data/pg_wal
000000010000000000000001
000000010000000000000002
000000010000000000000003

各ファイルは 16MB の固定サイズで、
トランザクションの変更ログが順番に記録されています。


💬 WAL Writerのイメージたとえ

登場人物 役割
🧾 WAL(ログ) 「何を変更したか」のメモ帳
🧱 WAL Writer そのメモ帳を定期的に金庫(ディスク)にしまう係
🧹 Writer 実際のデータ(部屋)を片付ける係
💾 Checkpointer 金庫の内容を確定して「ここまでは安全」と記録する係

🔁 実際の流れ(内部動作)

1️⃣ アプリがUPDATEを実行
→ 変更内容をWALバッファに記録

2️⃣ WAL Writerが動く(約200msごと)
→ バッファに溜まったWALを pg_wal に書き出す

3️⃣ トランザクションがCOMMIT
→ COMMIT時にもfsyncが走る(強制同期)

4️⃣ チェックポイント時
→ Checkpointerが「この時点までは安全」とWALとデータの整合を取る

Hands-on

では実際にWALを見ていこうと思います!

プロセスを確認
~/develop/postgresql_study  (main)$ docker compose exec db ps aux | grep walwriter
postgres    31  0.0  0.0 217864 11408 ?        Ss   Nov02   0:00 postgres: walwriter 
WALファイルの存在を確認
~/develop/postgresql_study  (main)$ docker compose exec db ls -lh /var/lib/postgresql/data/pg_wal/
total 32M
-rw------- 1 postgres postgres 16M Nov  2 13:40 000000010000000000000001
-rw------- 1 root     root     16M Nov  2 13:40 000000010000000000000002
drwx------ 2 postgres root      64 Nov  2 06:50 archive_status
INSERTを流して増加を観察
~/develop/postgresql_study  (main)$ docker compose exec db psql -U user -d mydatabase -c "\
                                    INSERT INTO test_cp(data)
                                    SELECT md5(random()::text) FROM generate_series(1,100000);"
INSERT 0 100000
~/develop/postgresql_study  (main)$ docker compose exec db ls -lh /var/lib/postgresql/data/pg_wal/
total 32M
-rw------- 1 postgres postgres 16M Nov  3 02:07 000000010000000000000001
-rw------- 1 postgres postgres 16M Nov  3 02:07 000000010000000000000002
drwx------ 2 postgres root      64 Nov  2 06:50 archive_status

なるほど。確かに更新日付が更新されているから更新されていそう。
この中身を見てみたい。中身を見ていきます

~/develop/postgresql_study  (main)$ docker compose exec db bash -lc 'su - postgres -c "/usr/lib/postgresql/16/bin/pg_waldump /var/lib/postgresql/data/pg_wal/000000010000000000000002 | head -n 10"'
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/02000050, prev 0/01FFFFC8, desc: INSERT off: 42, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/020000B0, prev 0/02000050, desc: INSERT off: 43, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/02000110, prev 0/020000B0, desc: INSERT off: 44, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/02000170, prev 0/02000110, desc: INSERT off: 45, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/020001D0, prev 0/02000170, desc: INSERT off: 46, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/02000230, prev 0/020001D0, desc: INSERT off: 47, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/02000290, prev 0/02000230, desc: INSERT off: 48, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/020002F0, prev 0/02000290, desc: INSERT off: 49, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/02000350, prev 0/020002F0, desc: INSERT off: 50, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot):     92/    92, tx:        736, lsn: 0/020003B0, prev 0/02000350, desc: INSERT off: 51, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583

→なんか、Heap領域に何かやっている。Insert、blkっていう言葉はわかるけど、うーんよくわからない。
もう一度ChatGPTにわかりやすく解説してもらう。解説を聞いてもまだ難しいです。。。。

ChatGPT解説 ## 🧠 今回やったこと

PostgreSQLの「WAL(Write Ahead Log)」を読み出して、
実際に INSERT がどんな風に記録されているのかを確認します。

実行したSQLは以下の通り👇

INSERT INTO test_cp(data)
SELECT md5(random()::text)
FROM generate_series(1, 100000);

📄 WALログを読み出す

docker compose exec db bash -lc \
'su - postgres -c "/usr/lib/postgresql/16/bin/pg_waldump /var/lib/postgresql/data/pg_wal/000000010000000000000002 | head -n 10"'

出力例👇

rmgr: Heap        len (rec/tot): 92/92, tx: 736, lsn: 0/02000050, prev 0/01FFFFC8, desc: INSERT off: 42, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot): 92/92, tx: 736, lsn: 0/020000B0, prev 0/02000050, desc: INSERT off: 43, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot): 92/92, tx: 736, lsn: 0/02000110, prev 0/020000B0, desc: INSERT off: 44, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
rmgr: Heap        len (rec/tot): 92/92, tx: 736, lsn: 0/02000170, prev 0/02000110, desc: INSERT off: 45, flags: 0x00, blkref #0: rel 1663/16384/16388 blk 583
...

🧩 各項目の意味(やさしく解説)

項目 意味 やさしい説明
rmgr: Heap テーブル(ヒープ)操作 INSERT, UPDATE, DELETEなどのテーブル操作
tx: 736 トランザクションID このINSERT操作の作業番号
desc: INSERT off: 42 ページ内の位置 「ブロック内の42番目のスロットにデータを追加した」
blkref #0: rel 1663/16384/16388 blk 583 書き込んだ場所 テーブルrel=16388の583番目の8KBブロック

🧱 ブロックとオフセットのイメージ

PostgreSQLはテーブルを8KBずつの「ブロック」に分けて保存しています。
1ページ(ブロック)内に複数の行が入っており、off はその「スロット番号」です。

ブロック583
 ├─ off 42 : INSERTされた1行目
 ├─ off 43 : INSERTされた2行目
 ├─ off 44 : INSERTされた3行目
 └─ ...

今回のログでは、同じブロックに対して連続で INSERT が行われていることがわかります💡


🧾 なぜWALに書くの?

PostgreSQLは 「データを本体に書く前に、ログ(WAL)に書く」 ことで
クラッシュ時にも復旧できるようにしています。

つまり、このWALは

「test_cpテーブルにこういう行を追加したよ!」

という“変更のメモ”なんです。
実データは後で反映されます。


🔍 どのテーブルに対応しているか調べる

WALの中で出てきた rel 1663/16384/16388 のうち、
最後の 16388ファイルノード(relfilenode) です。

SELECT relname, relfilenode
FROM pg_class
WHERE relfilenode = 16388;

test_cp が出てくるはずです 👏


💬 まとめ

確認できたこと 内容
WALにINSERTログが残る ✅ データ変更が物理ログとして記録されている
rmgr: Heap の意味 ✅ テーブル(ヒープ)操作を示す
off / blk の意味 ✅ ページ内の位置(行スロット)とブロック番号
rel の意味 ✅ テーブルを識別する内部ID(ファイルノード)
役割 ✅ 障害復旧(クラッシュリカバリ)のための変更履歴

🧠 一言でまとめると

PostgreSQLのWALは、
「どのテーブルに、どの位置に、何をINSERTしたか」
をぜんぶ記録してくれる「データベースの黒箱」です。

さいごに

今回はWAL Writerについて学びました。簡単にいうと、ディスクに書き込む前にログを残し障害対策をしているものだそうです。それ以上は分かりません。。。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?