TL

MPPデータウェアハウス

テラバイト級の集計が数十秒で返る理由を、内部から理解したい人へ。シェアードナッシングによる大規模並列処理、分散キー・ソートキー設計、データ再分散(シャッフル)、クエリの並列実行プランの原理を解きほぐします。

応用MPPデータウェアハウスシェアードナッシング分散キー並列処理分析基盤最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.MPPデータウェアハウスはシェアードナッシング構成で、データを多数のノードへ物理分割して各ノードが自分の持ち分だけを並列にスキャン・集計する。全体のスループットはノード数にほぼ比例してスケールし、1台のDBでは不可能なテラバイト級の集計を成立させる。
  • 2.性能を決める最大の設計変数は分散キー(distribution key)。結合する2表を同じキーで分散すればJOINはノード内で完結(コロケーテッド結合)し、ネットワークを介したデータ再分散(シャッフル)が消える。分散キー選定を誤ると全ノード間で大量のデータが飛び交い、遅さの主因になる。
  • 3.ソートキーはノード内でデータを物理的に並べ、ゾーンマップ(各ブロックのmin/max)で不要ブロックを読み飛ばすプルーニングを効かせる。分散=ノード間の配置、ソート=ノード内の並び順で、両者は独立に効く別々の最適化。

なぜ1台のDBでは分析が詰まるのか

数十億行のイベントログを「日別・地域別に集計せよ」というクエリを考えます。OLTP向けの単一DBは、少数行をインデックスで素早く引くのは得意でも、この種のクエリでは全行に近いスキャンと巨大な集約が必要で、1台のCPU・メモリ・ディスク帯域が律速になります。インデックスを足しても、読むべき行が全体の大半なら効きません。分析ワークロードのボトルネックは「特定行を探す」ことではなく「大量の行を舐めて畳み込む」帯域そのものにあります。

ここでの発想の転換は、速い1台を追うのをやめ、多数の凡庸なマシンにデータを分割して同時に働かせることです。100台で1/100ずつのデータを並列にスキャンできれば、原理上は100倍のスキャン帯域が出ます。これがMPP(Massively Parallel Processing:大規模並列処理)データウェアハウスの核であり、古典的にはAmazon Redshift、Greenplum、Teradata、Verticaなどのシェアードナッシング型がこのモデルを体現してきました(Google BigQueryやSnowflakeも同じ「大量の行を分割して並列に畳む」発想を受け継ぎますが、ストレージと計算を分離した構成をとる点は後述します)。B+木やLSMといった単一ノードのストレージ構造(/database/)の話ではなく、データと計算をどう水平に割るかが主題になります。

スケールアップではなくスケールアウト

単一DBの高速化は基本「スケールアップ(1台を強く)」で、CPUやメモリの物理上限とコストの非線形な跳ね上がりに突き当たります。MPPは「スケールアウト(台数を増やす)」で、データと計算を分割して台数に比例した帯域を得ます。分析基盤が扱うデータ量は年々増える一方なので、性能を台数で買える線形スケーラビリティが本質的な価値になります。

シェアードナッシング:分割統治の物理的な土台

MPPが台数比例のスケールを得られるのは、シェアードナッシング(shared-nothing) というアーキテクチャを採るからです。各ノードは自分専用のCPU・メモリ・ローカルディスクを持ち、他ノードと何も共有しません。あるノードが担当するデータには、そのノードだけがアクセスします。

対比すると輪郭がはっきりします。

方式共有するものスケールの壁
シェアードエブリシング全ノードが同一メモリ・ディスクを共有共有資源への競合が全体の律速になり台数を増やせない
シェアードディスク計算は分離、ストレージは共有共有ストレージのI/O帯域とロック調整が壁になる
シェアードナッシング何も共有しない(各自ローカル)ノード間通信量が壁。データ分割の巧拙が性能を決める

シェアードナッシングの利点は、競合する共有資源が原理的に存在しないことです。各ノードはローカルディスクをローカルCPUで読むだけなので、100台あれば100本のI/O経路とCPUが独立に動く。ボトルネックは「共有資源の奪い合い」から「ノード間でどれだけデータを動かすか」へ移ります。つまり設計の勝負どころは、各ノードが自分のローカルデータだけで仕事を完結できるように、データをどう配置するかに集約されます。

クラウドDWは『ストレージ分離+シェアードナッシング実行』

Redshift(旧来のノードローカル構成)やGreenplumが厳密なシェアードナッシングなのに対し、SnowflakeやBigQueryは永続データをオブジェクトストレージ(S3やColossus)に置き、計算ノードはそこから読み込むストレージと計算を分離した構成をとります。この意味では純粋なシェアードナッシングではありません。ただしクエリ実行の瞬間は、各計算ノードが担当データを読んでノード間でシャッフルする実行モデル自体はシェアードナッシング的であり、本記事の分散キー・シャッフル・部分集約の原理はそのまま当てはまります。分離構成の利点は、ストレージと計算を別々にスケールでき、計算クラスタを止めてもデータが残る点にあります。

データ分散:行をどのノードへ置くか

