Inhaltsverzeichnis22 Abschnitte
- Was ist Medallion Architecture?
- Voraussetzungen
- Unity-Catalog-Struktur aufsetzen
- Bronze-Layer: Raw-Ingestion
- Auto Loader für Streaming-Ingestion
- Bronze-Kernprinzipien
- Silver-Layer: Bereinigung und Standardisierung
- Idempotentes Merge mit Delta Lake
- Datenqualität mit Delta-Constraints
- Gold-Layer: Business-fertige Aggregationen
- Daily-Revenue-Aggregation bauen
- Gold-Tabellen für BI-Tools optimieren
- Orchestrierung mit Databricks Workflows
- Monitoring und Observability
- Best-Practices-Zusammenfassung
- FAQ
- Brauche ich alle drei Layer?
- Wann nutze ich replaceWhere vs MERGE?
- Wie passt das in eine DSGVO-Strategie?
- Wie behandle ich Late-Arriving-Data?
- Was, wenn meine Daten zu klein sind für drei Layer?
- Schluss
TL;DR: Bronze = Raw-Append-Only mit Metadaten, Silver = bereinigt mit idempotentem
MERGE, Gold = Business-Aggregate mitreplaceWhere. Auto Loader für Bronze, Delta-Constraints + DLT-Expectations für Quality, Liquid Clustering für Gold. Klein anfangen — Bronze + Gold reicht oft.
Die Medallion-Architektur (auch Multi-Hop-Pattern genannt) ist zum De-facto-Standard für die Organisation von Data Lakes geworden. Sie bietet einen strukturierten, inkrementellen Ansatz zur Verbesserung der Datenqualität — von Roh-Ingestion bis zu Analytics-fertigen Gold-Tabellen. In diesem Guide gehen wir durch den Bau einer produktionsreifen Medallion-Pipeline auf Databricks mit Delta Lake, PySpark und Unity Catalog.
Was ist Medallion Architecture?
Die Medallion-Architektur organisiert Daten in drei Schichten:
| Layer | Beschreibung | Typische Use-Cases |
|---|---|---|
| Bronze | Rohdaten as-is, Append-only | Audit, Reprocessing, Lineage |
| Silver | Bereinigt, validiert, dedupliziert | Operative Analytics, Feature Engineering |
| Gold | Business-aggregiert, Domain-spezifisch | Dashboards, ML-Training-Sets, Reports |
Jede Schicht verfeinert die vorherige — du kannst Daten immer zur Quelle zurückverfolgen und liefert gleichzeitig saubere, performante Tabellen an Konsumenten.
Voraussetzungen
- Databricks Runtime 12.2 LTS oder höher
- Delta Lake (mit Databricks gebündelt)
- Unity Catalog aktiviert (empfohlen)
- Cloud-Storage-Mount oder Unity-Catalog-External-Location
Unity-Catalog-Struktur aufsetzen
Bevor du Code schreibst, einen klaren Namespace etablieren:
-- Create catalogs per environment
CREATE CATALOG IF NOT EXISTS prod;
CREATE CATALOG IF NOT EXISTS dev;
-- Create schemas for each medallion layer
CREATE SCHEMA IF NOT EXISTS prod.bronze;
CREATE SCHEMA IF NOT EXISTS prod.silver;
CREATE SCHEMA IF NOT EXISTS prod.gold;
Bronze-Layer: Raw-Ingestion
Der Bronze-Layer fängt Daten exakt so ein, wie sie ankommen — keine Transformationen, kein Filtering. Das ist dein Audit-Log und Sicherheitsnetz.
Auto Loader für Streaming-Ingestion
Databricks Auto Loader ist der empfohlene Weg, Files bei Scale zu ingestieren. Er verarbeitet neue Files effizient via File-Notification oder Directory-Listing:
from pyspark.sql import SparkSession
from pyspark.sql.functions import current_timestamp, input_file_name, lit
spark = SparkSession.builder.getOrCreate()
def ingest_bronze(source_path: str, target_table: str, schema_hint: str = None):
# Incrementally ingest raw files into the Bronze layer using Auto Loader
reader = (
spark.readStream
.format("cloudFiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", f"/mnt/checkpoints/{target_table}/schema")
.option("cloudFiles.inferColumnTypes", "true")
)
if schema_hint:
reader = reader.schema(schema_hint)
df = (
reader.load(source_path)
.withColumn("_ingested_at", current_timestamp())
.withColumn("_source_file", input_file_name())
.withColumn("_batch_id", lit("manual"))
)
(
df.writeStream
.format("delta")
.option("checkpointLocation", f"/mnt/checkpoints/{target_table}/stream")
.option("mergeSchema", "true")
.outputMode("append")
.trigger(availableNow=True)
.table(target_table)
.awaitTermination()
)
# Usage
ingest_bronze(
source_path="abfss://landing@storageaccount.dfs.core.windows.net/events/",
target_table="prod.bronze.events_raw"
)
Bronze-Kernprinzipien
- Niemals aus Bronze löschen — das ist deine Source of Truth
- Metadaten-Spalten ergänzen (
_ingested_at,_source_file,_batch_id) mergeSchema=truenutzen, um Schema-Evolution sauber zu behandeln- Nach Ingestion-Datum partitionieren für Performance und Lifecycle
Silver-Layer: Bereinigung und Standardisierung
Der Silver-Layer ist, wo Business-Logik startet. Hier validierst, dedupliziert, type-castest und joinst du Referenzdaten.
Idempotentes Merge mit Delta Lake
MERGE INTO (Upsert) nutzen, um Silver-Writes idempotent zu machen — sicher nach Failures wiederholbar:
from delta.tables import DeltaTable
from pyspark.sql.functions import col, to_timestamp, trim, upper, when, current_timestamp
def transform_silver_events(spark):
# Clean and standardize raw events into the Silver layer
bronze_df = spark.read.table("prod.bronze.events_raw")
silver_df = (
bronze_df
.withColumn("event_timestamp", to_timestamp(col("event_ts"), "yyyy-MM-dd'T'HH:mm:ss'Z'"))
.withColumn("user_id", trim(col("user_id")))
.withColumn("event_type", upper(trim(col("event_type"))))
.withColumn("amount", col("amount").cast("decimal(18,4)"))
.filter(col("event_timestamp").isNotNull())
.filter(col("user_id").isNotNull() & (col("user_id") != ""))
.dropDuplicates(["event_id", "event_timestamp"])
.withColumn("_silver_processed_at", current_timestamp())
)
return silver_df
def write_silver_merge(df, target_table: str, merge_keys: list):
# Upsert into Silver using Delta merge for idempotency
df.limit(0).write.format("delta").mode("ignore").saveAsTable(target_table)
target = DeltaTable.forName(spark, target_table)
merge_condition = " AND ".join([f"target.{k} = source.{k}" for k in merge_keys])
(
target.alias("target")
.merge(df.alias("source"), merge_condition)
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.execute()
)
silver_df = transform_silver_events(spark)
write_silver_merge(
df=silver_df,
target_table="prod.silver.events",
merge_keys=["event_id"]
)
Datenqualität mit Delta-Constraints
Datenqualität auf Tabellen-Ebene mit Delta-Constraints durchsetzen:
ALTER TABLE prod.silver.events
ADD CONSTRAINT chk_amount_positive CHECK (amount >= 0);
ALTER TABLE prod.silver.events
ADD CONSTRAINT chk_event_type_valid CHECK (event_type IN ('CLICK', 'PURCHASE', 'VIEW', 'SIGN_UP'));
Alternativ Databricks Delta Live Tables (DLT)-Expectations für Pipeline-Level-Qualitätskontrolle:
import dlt
from pyspark.sql.functions import col, to_timestamp
@dlt.table(name="events_silver")
@dlt.expect_or_drop("valid_amount", "amount >= 0")
@dlt.expect_or_fail("non_null_user", "user_id IS NOT NULL")
def events_silver():
return (
dlt.read("events_raw")
.withColumn("event_timestamp", to_timestamp(col("event_ts")))
.dropDuplicates(["event_id"])
)
Gold-Layer: Business-fertige Aggregationen
Gold-Tabellen sind zweckgebaut für spezifische Konsumenten — BI-Dashboards, ML-Modelle oder APIs. Für Read-Performance optimieren.
Daily-Revenue-Aggregation bauen
from pyspark.sql.functions import (
col, sum as spark_sum, count, countDistinct,
date_trunc, avg, current_timestamp
)
def build_gold_daily_revenue(spark):
# Aggregate Silver events into a daily revenue Gold table
df = (
spark.read.table("prod.silver.events")
.filter(col("event_type") == "PURCHASE")
.filter(col("event_timestamp") >= "2024-01-01")
)
gold_df = (
df
.withColumn("event_date", date_trunc("day", col("event_timestamp")))
.groupBy("event_date", "country", "product_category")
.agg(
spark_sum("amount").alias("total_revenue"),
count("event_id").alias("transaction_count"),
countDistinct("user_id").alias("unique_buyers"),
avg("amount").alias("avg_order_value")
)
.withColumn("_gold_updated_at", current_timestamp())
)
return gold_df
gold_df = build_gold_daily_revenue(spark)
(
gold_df.write
.format("delta")
.mode("overwrite")
.option("replaceWhere", "event_date >= '2024-01-01'")
.partitionBy("event_date")
.saveAsTable("prod.gold.daily_revenue")
)
Gold-Tabellen für BI-Tools optimieren
-- Optimize file sizes for efficient scans
OPTIMIZE prod.gold.daily_revenue ZORDER BY (country, product_category);
-- Enable liquid clustering for automatic optimization (DBR 13.3+)
ALTER TABLE prod.gold.daily_revenue
CLUSTER BY (event_date, country);
-- Set table properties for BI performance
ALTER TABLE prod.gold.daily_revenue
SET TBLPROPERTIES (
'delta.autoOptimize.optimizeWrite' = 'true',
'delta.autoOptimize.autoCompact' = 'true'
);
Orchestrierung mit Databricks Workflows
Alle drei Layer in einem Databricks-Workflow verdrahten:
# workflow.yml (Databricks Asset Bundle format)
resources:
jobs:
medallion_pipeline:
name: "Medallion Pipeline - Events"
tasks:
- task_key: bronze_ingestion
notebook_task:
notebook_path: ./notebooks/bronze_ingestion
job_cluster_key: bronze_cluster
- task_key: silver_transform
depends_on:
- task_key: bronze_ingestion
notebook_task:
notebook_path: ./notebooks/silver_transform
job_cluster_key: standard_cluster
- task_key: gold_aggregation
depends_on:
- task_key: silver_transform
notebook_task:
notebook_path: ./notebooks/gold_aggregation
job_cluster_key: standard_cluster
Monitoring und Observability
Datenqualitäts-Metriken über Layer hinweg tracken:
from datetime import datetime
def log_layer_metrics(table: str, layer: str, spark):
# Log row counts and quality metrics for observability
df = spark.read.table(table)
metrics = {
"table": table,
"layer": layer,
"row_count": df.count(),
"timestamp": datetime.now().isoformat()
}
print(f"[{layer.upper()}] {table}: {metrics['row_count']:,} rows")
return metrics
Das Pattern lässt sich erweitern, um Metriken in eine Monitoring-Delta-Tabelle zu pushen und in Databricks-SQL-Dashboards zu visualisieren — oder Tools wie Harbinger Explorer anbinden, für plattformübergreifende Sichtbarkeit auf Pipeline-Health.
Best-Practices-Zusammenfassung
| Aspekt | Empfehlung |
|---|---|
| Bronze-Writes | Append-only, Metadaten-Spalten |
| Silver-Writes | Idempotentes MERGE INTO nach Natural Key |
| Gold-Writes | replaceWhere für inkrementellen Partition-Overwrite |
| Schema-Evolution | mergeSchema=true in Bronze, explizit ab Silver |
| Datenqualität | Delta-Constraints + DLT-Expectations |
| Performance | OPTIMIZE ZORDER oder Liquid Clustering auf Gold |
| Orchestrierung | Databricks Workflows mit Dependency-Chains |
| Monitoring | Delta-History + Custom-Metrics-Tabellen |
FAQ
Brauche ich alle drei Layer?
Nein. Bronze + Gold reicht oft für kleinere Setups. Silver kommt dazu, wenn du saubere, wiederverwendbare Datasets brauchst, die mehrere Gold-Tabellen speisen.
Wann nutze ich replaceWhere vs MERGE?
replaceWhere für Gold-Aggregate mit klarer Partition-Grenze (z. B. eine Datums-Partition komplett neu rechnen). MERGE für Silver-Upserts mit Natural Key, wo du nicht weißt, welche Zeilen sich geändert haben.
Wie passt das in eine DSGVO-Strategie?
Bronze sollte mit Retention-Policy laufen (z. B. 90 Tage), Silver/Gold mit Lösch-Pipelines für Right-to-be-Forgotten. Personenbezogene Daten in Bronze pseudonymisieren ist häufig die sauberste Lösung.
Wie behandle ich Late-Arriving-Data?
Silver MERGE mit Watermark auf Event-Timestamp. Gold mit replaceWhere auf rückwirkenden Zeitraum (z. B. letzte 7 Tage neu rechnen).
Was, wenn meine Daten zu klein sind für drei Layer?
Unter ~100 GB ist Medallion-Overkill. Eine Staging-+Mart-Struktur in dbt reicht oft. Erst skalieren, wenn das wirklich Schmerz schafft.
Schluss
Die Medallion-Architektur ist nicht nur Ordner-Organisation — sie ist eine Disziplin, um Datenqualität, Zuverlässigkeit und Vertrauen über deinen gesamten Lakehouse zu managen. Durch die Kombination von Delta-Lake-ACID-Garantien mit Databricks-Processing-Power und Unity-Catalog-Governance bekommst du eine produktionsreife Datenplattform, die mit deiner Organisation skaliert.
Klein anfangen: schon ein Zwei-Layer-Bronze/Gold-Setup liefert enorme Vorteile gegenüber einem rohen Datensumpf. Silver dazunehmen, wenn wiederverwendbare bereinigte Datasets nötig werden.
Stand: 14. Mai 2026.
Geschrieben von
Harbinger Team
Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastrukturkritische Tech-Entscheidungen — keine Marketing- Folien, sondern echte Trade-offs aus Production-Workloads.
Hat dir das geholfen?
Jede Woche ein neuer Artikel über DACH-Cloud, Data und AI — direkt in dein Postfach. Kein Spam, kein Marketing-Sprech.
Kein Spam. 1-Klick-Abmeldung. Datenschutz bei Loops.so.