2012年12月6日

pg_filedumpでテーブル/インデックスファイルを覗いてみる

PostgreSQL Advent Calendar 2012(全部俺)のDay 6です。

PostgreSQLは、テーブルやインデックスなどは、それぞれ個別のファイルになっています。通常、これらのファイルを直接見ることはなく、あくまでもPostgreSQLのサーバに対してクエリを投げて、PostgreSQLのバックエンドプロセスがテーブルやインデックスなどのファイルを読み書きすることになります。


但し、何らかの異常(または単なる好奇心)が発生した場合、テーブルやインデックスのファイルそのものを直接見てみたくなることがあります。

そのような場合のために作成されているツールが "pg_filedump" と呼ばれるユーティリティです。

これは、PostgreSQLコアチームのTom Lane氏がひとりプロジェクトとして開発しているもので、インデックスやテーブルファイルを直接読み取って、その中身を人間に理解可能な形で表示してくれるツールです。pg_filedumpはpgFounderyで公開されています。

PgFoundry: pg_filedump: Project Info
http://pgfoundry.org/projects/pgfiledump/

今回は、このpg_filedumpユーティリティの使い方を紹介してみます。

■pg_filedumpのコンパイル


pg_filedumpをコンパイルするには、バージョン9.1まではPostegreSQLのソースコードが必要です(バージョン9.2以降は不要です)。具体的には src/backend/utils/hash/pg_crc.c というファイルの持つチェックサムテーブルを使うためです。

PostgreSQLをソースコードからコンパイルしていて、configureされた状態のソースツリーが手元にある場合には、tar.gzを解凍して、top_builddirを指定してMakefile.contribファイルを使ってmakeを実行すればコンパイルできます。
[snaga@devsv02 pgsql]$ tar zxf pg_filedump-9.1.0.tar.gz
[snaga@devsv02 pgsql]$ cd pg_filedump-9.1.0
[snaga@devsv02 pg_filedump-9.1.0]$ make -f Makefile.contrib top_builddir=/home/snaga/pgsql/postgresql-9.1.6
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv -g -I. -I. -I/home/snaga/pgsql/postgresql-9.1.6/src/include -D_GNU_SOURCE   -c -o pg_filedump.o pg_filedump.c
(...snip...)
gcc -O2 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv -g pg_filedump.o pg_crc.o   -L/home/snaga/pgsql/postgresql-9.1.6/src/port  -Wl,-rpath,'/usr/local/pgsql91/lib',--enable-new-dtags  -lpgport -lz -lreadline -ltermcap -lcrypt -ldl -lm  -o pg_filedump
[snaga@devsv02 pg_filedump-9.1.0]$ ls
Makefile          pg_crc.c@  pg_filedump*   pg_filedump.h  README.pg_filedump
Makefile.contrib  pg_crc.o   pg_filedump.c  pg_filedump.o
[snaga@devsv02 pg_filedump-9.1.0]$
但し、最近はRPMからインストールしていてソースコードが手元にない、というケースも多いと思います。その場合は、PostgreSQLのソースコードから src/backend/utils/hash/pg_crc.c を取り出してpg_filedumpのディレクトリに置き、Makefile.contribの以下の三行をコメントアウトした上で、前述したようにコンパイルします。
#EXTRA_CLEAN = pg_crc.c

#pg_crc.c: $(top_srcdir)/src/backend/utils/hash/pg_crc.c
#       rm -f $@ && $(LN_S) $< .
なお、8.3.21、8.4.14、9.0.10、9.1.6の各バージョンの pg_crc.c ファイルは以下にまとめておきましたので、必要に応じて利用してください。

pg_crc.tar.gz

■ダンプする対象ファイルを特定する


pg_filedumpコマンドの準備ができたら、ダンプする対象を探します。

今回は、特定のデータベース内にあるテーブルのインデックスファイルをダンプすることを考えてみます。

まず、oid2nameコマンドでデータベース名一覧を取得します。
[snaga@devsv02 pg_filedump-9.1.0]$ oid2name
All databases:
    Oid        Database Name  Tablespace
----------------------------------------
  78684              datacmn  pg_default
  52284             foodmart  pg_default
  52258              pgbench  pg_default
  12699             postgres  pg_default
  52283                snaga  pg_default
  52513             sugarcrm  pg_default
  12691            template0  pg_default
      1            template1  pg_default
[snaga@devsv02 pg_filedump-9.1.0]$
今回は "pgbench" データベースを対象にすることにします。

