マイペースなRailsおじさん

Ruby、Ruby on Rails、オブジェクト指向設計を主なテーマとして扱います。だんだん大きくなっていくRuby on Rails製プロダクトのメンテナンス性を損なわない方法を考えたり考えなかったりしている人のブログです。

PostgreSQLのデータファイルの中身を確認する

Postgresのマニュアルに、データファイルのレイアウトが載っていた。 www.postgresql.jp

テーブルに行を挿入して、実際に書き込まれるデータを確認してみる。

手順

データファイルのパスを調べる

テーブルを作成して、テーブルのファイルパスを調べる。

CREATE TABLE datafiletest (name varchar(100), age integer);
SELECT pg_relation_filepath('datafiletest');
-- base/16388/24577
SHOW data_directory;
--  /var/lib/postgresql/15/main

postgresのデータディレクトリからのパスが返ってくるので、データファイルは/var/lib/postgresql/15/main/base/16388/24577にあることがわかる。

データファイルの更新を確認する

デーブルを作成した時点ではファイルは空。0バイト。

postgres@TANAKA-DESKTOP:/home/ytnk531$ ls -l /var/lib/postgresql/15/main/base/16388
/24577
-rw------- 1 postgres postgres 0  1月  5 18:17 /var/lib/postgresql/15/main/base/16388/24577

データを追加する。 この時点ではWAL(先行書き込みログ)が記録されただけで、データファイルへの反映はされないので、CHECKPOINTを使って強制的にデータファイルを更新する。

INSERT INTO datafiletest VALUES('test', 10);
CHECKPOINT;
postgres@TANAKA-DESKTOP:/home/ytnk531$ ls -l /var/lib/postgresql/15/main/base/16388
/24577
-rw------- 1 postgres postgres 8192  1月  5 18:30 /var/lib/postgresql/15/main/base/16388/24577

このデータファイルには、今のところ1ページ分だけ書き込まれている。 マニュアルを頼りに読んでいく。 70.6. データベースページのレイアウト

まずヘッダ部分を確認するために、32バイトだけ読む。

postgres@TANAKA-DESKTOP:/home/ytnk531$ hexdump -C -n 32 /var/lib/postgresql/15/main
/base/16388/24577
00000000  00 00 00 00 e8 85 9a 01  00 00 00 00 1c 00 d8 1f  |................|
00000010  00 20 04 20 00 00 00 00  d8 9f 48 00 00 00 00 00  |. . ......H.....|
00000020
PageHeaderData

00 00 00 00 e8 85 9a 01  00 00 00 00 1c 00 d8 1f 
00 20 04 20 00 00 00 00
  • pd_lsn 00 00 00 00 e8 85 9a 01
  • pd_checksum 00 00
  • pd_flags 00 00
  • pd_lower 1c 00
  • pd_upper d8 1f
  • pd_special 00 20
  • pd_pagesize_version 04 20
  • pd_prune_xid 00 00

pd_upperが、空き領域の終わりのアドレスを指す。リトルエンディアンなので、1fd8を指している。

ItemIdData

d8 9f 48 00

1アイテム分。

postgres/itemid.h at c8e1ba736b2b9e8c98d37a5b77c4ed31baf94147 · postgres/postgres · GitHub

  • オフセット 11011000 0011111 -> 1f d8
  • フラグ 10
  • 長さ 1001000 00000000 -> 8 bytes

一つ目のレコードまでのオフセットが1fd8なので、データは1fd9から始まることがわかる。

postgres@TANAKA-DESKTOP:/home/ytnk531$ hexdump -C -s 8152 /var/lib/postgresql/15/ma
in/base/16388/24577
00001fd8  eb 02 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00001fe8  01 00 02 00 02 09 18 00  0b 74 65 73 74 00 00 00  |.........test...|
00001ff8  0a 00 00 00 00 00 00 00                           |........|
00002000
HeapTupleHeaderData
eb 02 00 00 00 00 00 00  00 00 00 00 00 00 00 00
01 00 02 00 02 09 18 00
  • t_xmin eb 02 00 00
  • t_xmax 00 00 00 00
  • t_cid 00 00 00 00
  • t_xvac なし?(t_cidとt_xvacは共用体)
  • t_ctid 00 00 00 00 01 00
  • t_infomask2 02 00
  • t_infomask 02 09
  • t_hoff 18

hoffが18なので実際のデータは1ff1から始まる。

postgres@TANAKA-DESKTOP:/home/ytnk531$ hexdump -C -s 8177 /var/lib/postgresql/15/ma
in/base/16388/24577
00001ff1  74 65 73 74 00 00 00 0a  00 00 00 00 00 00 00     |test...........|
  • name 74 65 73 74 -> test
  • age 00 00 00 0a -> 100

なんやかんや読み取れた。 後ろの4バイトは何なのかわからない。

nameを長くすると、いい感じに後ろに詰まる。4バイトごとにalignされている。

postgres@TANAKA-DESKTOP:/home/ytnk531$ hexdump -C -s 8177 /var/lib/postgresql/15/main/base/16388/24588
00001ff1  74 65 73 74 65 64 62 65  00 00 00 0a 00 00 00     |testedbe.......|
00002000