メダリオンアーキテクチャとは、Databricksが提唱したデータレイクハウスの設計パターンで、Bronze(生データ)・Silver(クレンジング済み)・Gold(ビジネス用)の3層でデータ品質を段階的に高めていく考え方です。各層の設計指針とコード例、dbtの3層構造(Raw / Staging / Mart)との対応、そして層の境界を守る原則までを順に見ていきます。

メダリオンアーキテクチャとは

メダリオンアーキテクチャは、Databricksが自社のレイクハウス設計で広めた概念で、メダル(金銀銅)になぞらえた3層構造を特徴とします。Bronze層にソースの原形を格納し、Silver層でクレンジングと整形を行い、Gold層でビジネスロジックを適用します。この段階的な品質向上が基本の思想です。

発祥はDatabricksですが、概念自体はクラウドDWH(BigQuery、Snowflake)でも適用できます。dbtコミュニティの「3層構造(Raw / Staging / Mart)」と本質的に同じ思想であり、呼び方が違うだけです。

実装基盤としては、DatabricksのDelta Lakeが参照実装として広く使われています。各層でACIDトランザクション、スキーマ進化、タイムトラベル(過去のデータ状態への遡及)が使えるため、「先週時点のデータで再分析したい」といった要件にも応えられます。オープンソースで組むなら、Apache IcebergとTrinoやDuckDBの組み合わせでも同等のアーキテクチャを実現できます。

Bronze層の設計

Bronze層の役割は「生データを忠実に保存すること」です。ソースシステムから受け取ったデータをそのままの形で保持し、将来の再処理や監査に備えます。変換・集計・結合は行いません。ここで気を付けるべきはスキーマ進化への対応で、Delta Lakeの場合はschema evolutionを有効化して後方互換性を確保します。

以下はDelta LakeでBronzeテーブルを作成する例です。

CREATE TABLE bronze.orders (
    id STRING,
    amount STRING,
    created_at STRING,
    _ingested_at TIMESTAMP,
    _source_file STRING
) USING DELTA
PARTITIONED BY (DATE(_ingested_at))
TBLPROPERTIES ('delta.autoOptimize.autoCompact' = 'true');

Silver層の設計

Silver層はBronzeを受けて、クレンジング・正規化・ビジネスキーの付与を行います。型変換、NULLハンドリング、重複除去、軽いJOINによるエンリッチメントといった処理を施し、「整った元テーブル」を準備します。ここまで到達したデータは、分析利用者が安心して触れるベースラインになります。

ひとつ意識したいのは冪等性(何度実行しても同じ結果になること)です。dbtのインクリメンタルモデルとmerge戦略を組み合わせると、冪等性を保ちながら大量データを効率よく処理できます。BronzeからSilverへの変換例をSQLで示します。

CREATE OR REPLACE TABLE silver.orders AS
SELECT
    id AS order_id,
    CAST(amount AS DOUBLE) AS amount,
    CAST(created_at AS TIMESTAMP) AS created_at,
    _ingested_at
FROM bronze.orders
WHERE id IS NOT NULL
  AND amount IS NOT NULL;

Gold層の設計

Gold層は分析・BI・業務還流で利用する最終形です。ビジネスロジックの適用、KPI計算、ファクト/ディメンション構築、集計マートの作成など、ビジネス文脈に即したテーブルを用意します。「ビジネスの質問に直接答えるテーブル」を原則とし、汎用的すぎるワイドテーブルを避けると、クエリ性能と保守性を両立できます。ドメイン別にフォルダを分け、テーブル数が爆発しないよう統治することも大切です。

CREATE OR REPLACE TABLE gold.daily_sales AS
SELECT
    DATE(created_at) AS sale_date,
    COUNT(*) AS order_count,
    SUM(amount) AS total_amount,
    AVG(amount) AS avg_amount
FROM silver.orders
GROUP BY DATE(created_at);

全体像とdbt 3層構造との対応

メダリオンアーキテクチャ全体は単純な1本の直線ではありません。複数のソースがBronzeへ集約され、Silverで整えられたあと、用途別の複数のGoldマートへ枝分かれし、BI・機械学習・業務SaaSへと届きます。入口で集約し、出口で分岐するのが特徴です。