表の各行を物理的にどのノードへ置くかを決めるのが分散(distribution) で、その基準列が分散キー(distribution key) です。代表的な分散方式は3つあります。

ハッシュ分散: node = hash(distkey) mod ノード数
             同じキー値の行は必ず同じノードへ集まる(決定的)

ラウンドロビン分散: 行を順番に各ノードへ均等配置
             データは平準化されるが、値による局所性は無い

全体複製(レプリケート): 小さい表を全ノードにまるごと複製
             どのノードでもローカルに参照できる(次元表向き)

ハッシュ分散が分析基盤の主役です。キー値のハッシュをノード数で割った余りで宛先が決まるため、同一キー値の行は必ず同一ノードに集まります。この「同じ値が同じ場所に来る」性質が、後述するJOINと集約の効率を根底から支えます。一方で分散キーの選び方を誤ると、特定ノードに行が偏るデータスキュー(偏り) が起き、その1台が最後まで走り続けて全体を遅らせます。理想はカーディナリティが高く(値の種類が多く)、値の出現が均等な列で、order_id のような列が向き、値が数種類しかない status 列などは最悪です。

分散スキューは『最も遅い1台』が全体を決める

MPPのクエリは全ノードの完了を待って結果を返すため、実行時間は最も遅いノードに支配されます。分散キーが偏っていて1台に行が集中すると、他の99台が終わっても、その1台が終わるまで全体は完了しません(ストラグラー問題)。台数を増やしても偏った1台の負荷は減らないため、スキューはスケールアウトで解決できないのが厄介な点です。データ分布を見て、均等に割れるキーを選ぶことが最優先になります。

再分散(シャッフル):JOINが速くも遅くもなる分岐点

分析クエリの中心はしばしば表と表の結合(JOIN) です。ここでシェアードナッシングの制約が牙をむきます。結合は「両表で結合キーが一致する行どうし」を突き合わせますが、一致すべき行が別々のノードに散っていると、そのままでは突き合わせられません。同じノードに集めてからでないと結合できないのです。

行き着く先は2つに分かれます。

  • コロケーテッド結合(co-located join):結合する両表が同じ列で同じようにハッシュ分散されていれば、キーが一致する行は初めから同じノードに同居しています。各ノードが自分のローカルデータだけで結合を完了でき、ノード間のデータ移動はゼロです。
  • 再分散結合(シャッフル):分散キーが結合キーと違う場合、DBは実行時に片方または両方の表を結合キーで再ハッシュしてネットワーク越しに配り直します。これがデータ再分散(redistribution/シャッフル) で、ネットワーク帯域を大量に消費する最大の遅延源です。
コロケーテッド結合(両表とも customer_id で分散):
  Node1: orders(cust=1..) ⋈ customers(cust=1..)  ← ローカルで完結、通信なし
  Node2: orders(cust=N..) ⋈ customers(cust=N..)  ← ローカルで完結、通信なし

再分散結合(orders は order_id 分散 → customer_id で配り直す):
  全ノード → shuffle(customer_id) → 全ノード  ← ネットワークを大量に流れる

もう一つの回避策がブロードキャスト結合(broadcast join) です。片方の表が十分小さければ、それを全ノードへ丸ごと複製して各ノードのローカル大表と結合します。小さな次元表を全ノードに配るコストのほうが、巨大な事実表をシャッフルするより安いときに選ばれます。「大表どうしはコロケーション、大表×小表はブロードキャスト、それ以外はシャッフル」 という判断が、MPPの物理設計とクエリプランの要になります。だからこそ、よく結合する事実表と次元表は同じ分散キーで揃えるのが定石です。

ソートキーとゾーンマップ:ノード内で読む量を削る

分散が「ノードの配置」を決めるのに対し、ソートキー(sort key) は「ノードでのデータの並び順」を決めます。この2つは独立した別々の最適化で、混同すると設計を誤ります。分散は並列度とJOINの局所性を、ソートは各ノードが読むブロック量を制御します。

MPPの多くは列指向で、各列を連続したブロック(Redshiftなら1MBブロック等)に格納します。ソートキーでデータを物理的に並べておくと、各ブロックが持つ値の範囲が狭く固まります。ここで効くのがゾーンマップ(zone map/min-maxインデックス) で、これは各ブロックに格納された値の最小値と最大値だけを記録した軽量メタデータです。

sort key = order_date でソート済み、各ブロックの min/max を記録:
  block A: min=2026-01-01  max=2026-01-09
  block B: min=2026-01-10  max=2026-01-18
  block C: min=2026-01-19  max=2026-01-27

WHERE order_date = '2026-01-15' のとき:
  block A → 範囲外なので読まずにスキップ
  block B → 範囲内なので読む
  block C → 範囲外なので読まずにスキップ

クエリの絞り込み条件がソートキーに沿っていれば、DBはゾーンマップを見て該当し得ないブロックを一切読まずに飛ばせます。これをプルーニング(枝刈り) と呼び、行を1件ずつ調べるB木インデックスとは全く別の、「読むブロックを丸ごと減らす」粗い粒度の高速化です。事実表を時刻列でソートしておけば、期間で絞る分析クエリの大半のブロックが読み飛ばされ、スキャン量が桁で減ります。列指向ゆえに必要な列のブロックしか読まない効果と合わせ、I/O量を二重に削るのがMPPのスキャン戦略です。

