列指向フォーマット(Parquet・ORC)
分析基盤で「なぜ Parquet に置くと速く安くなるのか」が腑に落ちます。オブジェクトストレージ上のオープンな列指向ファイルが、スキャン量課金を桁で減らし、複数エンジンで同じデータを共有できる原理を分析基盤の視点で押さえられます。
- 1.Parquet・ORC は特定 DB に閉じないオープンな列指向ファイルで、S3 などのオブジェクトストレージに置く。ストレージと計算を分離でき、Spark・Trino・DuckDB など複数エンジンが同じファイルをそのまま読める点が、分析基盤における最大の価値。
- 2.列ごとの射影と、フッタの min/max 統計・パーティションによるスキップで、分散クエリが実際に読むバイト数を大幅に削る。読んだバイトやスキャン量で課金される分析サービスでは、これがそのままコストと所要時間の削減になる。
- 3.自己記述的なフッタに全メタデータを持つため、中央カタログなしでもスキーマオンリードと列プロジェクションが成り立つ。行グループが分散エンジンの並列スキャン単位になり、ファイルサイズ設計と小ファイル問題が性能を左右する。
分析基盤で「ファイル形式」が主役になる理由
トランザクション DB では、データの物理表現は DB エンジンの内部実装であり外から意識しません。ビッグデータの分析基盤では逆転します。データは S3・GCS のようなオブジェクトストレージにファイルとして置かれ、それを Spark・Trino・Presto・Hive・DuckDB・Flink といった複数のエンジンが読む。ここでは「どのファイル形式で書くか」が性能・コスト・相互運用性を決める第一級の設計判断になります。
Parquet と ORC は、この用途のためのオープンな列指向ファイル形式です。特定 DB 製品に閉じていないため、Spark で書いた Parquet を別ジョブが Trino で、BI ツールが DuckDB で、いずれも変換なしに読めます。これがストレージと計算の分離を成立させます。データは安価なオブジェクトストレージに一元的に置き、計算エンジンは必要時に起動して同じファイルを読む。「データを入れたエンジンでしか読めない」という DB 的なロックインが消えるのが、分析基盤における列指向フォーマットの核心的な価値です。
分析クエリは「広い表の少数カラムを全行にわたって集計する」形が支配的です。同一カラムの値を連続配置する列指向なら、集計に使うカラムのバイトだけを読めばよく、同型値が並ぶため圧縮も強く効きます。物理レイアウト(行指向 NSM と列指向 DSM、その折衷の PAX)そのものの原理はデータベース側で扱うので、本稿はそれを分散・オブジェクトストレージ環境でどう活かすかに軸足を置きます。
オブジェクトストレージ経済:読まないバイトは課金されない
分析基盤の列指向が効く本質は、圧縮率よりむしろ読まずに済ませる(pruning)ことにあります。そしてオブジェクトストレージ上では、これが単なる高速化ではなく直接のコスト削減になります。Athena や BigQuery のオンデマンド課金は「クエリがスキャンしたバイト量」に比例するため、読むバイトを減らせば料金がそのまま下がるからです。
列指向ファイルは三段の粒度でスキャンを削ります。
| 段階 | 何を使うか | 何を読まずに済むか |
|---|---|---|
| パーティション枝刈り | ディレクトリ名(Hive パーティション dt=2024-01-01 等) | 対象外の日付のファイル群を丸ごと開かない |
| 行グループ枝刈り | フッタの列統計(min/max・null 数) | 述語と交差しない行グループを読まない |
| 列プロジェクション | フッタのカラムチャンク位置 | 集計に使わないカラムのバイトを読まない |
たとえば WHERE dt='2024-01-10' AND amount > 1000 なら、まず dt=2024-01-10 以外のディレクトリを開かず、残ったファイル内でも amount の最大値が 1000 以下の行グループを飛ばし、さらに amount 列のバイトだけを範囲 GET で取りに行きます。オブジェクトストレージはバイト範囲指定の GETに対応するので、「フッタを1回読んで必要位置を知り、その範囲だけ取得する」動きが可能です。読むバイトが減れば、転送時間・GET 課金・スキャン量課金がまとめて減ります。
枝刈りは述語がパーティションキーや統計と素直に突き合う形のときだけ効きます。WHERE date_trunc('month', ts) = ... のようにキー列を関数で包むと、多くのエンジンは min/max と照合できずスキップを諦めます。また高カーディナリティ列(例 user_id)でパーティションを切ると、ディレクトリが数百万個に膨れて逆に遅くなります。パーティションは日付など低カーディナリティかつ述語で頻繁に絞る列に、単純比較で当たるよう設計するのが原則です。
自己記述フッタ:中央カタログなしで読める仕組み
Parquet も ORC も、スキーマと全メタデータ(各行グループ・各カラムの統計とバイトオフセット)をファイル末尾のフッタにまとめて置きます。本体を書き終えるまで統計は確定しないため、ストリーミング書き込みと相性が良く、末尾に置くのが理にかないます。
この自己記述性が分析基盤で効くのは、**スキーマオンリード(schema-on-read)**を成立させるからです。読み手はファイル自身のフッタからスキーマと型を得られるので、DB のような中央のスキーマ定義がなくてもよい。列を追加するような後方互換な進化なら、古い読み手は新しい列を無視し、新しい読み手は欠けた列を null として扱えます。中央カタログでの DDL 強制なしに、ファイル単位でスキーマが進化していける点が、レイクのゆるやかな運用に合います。
Parquet / ORC ファイルの外形(読み手の動き)
[ 行グループ0 | 行グループ1 | ... | フッタ | フッタ長(固定) ]
▲
1) 末尾の固定長からフッタ位置を逆算し、フッタだけを1回読む
2) 必要な行グループ/カラムのオフセットと統計を得る
3) 述語で不要な行グループを外し、必要カラムの範囲だけ GET する
分散エンジンはクエリを多数のワーカーに分けて実行します。列指向ファイルの行グループ(ORC ではストライプ)は、この分割(split)の自然な単位です。1ファイルに複数の行グループがあれば、それぞれを別ワーカーが同時にスキャンでき、フッタのオフセットのおかげで各ワーカーは自分の担当範囲へ直接シークできます。行グループが枝刈り・並列化・射影の共通単位になっている、と押さえると設計の勘所が見えます。
ファイルサイズ設計と小ファイル問題
分析基盤特有の落とし穴が**小ファイル問題(small files problem)**です。行グループは既定で 128MB 前後を目安に作られますが、ストリーミング取り込みや過剰なパーティション分割で、数 KB〜数 MB の Parquet が大量に生成されることがあります。
なぜ問題か。第一に、ファイルごとにフッタ読み取りと最低1回の GET が要るため、ファイル数に比例してメタデータ処理とリクエスト回数が膨らみ、小ファイルが多いとクエリ時間の大半がファイルを開く往復で消えます(GET はリクエスト単位でも課金・レイテンシがある)。第二に、行グループが小さいと統計・辞書・フッタの相対オーバーヘッドが増え、符号化の効きも落ちます。逆に大きすぎると枝刈りの粒度が粗くなり、条件に1行合うだけで巨大な行グループ全体を読む無駄が出ます。
「取り込みは速いのにクエリが遅い」ときは、まずファイル数を疑います。対策は、取り込み後に小ファイルをまとめて書き直す**コンパクション(compaction)**を定期実行し、行グループを 128MB 前後の狙ったサイズに保つこと。パーティションは日付粒度など粗めに取り、1パーティションあたりのファイルが数個〜数十個に収まるよう設計します。オブジェクトストレージ上では「ファイル数を制御すること」が、圧縮率の追求より効く性能施策になりがちです。
エンコーディングと圧縮:転送量を減らして計算へ渡す
列指向ファイルの最内層は、軽量符号化+汎用圧縮の二段構えです。下段は辞書符号化(低カーディナリティ値を整数 ID へ)・RLE(連続同値をカウントへ)・デルタやビットパッキング(整数の値域を詰める)といった、型のそろった整数列を生む符号化。上段はその整数列に Snappy・Zstandard・gzip のような汎用圧縮を掛けて最終的に容量を詰めます(各符号化アルゴリズムの詳細はデータベース側で扱います)。
分析基盤の観点で重要なのは、この二段構えがネットワークと計算の両方を軽くすることです。オブジェクトストレージからワーカーへ流すバイトが減るので転送が速く、かつ辞書 ID の等値比較のように復号せず圧縮表現のまま述語評価できる符号化は、読み込み後のスキャンを SIMD ベースのベクトル化実行にそのまま乗せられます。圧縮方式の選択は、CPU に余裕があり容量を詰めたいなら Zstandard、計算律速で展開を速くしたいなら Snappy、というトレードオフで選びます。
Parquet と ORC の使い分け
両者は思想がほぼ同じ(ファイル → 行グループ/ストライプ → カラムチャンク → ページ/ストリームの入れ子、フッタ統計、二段圧縮)ですが、出自とエコシステムの重心が違います。
| 観点 | Parquet | ORC |
|---|---|---|
| 出自 | Twitter+Cloudera(Dremel の系譜) | Hive コミュニティ(Hortonworks 主導) |
| 親和が強い基盤 | Spark・Arrow・DuckDB など汎用分析 | Hive・Impala など Hadoop/Hive 中心 |
| ネスト表現 | definition/repetition レベルで平坦化 | 型ツリー+ストリーム分解 |
| 更新まわり | テーブルフォーマット層に委ねる | ACID 風の base+delta を古くから内蔵 |
| 実務での既定 | クラウド分析・レイクハウスの事実上の標準 | 既存 Hive 資産や ORC 前提の基盤 |
現状のクラウド分析やレイクハウスでは Parquet が事実上の標準で、Arrow との連携や幅広いエンジン対応から新規はまず Parquet を選ぶのが無難です。ORC は既存の Hive/Hadoop 資産や、ORC を前提に最適化された基盤で引き続き使われます。どちらを選んでも、上に Iceberg・Delta・Hudi といったテーブルフォーマットを重ねれば、不変ファイル+差し替え可能なメタデータで ACID やタイムトラベルを足せます。ファイル形式(読む単位)とテーブル形式(トランザクションの単位)は層が違う、と切り分けると全体像がつながります。
分析基盤の観点では、まずオープンな列指向ファイル=ストレージと計算の分離+複数エンジンの相互運用を即答できるように。速く安くなる理由は(1) パーティション枝刈り、(2) フッタの min/max による行グループスキップ、(3) 列プロジェクションで読むバイトを削り、スキャン量課金を下げるの三段で説明します。行グループが分散スキャンの割り当て単位であること、小ファイル問題とコンパクション、ファイル形式とテーブルフォーマットの層の違いまで触れると上級者の解答になります。
まとめ
- Parquet・ORC は特定 DB に閉じないオープンな列指向ファイルで、オブジェクトストレージに置くことでストレージと計算を分離し、Spark・Trino・DuckDB など複数エンジンが同じファイルを変換なしに読める。これが分析基盤における最大の価値。
- スキャン削減はパーティション枝刈り→行グループの min/max スキップ→列プロジェクションの三段で効き、読んだバイトで課金される環境ではそのままコストと所要時間の削減になる。
- 自己記述的なフッタが全メタデータを持つため、中央カタログなしでスキーマオンリードと列プロジェクションが成り立ち、後方互換なスキーマ進化を許す。
- 行グループ(ストライプ)が分散スキャンの割り当て単位。ゆえにファイルサイズ設計と小ファイル問題が性能を左右し、コンパクションで 128MB 前後に保つのが定石。
- 最内層は軽量符号化+汎用圧縮の二段構えで転送量を削り、圧縮のまま述語評価してベクトル化実行へ渡す。新規は Parquet が事実上の標準、ORC は Hive 資産で残る。ファイル形式の上にテーブルフォーマットを重ねれば ACID・タイムトラベルを足せる。
データ工学 Article
列指向フォーマット(Parquet・ORC)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
列指向
比較で見る軸
難易度: advanced / カテゴリ: データ工学 / タグ数: 6
導入後に効く点
列ごとの射影と、フッタの min/max 統計・パーティションによるスキップで、分散クエリが実際に読むバイト数を大幅に削る。読んだバイトやスキャン量で課金される分析サービスでは、これがそのままコストと所要時間の削減になる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データ工学
- タグ数
- 6
判断チェックリスト
- 自社の用途が「列指向 / Parquet」に近いか確認する。
- 強みである「Parquet・ORC は特定 DB に閉じないオープンな列指向ファイルで、S3 などのオブジェクトストレージに置く。ストレージと計算を分離でき、Spark・Trino・DuckDB など複数エンジンが同じファイルをそのまま読める点が、分析基盤における最大の価値。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。