flowchart LR S1[“業務DB”] –> B[“Bronze(生データ)”] S2[“SaaS・API”] –> B S3[“イベントログ”] –> B B –> SV[“Silver(クレンジング・統合)”] SV –> G1[“Gold:売上マート”] SV –> G2[“Gold:顧客360”] SV –> G3[“Gold:KPI集計”] G1 –> BI[“BI・ダッシュボード”] G2 –> BI G2 –> ML[“機械学習・予測”] G2 –> RE[“Reverse ETL→業務SaaS”] G3 –> BI B -.->|障害時は再処理| SV

各層の設計ルールをまとめました。

観点BronzeSilverGold
データ品質保証なし型とNULLを整備ビジネス品質
変換内容なしクレンジング・軽JOIN集計・KPI・ロジック
アクセス制御限定的データチーム分析者・全社員
保持期間長期(再処理用)中期短〜中期(再計算可能)
テスト密度取り込み完了確認型・NULL・ユニークビジネスルール検証
更新頻度取り込みごと日次〜時次日次
ストレージ形式Delta LakeDelta LakeDelta Lake / 集計テーブル

dbtの3層構造との対応は次の通りです。呼び名は違っても、担う役割はそのまま重なります。dbtでの具体的な実装手順(プロジェクト構成・命名・テスト)はdbtで3層構造を実装するに記載しています。

メダリオンdbt 3層構造役割
BronzeRaw原形保存
SilverStaging / Intermediateクレンジング・共通ロジック
GoldMart分析用・ビジネス

Snowflakeで3層を作るときに置き換わる3点

ここまでのコード例はDelta Lakeを前提にしています。メダリオンアーキテクチャの解説はDatabricks発の情報が多く、Snowflakeで同じ3層を組もうとすると対応するものが見つからずに手が止まりがちです。層の考え方はそのまま使えます。実装で置き換えが要るのは、取り込み・増分更新・履歴の保持期間の3点だけです。

やることDatabricksSnowflakeBigQuery
継続的な取り込みAuto Loader / COPY INTOSnowpipeStorage Write API / Data Transfer Service
ソース列の増加への追従Delta のスキーマ進化ENABLE_SCHEMA_EVOLUTIONCOPY INTO と Snowpipe のみ。1回のCOPYで既定100列まで)スキーマ自動検出
Silverの増分更新dbt incremental + MERGEDynamic Table(TARGET_LAG 最小60秒)MERGE + スケジュールされたクエリ
遡れる期間の既定値実質7日(VACUUM が deletedFileRetentionDuration を基準にファイルを消すため)1日(Standard は最大1日、Enterprise 以上で最大90日)7日(2〜7日で設定可)
消したデータの追加保持なしFail-safe 7日(変更不可)Fail-safe 7日(変更不可)

Bronzeへの取り込みでは、列が増えたときの条件を先に確認する

Snowflakeの取り込みはSnowpipeが標準で、DatabricksのAuto Loaderと同じくステージ上のファイルを検知して継続的に読み込みます。ここは素直に置き換わります。

つまずくのはソース側に列が増えたときです。SnowflakeもENABLE_SCHEMA_EVOLUTION = TRUEで列の自動追加に対応しますが、成立には3つの条件がそろう必要があります。COPY INTOMATCH_BY_COLUMN_NAMEを指定していること、実行ロールがEVOLVE SCHEMAかOWNERSHIPを持っていること、そして1回のCOPYで追加される列が既定の100本以内であることです。INSERTとTasksからの書き込みでは働きません。取り込みをTaskで自作していると、ここで静かに落ちます。

CREATE TABLE bronze.orders (
    id            STRING,
    amount        STRING,
    created_at    STRING,
    _ingested_at  TIMESTAMP,
    _source_file  STRING
)
ENABLE_SCHEMA_EVOLUTION = TRUE
DATA_RETENTION_TIME_IN_DAYS = 0;

COPY INTO bronze.orders
FROM @raw_stage/orders/
FILE_FORMAT = (TYPE = PARQUET)
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;

SilverはDynamic Tableに置き換えると、順序を書かずに済む

dbtのインクリメンタルモデルとMERGEで書いていた部分は、SnowflakeではDynamic Tableで置き換えられます。SELECTの結果をマテリアライズしたうえで、TARGET_LAGに指定した鮮度を保つようSnowflakeが自動で更新します。変更のあった行だけを計算する増分更新にも対応します。

CREATE OR REPLACE DYNAMIC TABLE silver.orders
    TARGET_LAG = '1 hour'
    WAREHOUSE  = transform_wh
AS
SELECT
    id                                AS order_id,
    CAST(amount AS NUMBER(18,2))      AS amount,
    CAST(created_at AS TIMESTAMP_NTZ) AS created_at,
    _ingested_at
