PostgreSQLテーブル名から物理データファイルを特定する方法

検証環境の構築

デフォルトテーブルスペースのサンプル

-- ユーザーとデータベースの作成
psql -U postgres -c "create user sales with password 'sales';"
psql -U postgres -c "create database salesdb owner sales;"
psql -U sales -d salesdb -c "create schema authorization sales;"

-- 接続してテーブル作成
psql -U sales -d salesdb

-- 従業員テーブルの定義
create table staff_records
    ( staff_no       bigint      primary key
    , given_name     varchar(20)
    , family_name    varchar(25) not null
    , mail_addr      varchar(25) not null
    , tel_no         varchar(20)
    , entry_date     date        not null
    , position_code  bigint      not null
    , annual_pay     numeric(10,2)
    , bonus_rate     numeric(4,2)
    , supervisor_no  bigint
    , division_no    bigint
    , constraint pay_positive check (annual_pay > 0)
    );

-- サンプルデータ投入
insert into staff_records
select generate_series(1,50000) as id,
       substr(md5(random()::text),1,6),
       substr(md5(random()::text),1,10),
       substr(md5(random()::text),1,8)||'@example.com',
       '0'||ceiling(random()*8999999999+1000000000)::bigint::text,
       date '2000-01-01' + (random()*8000)::int,
       (random()*40+10)::int,
       (random()*8000000+2000000)::numeric(10,2),
       (random()*0.5)::numeric(4,2),
       (random()*200+1)::int,
       (random()*15+1)::int;

カスタムテーブルスペースのサンプル

[postgres@dbserver ~]$ psql
psql (14.9)

postgres=# show data_directory;
   data_directory   
--------------------
 /var/lib/pgsql/data
(1 row)

-- テーブルスペース用ディレクトリ作成
postgres=# \! mkdir -p /var/lib/pgsql/custom_ts

-- ユーザー・テーブルスペース・データベース作成
postgres=# create user appuser with password 'appuser';
postgres=# create tablespace custom_ts owner appuser location '/var/lib/pgsql/custom_ts';
postgres=# create database appdb owner appuser tablespace custom_ts;

-- スキーマ作成
[postgres@dbserver ~]$ psql -U appuser -d appdb -c "create schema authorization appuser;"

-- テーブル作成(デフォルトと明示的指定の比較)
psql -U appuser -d appdb

create table transaction_log (seq_id bigint, payload text, recorded_at timestamp);
insert into transaction_log select generate_series(1,200000),md5(random()::text),clock_timestamp();

create table audit_trail (seq_id bigint, payload text, recorded_at timestamp) tablespace custom_ts;
insert into audit_trail select generate_series(1,200000),md5(random()::text),clock_timestamp();

transaction_logはデフォルトテーブルスペースを、audit_trailは明示的にcustom_tsを使用します。

テーブルスペース情報の取得

pg_classのreltablespaceフィールドは0を返す場合があるため、実際のテーブルスペースを特定するにはpg_database.dattablespaceを参照する必要があります。

-- reltablespaceが0の場合の確認
testdb=# select reltablespace, current_database(), 
         relnamespace::regnamespace, relname, oid, relfilenode 
from pg_class where relname ~ 'transaction_log|audit_trail';

 reltablespace | current_database | relnamespace |    relname      |  oid  | relfilenode 
---------------+------------------+--------------+-----------------+-------+-------------
             0 | appdb            | appuser      | transaction_log | 16432 |       16432
             0 | appdb            | appuser      | audit_trail     | 16438 |       16438

物理ファイルパスを特定するSQL

