第1章 Databricks Intelligence Platform / 想定学習時間:30〜40分 / 最終確認:2026年8月

1-2. Delta Lakeの基礎(ACIDトランザクション・タイムトラベル・スキーマ強制)

🎯 この節の学習目標

1. Delta Lake の実体:Parquet+トランザクションログ

Delta Lake は、クラウドオブジェクトストレージ上のデータレイクに DWH 並みの信頼性を与えるオープンなストレージフォーマットです。その実体は驚くほどシンプルで、次の2つの要素でできています。

要素実体役割
データファイルParquet 形式のファイル群テーブルデータの実体。列指向で圧縮効率・読み取り効率が高い
トランザクションログ_delta_log ディレクトリ内の JSON ファイル(+チェックポイント)「どのファイルがテーブルの現在の状態を構成するか」を記録する唯一の真実源。すべての変更が順序付きのコミットとして追記される

テーブルへの書き込みは「新しい Parquet ファイルを追加し、その事実を _delta_log にコミットとして記録する」という形で行われます。読み取り側は必ずログを起点に「現在有効なファイル一覧」を解決するため、書き込み途中の中途半端な状態が読み手に見えることはありません。この仕組みがオープンな標準として公開されているため、Databricks 以外のエンジンからも同じテーブルを読み書きできます。

2. ACID トランザクション:同時読み書きの安全性

トランザクションログがあることで、Delta Lake は ACID 特性を保証します。

特性意味Delta Lake での実現
A:原子性(Atomicity)変更は「全部成功」か「全部なかったこと」のどちらかログへのコミットが成立して初めて変更が可視化される。途中失敗はコミットされず、読み手には見えない
C:一貫性(Consistency)テーブルは常に整合した状態を保つスキーマ強制(後述)や制約により不正なデータの混入を防ぐ
I:分離性(Isolation)同時実行される操作が互いを壊さない楽観的同時実行制御。読み手はスナップショットを読み、書き手同士の衝突はコミット時に検出される
D:永続性(Durability)コミットされた変更は失われないログとデータはクラウドオブジェクトストレージに永続化される

実務的に重要なのは、「ETL ジョブがテーブルへ書き込んでいる最中でも、BI ユーザーは直前のコミット時点の一貫したスナップショットを安全に読める」という点です。素の Parquet ファイル群を直接置き換える運用では、この保証がありませんでした。

3. タイムトラベル:過去のバージョンを読む・戻す

すべての変更がログにバージョン付きで残るため、Delta Lake では過去の任意時点のテーブルを読み出す(タイムトラベル)ことができます。

-- バージョン番号で過去の状態を読む
SELECT * FROM main.sales.orders VERSION AS OF 12;

-- タイムスタンプで過去の状態を読む
SELECT * FROM main.sales.orders TIMESTAMP AS OF '2026-08-01T00:00:00';

-- 変更履歴(いつ・誰が・どんな操作をしたか)を確認する
DESCRIBE HISTORY main.sales.orders;

-- 誤った更新をなかったことにして、テーブルを過去バージョンに戻す
RESTORE TABLE main.sales.orders TO VERSION AS OF 12;

📝 試験のポイント

公式サンプル問題に対応する典型パターンとして、「信頼できるロールバック・監査証跡・単一の真実源(single source of truth)が必要」という要件が示されたら、正解は Delta Lake(タイムトラベル・DESCRIBE HISTORY・トランザクションログ)+ Unity Catalog(アクセス制御・監査・リネージ) の組み合わせです。生の Parquet や CSV にはバージョン管理も監査ログもない点が誤答側の根拠になります。また「過去を読むだけなら AS OF、テーブルを実際に戻すなら RESTORE」という使い分けも問われやすい論点です。

4. スキーマ強制とスキーマ進化

Delta Lake はテーブルのスキーマをログに保持し、書き込み時に検証します。

💡 具体例:スキーマ強制に阻まれた書き込みを、スキーマ進化で通す

# PySpark:ソースに新列 coupon_code が増えたケース
df = spark.read.table("main.staging.new_orders")

# 既定ではスキーマ不一致でエラーになる(スキーマ強制)
# df.write.mode("append").saveAsTable("main.sales.orders")
# => AnalysisException: A schema mismatch detected ...

# 意図的な変更なら mergeSchema で列追加を許可して書き込む(スキーマ進化)
(df.write
   .mode("append")
   .option("mergeSchema", "true")
   .saveAsTable("main.sales.orders"))

ポイントは「既定は拒否、進化は明示的なオプトイン」という設計です。SQL では ALTER TABLE ... ADD COLUMN で先に列を追加する方法もあります。うっかり型の違うデータを流し込んでテーブルを汚す事故を、既定の強制が防いでくれます。

5. DML と保守:MERGE / OPTIMIZE / VACUUM

Delta Lake のテーブルには、DWH と同様の DML(UPDATE / DELETE / MERGE)がそのまま使えます。特に MERGE INTO は「あれば更新、なければ挿入(upsert)」を1文で表現でき、増分取り込みの中心的な構文です。

-- テーブル作成(Unity Catalog 配下では既定で Delta 形式)
CREATE TABLE main.sales.orders (
  order_id BIGINT,
  customer_id BIGINT,
  amount DOUBLE,
  updated_at TIMESTAMP
);

