Zurück zum Log
· 12 min

Postgres Partitionierung jenseits der Basics

PostgresPerformance

Warum RANGE nicht genug ist

Die meisten Postgres-Partitionierungs-Tutorials zeigen RANGE-Partitionierung auf einem Zeitstempel. Für einfache Log-Tabellen funktioniert das. Für echte Workloads mit multi-tenant Daten, unterschiedlichem Zugriff pro Tenant und variablen Datenmengen bricht es zusammen.

Ich hatte einen Kunden mit einer 4TB Orders-Tabelle. RANGE auf created_at — die Standard-Wahl. Das Problem: 80% der Queries filterten auf tenant_id, nicht auf Zeit. Jede Query musste alle 36 monatlichen Partitionen scannen.

Die Lösung: LIST + HASH Kombination

Anstatt eine Dimension zu partitionieren, kombinieren wir zwei. Sub-partitioning: LIST by tenant, dann HASH by order_id. Per-tenant partitions, each sub-partitioned by HASH. Das Ergebnis: Queries die nach tenant_id filtern scannen nur noch eine Partition.

Benchmarks

Auf der 4TB-Tabelle (100M Zeilen, 50 Tenants): SELECT by tenant_id + id ging von 340ms auf 12ms (28x). SELECT by tenant_id + range von 890ms auf 45ms (19x). VACUUM einer einzelnen Partition von 45min auf 8min (5.6x).

Wann welche Strategie

RANGE: Reine Time-Series, Logs, IoT-Daten mit regelmäßigem Archiving
HASH: Gleichmäßig verteilte IDs, Multi-Tenant mit ähnlicher Größe
LIST: Stark unterschiedliche Tenant-Größen, klar abgegrenzte Datenbereiche
LIST + HASH/SUB: Multi-Tenant mit großen Tenants die eigene Partitionen brauchen

Fazit

Partitionierung ist kein Set-it-and-forget-it. Wenn deine Query-Patterns nicht zu deiner Partitionierungs-Strategie passen, kostet dich das mehr als es bringt. Messen, nicht raten.