with 
db_metadata as (
    select oid as database_oid, 
           datname as database_name, 
           dattablespace as default_ts_oid 
    from pg_database 
    where datname = current_database()
),
ts_metadata as (
    select ts.oid as ts_oid, 
           ts.spcname as ts_name,
           case 
               when ts.oid = 1663 then 'pg_global'
               when ts.oid = 1664 then 'pg_default'
               else ts.spcname 
           end as display_name
    from pg_tablespace ts
    join db_metadata db on ts.oid = db.default_ts_oid
       or exists (
           select 1 from pg_class c 
           where c.reltablespace = ts.oid 
           and c.relnamespace = '対象スキーマ'::regnamespace
       )
),
data_dir as (
    select setting as pg_data_path 
    from pg_settings 
    where name = 'data_directory'
)
select 
    format('TS:%s (OID:%s)', ts.display_name, ts.ts_oid) as tablespace_detail,
    format('DB:%s (OID:%s)', db.database_name, db.database_oid) as database_detail,
    format('Schema:%s (OID:%s)', 
           c.relnamespace::regnamespace::text, 
           c.relnamespace) as schema_detail,
    c.oid as object_id,
    c.relfilenode as file_node,
    format('%s/%s', 
           d.pg_data_path, 
           pg_relation_filepath(c.oid)) as physical_location,
    c.relname as table_name,
    pg_size_pretty(pg_relation_size(c.oid)) as current_size
from pg_class c
cross join db_metadata db
cross join data_dir d
left join ts_metadata ts on true
where c.relnamespace::regnamespace::text = '対象スキーマ名'
  and c.relname = '対象テーブル名'
  and c.relkind = 'r';  -- 通常テーブルのみ

実行結果の例

デフォルトテーブルスペースの場合

-- sales.staff_records の確認

tablespace_detail  | TS:pg_default (OID:1664)
database_detail    | DB:salesdb (OID:16401)
schema_detail      | Schema:sales (OID:16402)
object_id          | 16415
file_node          | 16415
physical_location  | /var/lib/pgsql/data/base/16401/16415
table_name         | staff_records
current_size       | 4528 kB

salesdb=# \! ls -lh /var/lib/pgsql/data/base/16401/16415
-rw------- 1 postgres postgres 4.5M Jul 15 14:32 /var/lib/pgsql/data/base/16401/16415

カスタムテーブルスペースの場合

-- appuser.audit_trail の確認

tablespace_detail  | TS:custom_ts (OID:16388)
database_detail    | DB:appdb (OID:16389)
schema_detail      | Schema:appuser (OID:16390)
object_id          | 16438
file_node          | 16438
physical_location  | /var/lib/pgsql/data/pg_tblspc/16388/PG_14_202107181/16389/16438
table_name         | audit_trail
current_size       | 14 MB

appdb=# \! ls -lh /var/lib/pgsql/data/pg_tblspc/16388/PG_14_202107181/16389/16438
-rw------- 1 postgres postgres 14M Jul 15 14:35 /var/lib/pgsql/data/pg_tblspc/16388/PG_14_202107181/16389/16438

重要な注意事項

テーブルのoidとrelfilenodeは必ずしも一致しません。oidはテーブル作成後変更されませんが、relfilenodeは以下の操作で変化します:

  • TRUNCATE
  • REINDEX
  • CLUSTER
  • VACUUM FULL

実際のディスク上のファイル名はrelfilenodeに追従します。

-- TRUNCATE前後の変化を確認
appdb=# select oid, relfilenode, relname from pg_class where relname = 'audit_trail';
  oid   | relfilenode |  relname    
--------+-------------+-------------
  16438 |       16438 | audit_trail

appdb=# truncate appuser.audit_trail;
TRUNCATE TABLE

appdb=# select oid, relfilenode, relname from pg_class where relname = 'audit_trail';
  oid   | relfilenode |  relname    
--------+-------------+-------------
  16438 |       16455 | audit_trail  -- relfilenodeが変更

-- 新しいファイルパスを確認
physical_location  | /var/lib/pgsql/data/pg_tblspc/16388/PG_14_202107181/16389/16455

ファイルが1GBを超えると、PostgreSQLは自動的にセグメントファイルを分割します(16455.1, 16455.2など)。この場合、pg_relation_filepathはベースファイルのみ返し、追加セグメントは別途確認が必要です。

タグ: PostgreSQL テーブルスペース pg_class pg_database 物理ストレージ

9月21日 23:37 投稿