次に、pgbenchデータベース内のテーブルとインデックスの一覧を取得します。特定のデータベース内のテーブル一覧を取得するには "-d" オプションでデータベース名を指定し、インデックスも含めて表示するには "-i" オプションを指定します。
[snaga@devsv02 pg_filedump-9.1.0]$ oid2name -d pgbench -i
From database "pgbench":
  Filenode             Table Name
---------------------------------
     52271       pgbench_accounts
     52276  pgbench_accounts_pkey
     52259       pgbench_branches
     52272  pgbench_branches_pkey
     52282        pgbench_history
     52262        pgbench_tellers
     52274   pgbench_tellers_pkey
[snaga@devsv02 pg_filedump-9.1.0]$
今回はこのうち、"pgbench_accounts_pkey" というpgbench_accountsテーブルのプライマリキーインデックスを対象にしてみます。

ここまでで、データベースpgbenchのOidは「52258」であり、pgbench_accounts_pkeyインデックスのFilenodeが「52276」であることが分かりました。

PostgreSQLでは、データベースクラスタ内のbaseディレクトリに「Oid」でディレクトリとして作成され、テーブルやインデックスのファイルはさらにその中に「Filenode」でファイルとして作成されていますので、この情報を使って実ファイルを探します。
bash-3.2$ ls -l /var/lib/pgsql/9.1/data/base/52258/52276
-rw------- 1 postgres postgres 224641024 Sep 13 00:22 /var/lib/pgsql/9.1/data/base/52258/52276
bash-3.2$
というわけで、今回ダンプしたいpgbenchデータベース内のpgbench_accontsテーブルのプライマリキーインデックスpgbench_accounts_pkeyの実ファイルを特定できました。

■インデックスファイルをダンプする


それではこのファイルをダンプしてみましょう。おもむろにダンプしてみます。
bash-3.2$ pg_filedump /var/lib/pgsql/9.1/data/base/52258/52276

*******************************************************************
* PostgreSQL File/Block Formatted Dump Utility - Version 9.1.0
*
* File: /var/lib/pgsql/9.1/data/base/52258/52276
* Options used: None
*
* Dump created on: Fri Nov 23 15:48:13 2012
*******************************************************************

Block    0 ********************************************************
----- Block Offset: 0x00000000 Offsets: Lower 48 (0x0030) Block: Size 8192 Version 4 Upper 8176 (0x1ff0) LSN: logid 143 recoff 0x8e4b0fb0 Special 8176 (0x1ff0) Items: 6 Free Space: 8128 TLI: 0x0001 Prune XID: 0x00000000 Flags: 0x0000 () Length (including item array): 48 BTree Meta Data: Magic (0x00053162) Version (2) Root: Block (290) Level (2) FastRoot: Block (290) Level (2) ----- BTree Index Section: Flags: 0x0008 (META) Blocks: Previous (0) Next (0) Level (0) CycleId (0) Block 1 ********************************************************
----- Block Offset: 0x00002000 Offsets: Lower 1516 (0x05ec) Block: Size 8192 Version 4 Upper 2208 (0x08a0) LSN: logid 144 recoff 0x3f8f4508 Special 8176 (0x1ff0) Items: 373 Free Space: 692 TLI: 0x0001 Prune XID: 0x00000000 Flags: 0x0000 () Length (including item array): 1516 ------ Item 1 -- Length: 16 Offset: 2304 (0x0900) Flags: NORMAL Item 2 -- Length: 16 Offset: 8160 (0x1fe0) Flags: NORMAL Item 3 -- Length: 16 Offset: 8144 (0x1fd0) Flags: NORMAL Item 4 -- Length: 16 Offset: 8128 (0x1fc0) Flags: NORMAL (...snip...) Item 147 -- Length: 16 Offset: 5856 (0x16e0) Flags: NORMAL Item 148 -- Length: 16 Offset: 5840 (0x16d0) Flags: NORMAL Item 149 -- Length: 16 Offset: 5824 (0x16c0) Flags: NORMAL Item 150 -- Length: 16 Offset: 5808 (0x16b0) Flags: NORMAL ----- BTree Index Section: Flags: 0x0001 (LEAF) Blocks: Previous (27420) Next (0) Level (0) CycleId (0) *** End of File Encountered. Last Block Read: 27421 *** bash-3.2$
インデックスファイルのサイズにもよりますが、大量のデータが表示されます。

インデックスの各ブロックのヘッダに含まれる情報、インデックスエントリの格納情報(オフセット、サイズ)などを、上記の例ではブロック0からブロック27421までについてダンプしています。

■pg_filedumpのオプション