-- upsert:order_id が一致すれば更新、なければ挿入
MERGE INTO main.sales.orders AS t
USING main.staging.new_orders AS s
ON t.order_id = s.order_id
WHEN MATCHED THEN
  UPDATE SET t.amount = s.amount, t.updated_at = s.updated_at
WHEN NOT MATCHED THEN
  INSERT (order_id, customer_id, amount, updated_at)
  VALUES (s.order_id, s.customer_id, s.amount, s.updated_at);

また、運用が続くと小さなファイルが増えて読み取り性能が落ちるため、保守コマンドを併用します。

コマンドやること注意点
OPTIMIZE小さな Parquet ファイルを大きなファイルにまとめ直す(コンパクション)。読み取り性能を改善するデータの中身は変わらない。旧ファイルは「非参照」になるだけで即削除はされない
VACUUMもはや参照されていない古いデータファイルを物理削除してストレージを節約する保持期間(既定 7 日)より古いファイルが対象。削除した期間へのタイムトラベルはできなくなる

ここまでの仕組みを、書き込みから読み取りまでの流れとして図にまとめます。

書き込み(INSERT / MERGE / UPDATE / DELETE)
新しい Parquet ファイルを追加
データファイル(Parquet)列指向・イミュータブル(追記型)
変更内容をコミットとして追記
トランザクションログ(_delta_log)バージョン 0, 1, 2, ... と順序付きで記録
現在の読み取り最新コミット時点の一貫したスナップショット
タイムトラベルVERSION AS OF / TIMESTAMP AS OF / RESTORE

図:Delta Lake の書き込み・読み取りの流れ。すべての読み取りはトランザクションログを起点に解決される

✅ この節のまとめ

練習問題

問1. Delta Lake テーブルの物理的な構成として正しいものはどれか。

  1. 独自バイナリ形式のデータファイルのみで構成され、他のエンジンからは読み取れない
  2. Parquet 形式のデータファイルと、_delta_log ディレクトリ内のトランザクションログで構成される
  3. CSV ファイルと、それを管理するリレーショナルデータベースで構成される
  4. すべてのデータが単一の JSON ファイルに格納される
解答と解説を見る

正解:B

Delta Lake=「Parquet+トランザクションログ」です。オープンなフォーマットであるため、Aの「独自形式で他エンジンから読めない」は誤りです。CはストレージがオブジェクトストレージではなくRDBという点で誤り、Dはログが JSON である点と混同させる選択肢で、データ本体は Parquet に格納されます。

問2. 昨夜の ETL ジョブの不具合で main.sales.orders に誤ったデータが書き込まれた。テーブル全体を不具合前のバージョン 42 の状態に実際に戻したい。実行すべきコマンドはどれか。

  1. SELECT * FROM main.sales.orders VERSION AS OF 42
  2. DESCRIBE HISTORY main.sales.orders
  3. RESTORE TABLE main.sales.orders TO VERSION AS OF 42
  4. VACUUM main.sales.orders
解答と解説を見る

正解:C

テーブルの状態を実際に過去バージョンへ戻すのは RESTORE TABLE です。Aは過去のスナップショットを「読むだけ」でテーブル自体は変わりません。Bは履歴の確認(どのバージョンに戻すべきかの調査)には有用ですが、それ自体は何も戻しません。Dは古い非参照ファイルを物理削除するコマンドで、むしろ実行するとその期間へのタイムトラベルができなくなるおそれがあります。

問3. Delta Lake のスキーマ強制(schema enforcement)の既定の動作として正しいものはどれか。

  1. テーブル定義と一致しないスキーマのデータを書き込もうとすると、書き込みが拒否される
  2. 不一致の列は自動的にテーブルへ追加され、書き込みは常に成功する
  3. 不一致の列は警告なしに黙って破棄される
  4. 読み取り時にスキーマを検証し、書き込み時には何も検証しない
解答と解説を見る

正解:A

既定はスキーマ強制で、不一致の書き込みはエラーとして拒否されます。Bはスキーマ進化の動作ですが、mergeSchema などで明示的に有効化した場合に限られ、既定ではありません。Cのような黙殺は起きず、事故に気づけるようエラーになります。Dは逆で、検証は書き込み時(write-time)に行われます。

問4. データガバナンス担当者から「誤操作からの信頼できるロールバック、すべての変更の監査証跡、部門横断で共有できる単一の真実源」という要件が提示された。Databricks 上で最も適切な構成はどれか。

  1. 生の Parquet ファイルをオブジェクトストレージに置き、日次でフォルダごとコピーしてバックアップする
  2. Delta Lake テーブルとして管理し(タイムトラベル・DESCRIBE HISTORY)、Unity Catalog でアクセス制御と監査を一元化する
  3. CSV ファイルをノートブックごとに個別に保存し、変更のたびにファイル名へ日付を付ける
  4. テーブルを部門ごとに複製し、それぞれが自由に編集できるようにする
解答と解説を見る

正解:B

「ロールバック → タイムトラベル/RESTORE」「監査証跡 → トランザクションログ/DESCRIBE HISTORY と UC の監査機能」「単一の真実源 → UC 配下で一元管理された Delta テーブル」ときれいに対応します。AとCはバージョン管理・監査・同時実行の安全性がいずれも人手頼みで、信頼できるロールバックになりません。Dは複製ごとに内容が分岐し、「単一の真実源」の要件に真っ向から反します。