分散キーとソートキーは役割が違う

分散キーは「どのノードに置くか(並列度とJOINのコロケーション)」、ソートキーは「ノード内でどう並べるか(ブロックのプルーニング)」を決めます。しばしば分散キーは頻繁に結合する列(例:customer_id)に、ソートキーは頻繁に範囲で絞る列(例:order_date)に別々に設定するのが効果的です。両方を同じ列にする必要はなく、それぞれ効かせたい最適化に合わせて独立に選ぶのが原則です。

クエリの並列実行:プランはノードをまたいで組まれる

最後に、1本のSQLがどうやって多数のノードで並列に走るのかを見ます。MPPは通常、クエリを受け付けて全体を統括するリーダーノード(coordinator) と、実データを持って計算する多数のコンピュートノード(worker) に役割が分かれます。

1) リーダーがSQLを解析し、分散を意識した実行プランを作る
2) プランをステージに分割し、各ステージのタスクを全コンピュートノードへ配る
3) 各ノードが自分のローカルデータで、スキャン→フィルタ→部分集約を並列実行
4) 必要ならステージ境界で再分散(シャッフル)してデータを配り直す
5) 各ノードの部分結果をマージして最終結果をリーダーが返す

要はプランがノード間の物理配置を前提に組まれる点が単一DBと決定的に違います。集約 SUM(amount) を例にすると、各ノードがまず自分の持ち分だけを部分集約して小さな中間結果にし、それらだけをネットワークで集めて最終合算します。全生データを1か所へ運んでから畳むのではなく、畳んでから運ぶことで通信量を最小化する——この「部分集約→マージ」の二段構えが並列集約の定石です。プランは複数のステージに分かれ、ステージの切れ目(シャッフルが要る箇所)が並列実行の同期点になります。

設計レビュー・試験の頻出論点

押さえるべき勘所は次の通り。(1) シェアードナッシングでは何も共有しない代わりにノード間のデータ移動量が律速になる。(2) 分散キーが結合キーと一致すればコロケーテッド結合でシャッフルが消え、不一致だと再分散(シャッフル)が発生して遅くなる。小表はブロードキャストで回避できる。(3) 分散キーは高カーディナリティかつ均等な列を選び、偏るとスキューで最も遅い1台が全体を支配する。(4) 分散(ノード間配置)とソート(ノード内の並び)は別物。ソートキー+ゾーンマップのmin/maxでブロックをプルーニングする。(5) 集約は各ノードで部分集約してからマージし、通信量を抑える。「MPPは速い」ではなく「シャッフルを避ける物理設計で初めて速い」という条件付きが落とし穴。

まとめ

  • MPPデータウェアハウスはシェアードナッシングでデータを多数ノードに水平分割し、各ノードが自分の持ち分を並列にスキャン・集計する。スループットは台数にほぼ比例してスケールし、単一DBでは詰まるテラバイト級の集計を成立させる。
  • 何も共有しない代わりに、ノード間のデータ移動量が新たな律速になる。物理設計の目的は「各ノードがローカルデータだけで仕事を完結できる配置」を作ること。
  • 最重要変数は分散キー。結合する表を同じキーで分散すればコロケーテッド結合でシャッフルが消え、不一致なら再分散(シャッフル)が起きて遅くなる。小表はブロードキャストで回避する。
  • 分散キーは高カーディナリティかつ均等な列を選ぶ。偏るとスキューで最も遅い1台が全体を支配し、スケールアウトでは解消できない。
  • 分散(ノード間の配置)とソート(ノード内の並び)は独立。ソートキーとゾーンマップのmin/maxで該当しないブロックをプルーニングし、列指向と合わせてスキャン量を桁で削る。
  • クエリはリーダーがプランを分割して全ノードへ配り、各ノードが部分集約してからマージする。並列実行はシャッフルを避ける設計があって初めて速い。

データ工学 Article

MPPデータウェアハウスを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

MPP

比較で見る軸

難易度: advanced / カテゴリ: データ工学 / タグ数: 6

導入後に効く点

性能を決める最大の設計変数は分散キー(distribution key)。結合する2表を同じキーで分散すればJOINはノード内で完結(コロケーテッド結合)し、ネットワークを介したデータ再分散(シャッフル)が消える。分散キー選定を誤ると全ノード間で大量のデータが飛び交い、遅さの主因になる。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
データ工学
タグ数
6

判断チェックリスト

  • 自社の用途が「MPP / データウェアハウス」に近いか確認する。
  • 強みである「MPPデータウェアハウスはシェアードナッシング構成で、データを多数のノードへ物理分割して各ノードが自分の持ち分だけを並列にスキャン・集計する。全体のスループットはノード数にほぼ比例してスケールし、1台のDBでは不可能なテラバイト級の集計を成立させる。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

MPPデータウェアハウスシェアードナッシング分散キー並列処理MPPデータウェアハウスシェアードナッシング