pg_filedumpにはさまざまなオプションがあります。
bash-3.2$ pg_filedump

Version 9.1.0 (for PostgreSQL 9.1.x)
Copyright (c) 2002-2010 Red Hat, Inc.
Copyright (c) 2011, PostgreSQL Global Development Group

Usage: pg_filedump [-abcdfhixy] [-R startblock [endblock]] [-S blocksize] file

Display formatted contents of a PostgreSQL heap/index/control file
Defaults are: relative addressing, range of the entire file, block
               size as listed on block 0 in the file

The following options are valid for heap and index files:
  -a  Display absolute addresses when formatting (Block header
      information is always block relative)
  -b  Display binary block images within a range (Option will turn
      off all formatting options)
  -d  Display formatted block content dump (Option will turn off
      all other formatting options)
  -f  Display formatted block content dump along with interpretation
  -h  Display this information
  -i  Display interpreted item details
  -R  Display specific block ranges within the file (Blocks are
      indexed from 0)
        [startblock]: block to start at
        [endblock]: block to end at
      A startblock without an endblock will format the single block
  -S  Force block size to [blocksize]
  -x  Force interpreted formatting of block items as index items
  -y  Force interpreted formatting of block items as heap items

The following options are valid for control files:
  -c  Interpret the file listed as a control file
  -f  Display formatted content dump along with interpretation
  -S  Force block size to [blocksize]

Report bugs to 
bash-3.2$
例えば、-Rオプションを指定することによって一部のブロックだけをダンプの対象とすることができますし、-iオプションを指定すると各アイテム(インデックスエントリやタプル)の中身まで含めてダンプすることができます。
bash-3.2$ pg_filedump -R 100 100 -i /var/lib/pgsql/9.1/data/base/52258/52276

(...snip...)

 ------
 Item   1 -- Length:   16  Offset: 2304 (0x0900)  Flags: NORMAL
  Block Id: 594  linp Index: 1  Size: 16
  Has Nulls: 0  Has Varwidths: 0

 Item   2 -- Length:   16  Offset: 8160 (0x1fe0)  Flags: NORMAL
  Block Id: 588  linp Index: 1  Size: 16
  Has Nulls: 0  Has Varwidths: 0

 Item   3 -- Length:   16  Offset: 8144 (0x1fd0)  Flags: NORMAL
  Block Id: 588  linp Index: 2  Size: 16
  Has Nulls: 0  Has Varwidths: 0

(...snip...)
上記の例では、100番目のブロックについて、インデックスエントリの内容も含めてダンプしています。

例えば、ブロック内の最初のインデックスエントリ(Item 1)は、テーブルファイルのブロック594(Block Id: 594)の最初のタプル(linp index: 1)を指し示している、ということが分かるわけです。

上記はインデックスファイルの例でしたが、テーブルファイル(Filenode 52271はpgbench_accountsテーブル)に対して同じオプションを指定して実行すると、以下のような出力になります。
bash-3.2$ ./pg_filedump -R 100 100 -i /var/lib/pgsql/9.1/data/base/52258/52271

(...snip...)

 ------
 Item   1 -- Length:  121  Offset: 8064 (0x1f80)  Flags: NORMAL
  XMIN: 14517797  XMAX: 0  CID|XVAC: 1
  Block Id: 100  linp Index: 1   Attributes: 4   Size: 24
  infomask: 0x0902 (HASVARWIDTH|XMIN_COMMITTED|XMAX_INVALID)

 Item   2 -- Length:  121  Offset: 7936 (0x1f00)  Flags: NORMAL
  XMIN: 14517797  XMAX: 0  CID|XVAC: 1
  Block Id: 100  linp Index: 2   Attributes: 4   Size: 24
  infomask: 0x0902 (HASVARWIDTH|XMIN_COMMITTED|XMAX_INVALID)

(...snip...)
これは、各タプル(レコード)のヘッダ情報をダンプしたものです。

このようにpg_filedumpを使うことによって、インデックスファイルやテーブルファイルの物理的な内容を直接的に確認することができます。

■まとめ


今日は「pg_filedump」というPostgreSQLのインデックスファイルやテーブルファイルを、直接ダンプするユーティリティの使い方を紹介しました。

普通に使っている時にはあまり気にしないと思いますが、インデックスやテーブルファイルの詳細を人間が読める形式で表示できるため、トラブルシューティングや動作を解析したい場合などに重宝することがあるツールです。

ぜひ、頭の片隅に置いておいて、機会があれば使ってみていただければと思います。

ではでは。

0 件のコメント:

コメントを投稿