1. 格納方式の概要
Greenplumでは「行方向/列方向」と「ヒープ/Append-Only(AO)」の2軸でテーブルを設計できる。用途に応じて最適な組み合わせを選ぶことで、更新頻度、読み取りパターン、圧縮率、バックアップ戦略などを最適化できる。
2. ヒープ vs AO
2.1 動作原理
- ヒープ:PostgreSQL と同じ MVCC ヒープ。UPDATE/DELETE が頻繁な OLTP 向き。WAL(REDO)により時点復旧が可能。
- AO:追記専用ファイルに新規ブロックを追加し、削除/更新は別ビットマップで管理。ブロックは再利用されないため単行 INSERT は非効率だが、バッチロードには最適。約20バイト/行の可視性情報が不要になる。
2.2 選択基準
| 観点 | ヒープ推奨 | AO推奨 |
|---|---|---|
| トランザクション粒度 | 小さな単位で頻繁に更新 | バッチ単位で大量挿入 |
| リカバリ方式 | 時点復旧(PITR)必須 | 増分バックアップで十分 |
| テーブルサイズ | 小〜中規模(例:マスタデータ) | 大規模(例:ファクトテーブル) |
2.3 DDL例
-- ヒープ
CREATE TABLE customer_dim
( id int PRIMARY KEY, name text )
DISTRIBUTED BY (id);
-- AO(無圧縮)
CREATE TABLE sales_fact
( id bigint, amount numeric )
WITH (appendoptimized=true)
DISTRIBUTED BY (id);
3. 行方向 vs 列方向
3.1 特性比較
| 項目 | 行方向 | 列方向 |
|---|---|---|
| 格納単位 | 1レコード=連続配置 | 1列=独立ファイル |
| 列読み出し | 先頭列から順にデコード | 対象列のみシーク |
| 圧縮可否 | 不可(行の型が混在) | 可(同型連続値) |
| ALTER TABLE | フルリライト | 該当列ファイルのみ追加 |
3.2 選択基準
- 行方向:SELECT リスト・WHERE で多列を扱う OLTP/明細検索
- 列方向:集計クエリが列を限定する OLAP/高圧縮が必須
- 混在:時間軸パーティションで「新規区間=行方向、過去区間=列方向」
3.3 DDL例
-- 列方向 AO
CREATE TABLE page_view
( ts timestamp, user_id bigint, url text )
WITH (appendoptimized=true, orientation=column)
DISTRIBUTED BY (user_id);
4. 圧縮戦略(AO のみ)
4.1 圧縮アルゴリズム
| 方向 | レベル | アルゴリズム |
|---|---|---|
| 行 | テーブル | ZLIB, ZSTD, QUICKLZ |
| 列 | テーブル/列 | RLE_TYPE, ZLIB, ZSTD, QUICKLZ |
4.2 圧縮率 vs 速度
- QUICKLZ:低圧縮・高速(CPU 負荷小)
- ZLIB(6):中圧縮・中速(バランス型)
- ZLIB(9)/ZSTD(19):高圧縮・低速(ストレージ重視)
- RLE_TYPE:連続同一値に強い(ソート済データ向き)
4.3 圧縮設定例
-- テーブル単位
CREATE TABLE log_large
( id bigint, msg text )
WITH (
appendoptimized=true,
compresstype=zstd,
compresslevel=3
);
-- 列単位
CREATE TABLE metrics
(
ts timestamp,
val float8 ENCODING (compresstype=RLE_TYPE, blocksize=65536),
tag text ENCODING (compresstype=zlib, compresslevel=6)
)
WITH (appendoptimized=true, orientation=column);
4関数で圧縮率を確認
SELECT get_ao_compression_ratio('log_large'); -- 例:3.19
5. パフォーマンス実験
400列×100万行のテストテーブルを作成し、行ヒープ/行AO/列AO で集計クエリを実行。
-- テーブル作成関数(略)
-- 実行時間比較(例:c2 列で GROUP BY)
行ヒープ 138 ms → 行AO 152 ms → 列AO 107 ms
末尾列でも列AO は 120 ms で最速。
列方向 AO は CPU 効率が高く、特に集計列が少ないクエリで優位性が顕著。
6. 格納方式の照会
-- AO テーブル一覧
SELECT nspname, relname
FROM pg_class c JOIN pg_namespace n ON n.oid = relnamespace
WHERE relstorage IN ('a','c');
-- ヒープ テーブル一覧
SELECT nspname, relname
FROM pg_class c JOIN pg_namespace n ON n.oid = relnamespace
WHERE relstorage = 'h' AND relkind = 'r';
7. ディストリビューション再編成
-- ハッシュキー変更(自動リバランス)
ALTER TABLE sales SET DISTRIBUTED BY (customer_id);
-- ランダム化(データは動かない)
ALTER TABLE sales SET DISTRIBUTED RANDOMLY;
-- 手動リバランス
ALTER TABLE sales SET WITH (REORGANIZE=true);
8. 格納方式の変更手順
CREATE → INSERT → DROP → RENAME の4ステップで移行。
CREATE TABLE sales_col
(LIKE sales)
WITH (appendoptimized=true, orientation=column, compresstype=zstd);
INSERT INTO sales_col SELECT * FROM sales;
DROP TABLE sales;
ALTER TABLE sales_col RENAME TO sales;
9. 列追加と圧縮継承
-- 圧縮列追加
ALTER TABLE sales
ADD COLUMN discount numeric DEFAULT 0
ENCODING (compresstype=zlib);
-- パーティション継承例
CREATE TABLE ccddl (i int, j int, k int, l int)
WITH (appendoptimized=true, orientation=column)
PARTITION BY RANGE (j)
SUBPARTITION BY LIST (k)
SUBPARTITION TEMPLATE (
SUBPARTITION sp1 VALUES (1,2,3),
COLUMN i ENCODING (compresstype=zlib),
COLUMN j ENCODING (compresstype=quicklz)
) ( PARTITION p1 START (1) END (10) );
ALTER TABLE ccddl ADD PARTITION p3 START (20) END (30);
-- p3 は sp1 の圧縮設定を自動継承