Inhaltsverzeichnis10 Abschnitte
Deine Query scannt 900 GB, um 2.000 Zeilen zurückzugeben. Der Fix ist kein größerer Cluster — es ist eine Partitionierungsstrategie, die du wahrscheinlich übersprungen hast.
TL;DR
- Partitioning organisiert Daten physisch, damit Queries nur die relevanten Slices anfassen.
- Vier Pattern: Range (Zeit), List (Kategorien), Hash (gleichmäßige Verteilung), Hive-Style (Directories).
- Filter-Spalte sollte in >80 % deiner WHERE-Klauseln vorkommen.
- Funktionen auf der Partitions-Spalte killen Pruning.
- Über-Partitionierung killt Performance — Planning > Execution.
Was Partitioning ist
Partitioning teilt ein Dataset in kleinere, unabhängige Units — Partitions — basierend auf Spaltenwerten. Die Engine liest nur die relevanten Partitions = Partition Pruning.
| Strategie | Wie sie splittet | Best für |
|---|---|---|
| Range | Wert in definiertem Bereich | Datums, Time-Series, sequenzielle IDs |
| List | Wert matched explizite Set | Country-Codes, Kategorien, Status |
| Hash | Hash modulo N Buckets | High-Cardinality ohne Natural Range |
Hive-Style ist eine Directory-Naming-Convention von Spark, Hive, Lakehouses — layert auf die Strategien.
Range-Partitioning
Häufigste Form: Partition nach Datum.
CREATE TABLE orders (
order_id BIGINT,
customer_id BIGINT,
order_date DATE,
amount NUMERIC
) PARTITION BY RANGE (order_date);
CREATE TABLE orders_2024_q1 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
CREATE TABLE orders_2024_q2 PARTITION OF orders
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
Bei WHERE order_date BETWEEN '2024-06-01' AND '2024-06-30' liest der Planner nur orders_2024_q2.
Trade-Offs:
- Exzellent für Time-Series und Date-Bound Reporting
- Einfach altes zu archivieren (Partition droppen statt Millionen Rows)
- Anfällig für Partition-Skew bei ungleicher Verteilung
- Schwach für Point-Lookups auf Non-Partition-Columns
List-Partitioning
Routet jede Zeile zu einer Partition basierend auf diskretem Wert. Gut bei Low-Cardinality.
CREATE TABLE sales (
sale_id BIGINT,
region TEXT,
product_id INT,
revenue NUMERIC
) PARTITION BY LIST (region);
CREATE TABLE sales_europe PARTITION OF sales
FOR VALUES IN ('DE', 'FR', 'NL', 'ES', 'IT', 'AT', 'CH');
CREATE TABLE sales_americas PARTITION OF sales
FOR VALUES IN ('US', 'CA', 'BR', 'MX');
Trade-Offs:
- Saubere Isolation logischer Datendomänen
- Einfache Per-Region-Access-Controls und Maintenance-Fenster
- Neue Werte = DDL-Changes
- Sinnlos, wenn Queries nicht auf der Partition-Spalte filtern
Hash-Partitioning
Hashfunktion auf Spaltenwert, Routing zu hash(value) % N. Keine natürliche Order, nur gleichmäßige Verteilung.
CREATE TABLE events (
event_id BIGINT,
customer_id BIGINT,
event_type TEXT,
created_at TIMESTAMPTZ
) PARTITION BY HASH (customer_id);
CREATE TABLE events_p0 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE events_p1 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE events_p2 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE events_p3 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 3);
Hash gibt dir gleichmäßige Verteilung, hilft aber nicht bei Range-Queries — WHERE customer_id > 100000 scannt alle vier.
Trade-Offs:
- Verhindert Skew, gut für Parallel-Processing und Write-Throughput
- Kein Pruning für Range-Filter
- Repartitioning (N ändern) = Full-Rewrite
Hive-Style-Partitioning
Directory-Naming-Convention statt Algorithmus. Daten in Folder-Hierarchie mit Partitions-Werten:
s3://my-bucket/events/
├── year=2024/
│ ├── month=01/
│ │ ├── day=01/
│ │ │ └── part-00000.parquet
│ │ └── day=02/
│ └── month=02/
└── year=2025/
Spark, Hive, Trino, Athena, DuckDB unterstützen das nativ.
df.write \
.mode("overwrite") \
.partitionBy("year", "month", "day") \
.parquet("s3://my-bucket/events/")
spark.read \
.parquet("s3://my-bucket/events/") \
.filter("year = 2024 AND month = 1")
Klassischer Fehler: Partition nach High-Cardinality-Spalte wie user_id. 10 Mio. User = 10 Mio. Directories. File-System-Overhead killt Performance. Faustregel: DISTINCT count < ~10.000.
Trade-Offs:
- Portabel (Spark, Athena, Trino, DuckDB, Hive)
- Menschlich lesbar
- Inkrementelle Writes ohne Full-Rewrite
- Small-Files-Problem
- Metadata-Overhead bei zu vielen Partitions
Welche Strategie wann?
| Szenario | Empfehlung |
|---|---|
| Time-Series / Append-by-Date | Range (Datum) + Hive-Style |
| Multi-Region-Reporting | List (Region) |
| High-Cardinality-Join-Keys | Hash |
| Lakehouse auf Object Storage | Hive-Style (Range oder List darunter) |
| Große Parallel-Loads | Hash |
| DSGVO-Löschung nach Region | List (Partition droppen = nach Region löschen) |
Heuristik: Die Spalte, nach der du partitionierst, sollte in >80 % deiner WHERE-Klauseln auftauchen.
Pruning-Killer
-- PRUNING WORKS — direkter Equality-Filter
SELECT * FROM events WHERE year = 2024 AND month = 3;
-- PRUNING FAILS — Funktion auf der Partition-Spalte
SELECT * FROM events WHERE YEAR(created_at) = 2024;
-- PRUNING FAILS — nicht-deterministischer Ausdruck
SELECT * FROM events WHERE created_at > NOW() - INTERVAL 30 DAYS;
Wenn deine Partition-Spalten year/month/day als Integer sind: filtere gegen Integers, nicht abgeleitete Ausdrücke.
Über-Partitioning
Mehr Partitions ist nicht besser. Jede ist ein Metadata-Eintrag, jede Hive-Style-Partition ein Directory-Listing. 500.000 Partitions = Planning-Overhead schlägt Scan-Ersparnis.
Zeichen für Über-Partitioning:
- Query-Planning dauert länger als Execution
- Viele Partitions mit < 10 MB Daten
- Filezahl wächst schneller als Datenvolumen
Fix: Partition Coarsening (täglich statt stündlich) oder File Compaction (OPTIMIZE in Spark, Delta Auto-Optimize).
FAQ
Wie viele Partitions sind zu viele? Hängt vom Engine ab. Spark/Hive vertragen Tausende, brechen bei Hunderttausenden ein. DuckDB hat keine harten Grenzen, aber Listing-Cost auf S3 wird teuer.
Hash + Range kombinieren? Geht (Composite Partitioning). Komplexer zu managen, lohnt sich nur bei massiven Tables.
Wann lohnt sich Partitioning gar nicht? Tables unter ~1 GB. Da kostet das Management mehr als der Scan.
Was hat das mit DSGVO zu tun?
Mit List-Partition nach country kannst du DSGVO-Löschungen einer ganzen Region per Partition-Drop machen statt millionenfach UPDATE/DELETE.
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.