FROM bronze.orders
WHERE id IS NOT NULL
  AND amount IS NOT NULL;

TARGET_LAGの最小値は60秒です。Silverのように後段からしか参照されない中間テーブルにはTARGET_LAG = DOWNSTREAMを指定でき、下流のGoldが必要とするときだけ更新されます。日次のGoldしか無いのにSilverを1時間ごとに回して費用を払う、という無駄が避けられます。

Stream と Task の組み合わせでも同じことは実現できます。ただし依存の順序を自分で書くことになるため、層が3つで収まるうちはDynamic Tableのほうが運用が軽くなります。

「障害時はBronzeから再処理」は、遡れる期間を確認してから書く

3層設計の安心材料は「壊れてもBronzeから作り直せる」という一点にあります。この前提は履歴が残っている期間しか成り立ちません。既定値が両者でかなり違うので、設計の前に確認しておきます。

Databricksではdelta.logRetentionDurationの既定が30日、delta.deletedFileRetentionDurationの既定が7日です。VACUUMは後者を基準に参照されなくなったファイルを削除するため、既定のままで実際に遡れるのは7日までです。それより前に戻したいなら両方を引き上げます。ログの保持は、ファイルの保持以上にしておく必要があります。

Snowflakeの Time Travel は既定1日です。しかも Standard Edition では0日か1日しか選べません。90日まで延ばせるのは Enterprise Edition 以上です。「四半期前の状態で再集計したい」という要件が監査や経理から出てくる会社では、これは設計ではなくエディション選定の話になります。見積り前に確認しておく項目です。

3層にすると保管量は3倍になるのか

3層構造を役員に説明すると、ほぼ必ず「同じデータを3回持つのだから、費用も3倍になるのか」と聞かれます。答えは、設計しだいで3倍より小さくもなるし、1つのテーブルで9倍にもなる、です。

Snowflakeの永続テーブルは、Active・Time Travel・Fail-safe のどの状態にあっても課金対象になります。公式ドキュメントは、200GBのディメンションテーブルを1日20回更新した場合の内訳を挙げています。

状態保管量計算
Active200 GB現在の実体
Time Travel4 TB200 GB × 20回 × 1日(既定)
Fail-safe28 TB200 GB × 20回 × 7日(変更不可)
合計32.2 TB実体の約160倍

これは更新頻度が極端な例です。ただし計算式は自社のテーブルにそのまま当てはめられます。保管量は、実体に加えて「1日の変更量 × Time Travel日数」と「1日の変更量 × 7日」が積み上がる形になります。

身近な例で見ます。500GBの顧客マスタをSilverに置き、日次で全件洗い替えしている構成はよく見かけます。

構成Active1日の変更量Time Travel(1日)Fail-safe(7日)合計
永続テーブル・日次で全件洗い替え500 GB500 GB500 GB3.5 TB4.5 TB
永続テーブル・MERGEで差分更新(変更5GB/日)500 GB5 GB5 GB35 GB540 GB
transientテーブル・日次で全件洗い替え500 GB500 GB0〜500 GB無し500 GB〜1 TB

同じ500GBのテーブルが、実装しだいで500GBにも4.5TBにもなります。効く手は2つあり、どちらも単独で8〜9倍の差を生みます。

1つ目は、全件洗い替えをやめてMERGEの差分更新にすることです。冪等性を保つ話として先に書いた設計が、そのまま保管費用にも効いてきます。

2つ目は、BronzeとSilverを永続テーブルにしないことです。transientテーブルは Fail-safe を持たず、Time Travel も0日か1日しか設定できません。復旧力が下がるように見えますが、Bronzeはソースから取り直せる層であり、SilverはBronzeから再計算できる層です。Fail-safeで7日分を二重に抱える意味がありません。ここで永続テーブルを使い続ける理由があるのは、ソース側が短期間で消えてしまい、取り直しがきかないデータだけです。

つまり「3層にすると3倍」という見立ては、方向としては正しくありません。Goldは集計後の小さなテーブルが中心なのでBronzeより一桁小さくなることが多く、実際の合計は3倍に届かないのが普通です。にもかかわらず請求が跳ねている場合、原因は層を分けたことではなく、洗い替えの実装かテーブル種別のどちらかにあります。

BigQueryでも構図は同じです。タイムトラベルの既定は7日(2〜7日で設定可)で、そのあとに変更できない7日間のFail-safe が続きます。物理ストレージ課金を選んだ場合、この両方が課金対象になります。論理ストレージ課金なら基本料金に含まれます。どちらの課金モデルを選んでいるかで、同じ設計でも請求が変わります。

