パーティショニングとバケッティング
分析基盤のクエリが遅いのは、多くの場合スキャン量とシャッフル量が過大だからです。パーティションで読む前に捨て、バケットでシャッフルを消し、スキューと小ファイル問題を避ける設計の勘所を原理から押さえられます。
- 1.テーブルパーティショニングは値でデータをディレクトリに分け、クエリの述語に合うパーティションだけを読むプルーニングを可能にする。効くのはパーティション列に述語が掛かるときだけで、粒度が細かすぎると小ファイルが激増し逆効果になる。
- 2.バケッティングはキーのハッシュを固定数のバケットへ写し、同じキーが必ず同じバケット番号に入るよう物理配置する。両表を同じキー・同じバケット数で切っておけば、結合や集約でネットワークシャッフルを省ける。
- 3.分散処理の性能を殺す二大要因はデータスキュー(一部キーへの偏り)とファイルサイズ(小さすぎ・大きすぎ)。パーティション設計はカーディナリティ、述語の形、1パーティションあたりのバイト数の三点で決める。
分析基盤では「速さ」はスキャン量とシャッフル量で決まる
ペタバイト級のテーブルに対する分析クエリの実行時間は、CPUの速さよりもどれだけのデータを読み、どれだけネットワーク越しに動かしたかでほぼ決まります。ここでの主役は二つの物理配置の技法です。パーティショニングは「読むデータ量(スキャン)」を減らし、バケッティングは「ノード間で動かすデータ量(シャッフル)」を減らします。両者はしばしば混同されますが、狙う相手が違います。
この記事はRDBの内部パーティション(子表・ローカル索引)や分散ログのパーティションではなく、データレイク/データウェアハウス上での分散データ処理(Spark・Hive・Trino・各種クエリエンジン)の観点で扱います。データは多くの場合オブジェクトストレージ(S3・GCS・ADLS)上のファイル群として置かれ、テーブルは「どのファイルがどのパーティションに属するか」というメタデータで定義されます。
RDBのパーティショニングは1論理表を境界メタデータで物理子表に分ける仕組みで、各子表は独立した格納・索引単位でした。分析基盤のパーティショニングは、値ごとにストレージ上のディレクトリ(プレフィックス)へファイルを振り分け、テーブルのメタデータ(Hiveメタストアやテーブルフォーマットのマニフェスト)が所属を管理します。索引ではなくファイルの並べ方そのもので読む範囲を絞るのが分析基盤流です。RDB内部の詳細は/database/を参照。
パーティショニング:読む前に捨てる
パーティショニングの本質は、テーブルを小さくすることではなく、クエリが読まなくてよいデータをファイルを開く前に確定できる構造を作ることです。あるパーティション列(典型的には日付)でデータを分け、値ごとに別ディレクトリへファイルを配置します。
s3://lake/events/
dt=2026-06-19/ part-00000.parquet part-00001.parquet ...
dt=2026-06-20/ part-00000.parquet ...
dt=2026-06-21/ part-00000.parquet ...
WHERE dt = '2026-06-21' というクエリが来ると、エンジンはディレクトリ名(Hive形式では 列名=値)から各パーティションのキー値をメタデータだけで知り、条件に合致しないディレクトリを走査計画から丸ごと外します。これが**パーティションプルーニング(枝刈り)**です。読むファイルが減るので、ネットワークI/Oもデコードも減ります。ペタバイトのテーブルでも「1日分」だけ読めば済むのはこの仕組みです。
重要なのは、プルーニングが効くのはパーティション列に直接述語が掛かるときだけという点です。関数を挟んで WHERE date(event_time) = '2026-06-21' と書くと、多くのエンジンは event_time(非パーティション列)への述語と見なしてプルーニングできず、全パーティションを読みます。パーティション列そのもの(dt)に等値・範囲条件を書くのが鉄則です。
「粒度を細かくするほど読む量が減る」と考えて dt/hour/country/user_id のように多段・高カーディナリティで切ると、パーティション数が組合せ的に爆発し、各パーティションに数KBのファイルが大量に散らばります。これが後述の小ファイル問題を直撃します。プルーニングの利得と、パーティション数増加によるメタデータ肥大・小ファイル化はトレードオフです。
バケッティング:シャッフルを事前に済ませておく
パーティショニングが「読む量」を減らすのに対し、バケッティングが減らすのはシャッフルです。分散結合や集約では、同じキーの行を同じノードに集める必要があり、そのためにネットワーク越しの再分配(シャッフル)が走ります。これが分散処理で最も重いフェーズです。
バケッティングは、キーを hash(key) mod N で N個のバケットに写し、同じキーが必ず同じバケット番号のファイルに入るよう、書き込み時点で物理配置します。パーティションが「値で分ける(無限に増えうる)」のに対し、バケットは「固定数のスロットにハッシュで散らす」点が決定的に違います。高カーディナリティのキー(ユーザーID・注文IDなど)はパーティションではなくバケットに向きます。
| 観点 | パーティショニング | バケッティング |
|---|---|---|
| 主に減らすもの | スキャン量(読むファイル数) | シャッフル量(ノード間転送) |
| 所属の決め方 | 列の値でディレクトリを分ける | hash(キー) mod N でバケットを決める |
| 数の増え方 | 値の種類だけ増える(可変・無限大も) | 設計時に固定(N個) |
| 向くキー | 低カーディナリティ・述語が掛かる列(日付等) | 高カーディナリティ・結合/集約キー(ID等) |
| 主な副作用 | 細かすぎると小ファイル爆発 | N不一致だと利得が消える/後から変えにくい |
効果が出るのは、結合する両表を同じキー・同じバケット数で切っておくときです。両表がバケット番号 b について「キー k は必ずバケット b にある」を満たすなら、結合はバケット b 同士だけを突き合わせれば足り、テーブル全体を再分配するシャッフルが要りません(Sparkはソートマージ結合からシャッフル(Exchange)を省き、Hiveではバケットマップ結合やソートマージバケット結合として実装されます)。集約も、グループキーでバケット化しておけば同じキーが1バケットに固まっているため再分配を省けます。
バケット数 N は、ハッシュが均等に散る前提で1バケットが扱いやすいサイズ(後述の目安)になるよう決めます。両表で N が一致しないとバケットの対応が崩れ、結局シャッフルが復活します。N を後から変えるには全書き換えが要るため、バケット数は長期の設計判断として扱い、安易に大きくも小さくもしないのが勘所です。
データスキュー:平均は速くても最遅タスクで律速する
分散処理は「全タスクが終わって初めて次段へ進む」ため、実行時間は平均ではなく最も遅いタスクで決まります。ここを壊すのが**データスキュー(偏り)**です。ハッシュ分配は「キーが均等にばらける」ことを前提にしますが、現実のキーには偏りがあります。
- ホットキー:特定の値に行が集中する(人気商品、NULL、既定値、巨大テナント)。そのキーを担当するタスクだけ入力が桁違いに大きくなる。
- 結合の爆発:スキューしたキーで結合すると、片側の多数行×もう片側の多数行で出力が急増する。
# 均等分配(理想) # スキュー(現実)
task0: ████ task0: █
task1: ████ task1: █
task2: ████ task2: ████████████████ ← ホットキー担当
task3: ████ task3: █
↑ 全タスクほぼ同時終了 ↑ task2 が全体を律速
対策の原理は偏った負荷を分割することです。代表的なのがソルティング(salting)で、ホットキーに人工的な接尾辞を足して key#0…key#R の複数キーに割り、複数タスクへ散らします(結合相手側は各ソルト値に対応する行を複製して整合を取る)。近年のエンジンはこれを自動化する**適応的クエリ実行(AQE)**を持ち、実行時に大きすぎるパーティションを検知して分割します。いずれも「均等分配の前提が崩れたら、偏りを見つけて割り直す」という同じ発想です。
結合キーやパーティションキーに NULL や 0/空文字などの既定値が大量に混じると、それらが1つのハッシュ値へ集中して極端なスキューを生みます。しかも NULL は等値結合ではマッチしないため、重いだけで結果に寄与しないタスクになりがちです。取り込み時点で NULL を除外・分離するか、キー設計から見直すのが根本対策です。
ファイルサイズ問題:小さすぎても大きすぎても損
分散処理の性能は1ファイルのサイズに驚くほど左右されます。両極端がそれぞれ別の理由で遅くなります。
小ファイル問題:数KB〜数MBのファイルが大量にあると、ファイルごとにオープン・メタデータ読み・スケジューリングの固定コストが掛かり、実データの読み出しよりオーバーヘッドが支配的になります。オブジェクトストレージは1リクエストのレイテンシが大きいため、ファイル数がそのままリクエスト数として効きます。ストリーミング取り込みや細かすぎるパーティションで発生しやすく、**コンパクション(小ファイルの統合)**で定期的に束ねるのが定石です。
大ファイル問題:逆に1ファイルが数GBと巨大だと、それを分割して並列に読めない場合に並列度が上がらず、1タスクが長時間走って律速します。Parquetのように内部がrow group/ページに分かれ、範囲を分割して読める(splittable)形式なら緩和されますが、圧縮形式やフォーマット次第では分割不能になります。
| 症状 | 主因 | 効く対策 |
|---|---|---|
| タスク数だけ多く実データが少ない | 小ファイルが大量(取り込み・過剰パーティション) | コンパクションで統合、パーティション粒度を粗く |
| 少数タスクが長時間走る | 巨大ファイルで分割読みできない | splittableな形式(Parquet等)で適正サイズに分割 |
| 特定タスクだけ極端に遅い | データスキュー(ホットキー) | ソルティング/AQEで偏りを分割 |
実務の目安として、1ファイル(および1タスクが読む単位)を概ね数百MB程度にそろえると、オープンコストの償却と並列度の両立が取りやすくなります。厳密な最適値はエンジンとストレージに依存しますが、「小さすぎず大きすぎず、均一に」が原則です。
パーティション設計の勘所:三つの軸で決める
良いパーティション設計は、次の三点を突き合わせて決めます。原理はすべて「プルーニングの利得」対「パーティション数・小ファイルのコスト」のバランスです。
- カーディナリティ:パーティション列は低〜中カーディナリティが原則。日付は自然な好例。
user_idのような高カーディナリティ列はパーティションにせず、必要ならバケットにする。 - 述語の形(クエリパターン):実際のクエリが
WHEREで絞る列をパーティションキーにする。誰も日付で絞らないのに日付で切っても、プルーニングは働かずコストだけ残る。アクセスパターンがパーティション設計を規定する。 - 1パーティションあたりのバイト数:各パーティションが小さくなりすぎないサイズ(前節の目安に沿う)に落ち着く粒度を選ぶ。データ量が増えたら日次→時次のように細かく、少なければ月次に粗く調整する。
多くの分析テーブルは、日付(や年月)で1段パーティションし、結合・集約に使う高カーディナリティキーをバケットにする構成が素直な出発点です。パーティションでスキャンを減らし、バケットでシャッフルを減らす——役割を分けて考えると設計がぶれません。列の順序や物理配置による絞り込みの原理は、クエリ最適化(/database/)や分散処理基盤(/devops/)の考え方とも地続きです。
まとめ
- 分析基盤の速さはスキャン量とシャッフル量でほぼ決まる。パーティショニングがスキャンを、バケッティングがシャッフルを減らす。狙う相手が違う。
- パーティショニングは値でディレクトリを分け、述語がパーティション列に掛かるときだけプルーニングが効く。関数を挟むと効かない。細かすぎると小ファイルを生む。
- バケッティングは
hash(キー) mod Nで固定数のバケットへ写し、両表を同じキー・同じ N で切るとシャッフルなしで結合・集約できる。Nは後から変えにくい長期の設計判断。 - 分散処理は最遅タスクで律速するため、データスキュー(ホットキー・NULL・既定値)が致命的。ソルティングやAQEで偏りを分割する。
- ファイルサイズは小さすぎるとオープンコスト過多、大きすぎると並列度不足。コンパクションと適正サイズ化(概ね数百MB・均一)が要。
- パーティション設計はカーディナリティ・述語の形・1パーティションのバイト数の三軸で決める。出発点は「日付1段+高カーディナリティはバケット」。
データ工学 Article
パーティショニングとバケッティングを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
パーティショニング
比較で見る軸
難易度: advanced / カテゴリ: データ工学 / タグ数: 6
導入後に効く点
バケッティングはキーのハッシュを固定数のバケットへ写し、同じキーが必ず同じバケット番号に入るよう物理配置する。両表を同じキー・同じバケット数で切っておけば、結合や集約でネットワークシャッフルを省ける。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- データ工学
- タグ数
- 6
判断チェックリスト
- 自社の用途が「パーティショニング / バケッティング」に近いか確認する。
- 強みである「テーブルパーティショニングは値でデータをディレクトリに分け、クエリの述語に合うパーティションだけを読むプルーニングを可能にする。効くのはパーティション列に述語が掛かるときだけで、粒度が細かすぎると小ファイルが激増し逆効果になる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。