Postgres Partitionierung jenseits der Basics
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
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.