層の境界を守る:原則とよくある失敗

メダリオンアーキテクチャが破綻するときは、たいてい層の境界があいまいになっています。どの層に何を置くかさえ守れば、多くのトラブルは未然に防げます。よくある失敗と、その裏返しである守るべき原則を整理します。

よくある失敗なぜ起きるか守るべき原則
Bronzeを加工してしまう早く整えたいBronzeは無変換で生データを保存。障害時に完全再処理できる状態を保つ
Silverを飛ばしてGoldを作る手早く結果を出したい必ずSilverを通す。後からSilverを挿入するのは全書き直しになる
Silver層の肥大化変換を積み上げすぎるSilverにビジネスロジックを持たせない。整形までで止める
Gold層の粒度ミス集計しすぎて詳細が出せない完全集計と詳細の両方を用意し、ビジネスの質問に答える粒度にする
層・派生の増やしすぎ「念のため」で分岐する中間層は最小限に。どこを参照するかを1つに定める
コストの膨張保持と再計算が積み重なるBronzeの保持期間を絞り、Goldは必要に応じてマテリアライズする

とくに重要なのは、Silverの変換を冪等に保つことです。何度実行しても同じ結果になれば、再処理や差分更新が安全になり、上の失敗の多くを同時に避けられます。

まとめ

メダリオンアーキテクチャは、データの品質段階を明確に意識させる優れた設計パターンです。Databricks環境だけでなく、SnowflakeやBigQueryでも同じ思想を取り入れられます。各層の役割と境界を守り、dbtの3層構造と併せて理解することで、チーム内の共通言語が生まれ、保守性の高いデータ基盤を築けます。

よくある質問

メダリオンアーキテクチャはDatabricks専用ですか?

いいえ。Databricksが提唱しましたが、概念自体はクラウドDWH(BigQuery、Snowflake)でも適用可能です。dbtの3層構造と本質的に同じ考え方です。ツール選定と設計思想は切り離して考えましょう。

Bronze/Silver/Goldの3層は必須ですか?

規模に応じて2層(Raw / Mart)にすることも可能です。ただし中規模以上ではSilver層が品質ゲートとして重要な役割を果たします。最初は3層で設計し、必要に応じて簡略化するのが安全です。

各層にどのようなテストを設定すべきですか?

Bronze層は取り込み完了の確認、Silver層は型チェック・NULL制約・ユニーク制約、Gold層はビジネスルール検証が基本です。層が進むほどテストを厳密にします。dbt testsやElementaryで自動化するのが定石です。

SnowflakeでBronze層に使うテーブルは永続とtransientのどちらがよいですか?

Bronzeはソースから取り直せる層なので、原則transientテーブルにします。transientはFail-safeを持たないため、1日の変更量の7日分がまるごと保管対象から外れます。永続テーブルを選ぶ理由があるのは、ソース側のデータが短期間で消えてしまい、あとから取り直せない場合だけです。

メダリオンアーキテクチャをSnowflakeで組むとき、dbtは要りますか?

SilverとGoldの更新だけならDynamic Tableで完結できます。TARGET_LAGを指定すればSnowflakeが依存順序と増分更新を引き受けるため、オーケストレーションを別に用意せずに済みます。テストの記述、ドキュメント生成、環境の切り替えまで含めて管理したい段階になったら、dbtを重ねる判断になります。

関連記事:データプロダクトとは?データをプロダクトとして扱うための5原則|広告データ基盤設計:マルチチャネル広告データの統合とアトリビューション分析|Airbyte vs Fivetran:オープンソースと商用ETLの選び方

▼ このトピックをポチポチ学ぶ

データエンジニア入門:取り込みから配膳まで

関連記事を順序立てて読みながら、ステップごとに4択クイズで理解を確認できる学習パスです。登録不要・進捗自動保存。

学習パスを始める →

出典

各製品の仕様・機能に関する記述は、上記の公式ドキュメントにもとづきます。仕様は更新されるため、導入判断の際は参照日以降の変更を各公式情報でご確認ください。本文中の試算や運用上の判断は当社の見解です。

Bronze・Silver・Goldの三層は、名前を借りるだけなら簡単ですが、どこで品質を担保するかを決めないと形だけになります。自社のデータ量と体制に合った層の切り方は、初回相談(30分・無料)で設計できます。