この記事について
「Spark って何?」「なんで普通のプログラムじゃダメなの?」という疑問に答える記事。技術的な詳細に入る前に、Spark が解決する問題と、使うと何がうれしいのかを整理する。
1. そもそもなぜ分散処理が必要なのか
1-1. 1台のマシンの限界
普通のプログラム(Python、Java など)は1台のマシンで動く。メモリに収まるデータなら問題ない。
データ量 1台のマシンで処理できるか
─────────────────────────────────────
1MB → 余裕。pandas で一瞬
100MB → 余裕。pandas で数秒
1GB → ギリギリ。pandas だとメモリが厳しくなる
10GB → 厳しい。メモリに載らない。処理に数十分〜数時間
100GB → 無理。メモリ不足でクラッシュ
1TB → 完全に無理
1PB → 論外
現実のデータは大きい。ECサイトのアクセスログは1日で数十GB、SNSの投稿データは1日で数TB。1台のマシンでは処理しきれない。
1-2. 解決策:複数のマシンで分担する
1台で処理(垂直スケール):
┌──────────────┐
│ 超高性能マシン │ ← メモリ1TB、CPU128コア... 高い。限界がある
│ 全データを処理 │
└──────────────┘
複数台で処理(水平スケール):
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│マシン1│ │マシン2│ │マシン3│ │マシン4│ ← 普通のマシンを並べる
│1/4 │ │1/4 │ │1/4 │ │1/4 │ ← データを分割して分担
└──────┘ └──────┘ └──────┘ └──────┘
→ 足りなければマシンを追加するだけ
これが「分散処理」の基本的な考え方。Spark はこの「複数マシンでデータを分担して処理する」を簡単に書けるようにするフレームワーク。
2. Spark がない世界
2-1. 自分で分散処理を書くと…
「100GBのCSVから売上を地域別に集計したい」という単純な処理でも、自分で分散処理を書くとこうなる:
自分でやること:
1. データを10台のマシンに分割して配る
2. 各マシンで集計プログラムを起動する
3. 1台が故障したら、そのマシンの分を別のマシンに再割り当て
4. 各マシンの部分結果を集めて最終集計する
5. ネットワークエラーのリトライ処理を書く
6. メモリが足りなくなったらディスクに退避する処理を書く
7. 全マシンの進捗を監視する仕組みを作る
...
本来やりたいのは「地域別に売上を集計する」だけなのに、分散処理の仕組みを全部自分で作る必要がある。
2-2. Hadoop MapReduce の時代
2006年、Google の論文をもとに Apache Hadoop が登場。分散処理の仕組みを提供してくれた。
でも MapReduce には問題があった:
// MapReduce で「地域別売上集計」を書くと...
// Map クラス、Reduce クラス、ドライバクラスを書いて
// 入出力の型を定義して、シリアライズを設定して...
// → 単純な集計なのに100行以上のJavaコード- Java でしか書けない(当時)
- 処理の中間結果を毎回ディスクに書き出す → 遅い
- Map と Reduce の2段階しかない → 複雑な処理は何段もチェーンする必要がある
3. Spark が解決すること
3-1. Spark で同じ処理を書くと
# PySpark で「地域別売上集計」
df = spark.read.csv("s3://data/sales.csv", header=True)
result = df.groupBy("region").agg(sum("amount"))
result.show()3行。やりたいことだけ書けばいい。裏側の分散処理は Spark が全部やってくれる。
3-2. Spark がやってくれること
| 自分で書く | Spark がやってくれる |
|---|---|
| データの分割・配布 | ✅ 自動でパーティション分割 |
| 各マシンでの並列実行 | ✅ Task として自動配分 |
| 障害時の再実行 | ✅ リネージから自動再計算 |
| 中間結果の管理 | ✅ メモリ内で保持(高速) |
| ネットワーク通信 | ✅ シャッフルを自動管理 |
| クエリの最適化 | ✅ Catalyst が自動最適化 |
| メモリ管理 | ✅ Tungsten が自動管理 |
3-3. Spark の「うれしさ」まとめ
うれしさ1: 速い
→ メモリ内処理。MapReduce の10〜100倍速い
うれしさ2: 簡単に書ける
→ Python / SQL で書ける。分散処理を意識しなくていい
うれしさ3: 汎用的
→ バッチ、ストリーミング、SQL、ML が1つのフレームワーク
うれしさ4: スケールする
→ データが増えたらマシンを追加するだけ
うれしさ5: 壊れても大丈夫
→ マシンが故障しても自動で再計算
4. Spark を使う場面・使わない場面
4-1. Spark が向いている場面
| 場面 | 例 |
|---|---|
| 大量データの変換・集計 | 数十GB〜PB のログ分析、ETL |
| データレイクの処理 | S3 上の Parquet ファイルの加工 |
| 複雑なデータパイプライン | 複数テーブルの結合、多段階の変換 |
| 機械学習の前処理 | 特徴量エンジニアリング |
| ストリーミング処理 | リアルタイムログ集計 |
4-2. Spark が向いていない場面
| 場面 | 代わりに使うもの |
|---|---|
| 数MB のデータ | pandas で十分 |
| 単純な SQL 集計 | DWH(Redshift, BigQuery)の方が簡単 |
| 低レイテンシ(ミリ秒) | Flink、Kafka Streams |
| トランザクション処理 | RDB(MySQL, PostgreSQL) |
| リアルタイム API | 通常の Web アプリ |
目安:データが1台のマシンのメモリに収まるなら、Spark は不要。pandas や SQL で十分。
5. Spark の動き方をざっくり理解する
5-1. たとえ話:大量の手紙を仕分ける
1000万通の手紙を都道府県別に仕分けたいとする。
1人でやる(pandas):
1人が1000万通を1通ずつ確認して仕分け
→ 何日もかかる
Spark でやる:
1. 手紙を100人に均等に配る(パーティション分割)
2. 各人が自分の分を都道府県別に仕分ける(Map)
3. 「東京」の山を1人に集める、「大阪」の山を別の1人に集める(シャッフル)
4. 各人が自分の都道府県の手紙を数える(Reduce)
→ 100人で分担するので速い
5-2. コードで見る
# 1. データを読み込む(Spark が自動的にパーティション分割)
df = spark.read.parquet("s3://data/letters/")
# → 100個のパーティションに分割される(ファイル数やサイズに応じて)
# 2. 都道府県でグループ化して数える
result = df.groupBy("prefecture").count()
# → Spark が自動的に:
# a. 各パーティション内で部分集計(Map側)
# b. 同じ都道府県のデータを同じ Executor に集める(シャッフル)
# c. 最終集計(Reduce側)
# 3. 結果を表示
result.show()
# → この瞬間に初めて実行される(Lazy Evaluation)5-3. 何が起きているか
ユーザーが書くコード:
df.groupBy("prefecture").count()
Spark が裏でやっていること:
┌─────────────────────────────────────────────┐
│ Executor 1 │
│ Partition 1 → 部分集計 → {東京:500, 大阪:300}│
│ Partition 2 → 部分集計 → {東京:400, 福岡:200}│
└──────────────────┬──────────────────────────┘
│ シャッフル(東京のデータを集める)
┌──────────────────┴──────────────────────────┐
│ Executor 2 │
│ 東京: 500 + 400 + ... = 最終集計 │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Executor 3 │
│ 大阪: 300 + ... = 最終集計 │
└─────────────────────────────────────────────┘
ユーザーは「何をしたいか」だけ書く。「どう分散するか」は Spark が決める。
6. Spark のエコシステム
Spark は1つのフレームワークで複数のワークロードに対応する。
| コンポーネント | 用途 | 例 |
|---|---|---|
| Spark SQL / DataFrame | 構造化データの処理 | テーブルの結合、集計、ETL |
| Structured Streaming | ストリーミング処理 | リアルタイムログ集計 |
| MLlib | 機械学習 | 分類、回帰、クラスタリング |
| GraphX | グラフ処理 | ソーシャルネットワーク分析 |
従来: ワークロードごとに別のツール
バッチ → MapReduce
SQL → Hive
ストリーミング → Storm
ML → Mahout
→ ツール間のデータ受け渡しが面倒
Spark: 1つのフレームワークで全部
バッチ → Spark SQL
SQL → Spark SQL
ストリーミング → Structured Streaming
ML → MLlib
→ 同じデータ、同じ API、同じクラスタ
7. Spark を動かす場所
| 環境 | 説明 | 向いている人 |
|---|---|---|
ローカル(local[*]) |
自分の PC で動かす | 学習・開発 |
| Databricks Community Edition | 無料のクラウド環境 | 学習(おすすめ) |
| Databricks | 商用プラットフォーム | 本番運用 |
| AWS EMR | AWS マネージド Spark | AWS ユーザー |
| Google Dataproc | GCP マネージド Spark | GCP ユーザー |
| 自前クラスタ | YARN / Kubernetes 上に構築 | インフラを自分で管理したい |
学習を始めるなら Databricks Community Edition(無料)が最も手軽。ブラウザだけで Spark を試せる。
8. pandas との比較
pandas ユーザーが Spark に移行するときの対応表。
| 操作 | pandas | PySpark |
|---|---|---|
| 読み込み | pd.read_csv("file.csv") |
spark.read.csv("file.csv") |
| カラム選択 | df[["a", "b"]] |
df.select("a", "b") |
| フィルタ | df[df.amount > 100] |
df.filter(df.amount > 100) |
| 集約 | df.groupby("region").sum() |
df.groupBy("region").agg(sum("amount")) |
| ソート | df.sort_values("amount") |
df.orderBy("amount") |
| 行数 | len(df) |
df.count() |
| 表示 | df.head() |
df.show() |
見た目は似ている。大きな違い:
| pandas | PySpark | |
|---|---|---|
| 実行場所 | 1台のマシン | 複数マシン(分散) |
| データサイズ | メモリに収まる範囲 | 数TB〜PB |
| 実行タイミング | 即座に実行 | Action まで遅延(Lazy) |
| 変更 | インプレース可能 | 常に新しい DataFrame を返す(不変) |
9. まとめ
| 疑問 | 答え |
|---|---|
| Spark って何? | 大量データを複数マシンで並列処理するフレームワーク |
| なぜ必要? | 1台のマシンでは処理しきれないデータがあるから |
| 何がうれしい? | 速い、簡単に書ける、スケールする、壊れても大丈夫 |
| いつ使う? | データが数GB以上で、バッチ/ストリーミング/MLの処理が必要なとき |
| いつ使わない? | データが小さい、単純なSQL、ミリ秒レイテンシが必要なとき |
Spark は「大量データの処理を、普通のコードを書くのと同じくらい簡単にする」ためのツール。
参考文献
- Jules S. Damji et al. “Learning Spark.” 2nd Edition, O’Reilly Media, 2020. Chapter 1.
- Apache Spark. “Spark Overview.” https://spark.apache.org/docs/latest/
- Matei Zaharia et al. “Apache Spark: A Unified Engine for Big Data Processing.” Communications of the ACM, 2016.