Apache Spark とは何か:なぜ必要で、何がうれしいのか

この記事について

「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.

コメントする