Fallstudie · Vertrauliche Analytics-Plattform, Nordamerika

10-facher Ingest-Durchsatz in einer PostgreSQL-Analytics-Pipeline mit UNLOGGED-Tabellen

Wie UnlockLive mit PostgreSQL-UNLOGGED-Tabellen, gebatchter COPY-Ingestion und einem disziplinierten Zwei-Tabellen-Muster eine hochvolumige Analytics-Pipeline von 8 Tsd. auf 80 Tsd. Ereignisse/Sek. gesteigert hat – ohne die Datenintegritätsgarantien zu verlieren, die der auditierte Pfad verlangte.

  • BrancheDaten / Analytik
  • Jahr2024
  • LandUSA
  • Dauer3 Monate
10x Ingest Throughput on a PostgreSQL Analytics Pipeline Using UNLOGGED Tables hero screenshot

Ergebnisse auf einen Blick

  • 10xDauerhafter Ingest-Durchsatz (8K → 80K+ Ereignisse/Sek.)
  • 60%Geringere RDS-IOPS-Kosten bei gleicher Instanzklasse
  • <50msp95-Latenz für Stage-1-Inserts unter Dauerlast
  • 0Datenintegritäts-Regressionen im auditierten Reporting-Pfad

Die Herausforderung

Eine Analytics-Plattform erfasste Produktereignisse einer wachsenden Kundenbasis in einer einzigen Tabelle „raw_events“. Das ursprüngliche Schema war korrekt, aber langsam: Jedes Ereignis landete in einer protokollierten Tabelle mit sieben Indizes, das WAL war der Engpass, und der Ingest-Durchsatz hatte bei etwa 8.000 Ereignissen pro Sekunde seine Grenze erreicht. Die RDS-IOPS waren der dominierende Kostenblock, und eine geplante Verdreifachung der Kundenzahl hätte die Pipeline gesprengt, noch bevor der nächste Vertrag unterzeichnet war.

Das Team hatte die üblichen Ratschläge gelesen – „eine Queue verwenden“, „die Tabelle shardieren“, „auf eine dedizierte TSDB umziehen“ –, aber all diese Antworten bedeuteten eine Migration über mehrere Quartale. Die eigentliche Frage lautete: Kann PostgreSQL mithalten, wenn wir aufhören, dagegen anzukämpfen? Der Haken war eine harte Anforderung: Alles, was die auditierten Reporting-Tabellen erreichte, musste dauerhaft, konsistent und indiziert sein. Wir durften die Integrität auf der Analytics-Seite nicht opfern, um die Ingest-Seite zu entlasten.

Unsere Lösung

Wir haben die Pipeline in zwei klar getrennte Stufen mit zwei unterschiedlichen Dauerhaftigkeitsverträgen aufgeteilt.

Stufe 1 (Ingest): eine UNLOGGED-Staging-Tabelle, die den Datenstrom aufnimmt. UNLOGGED-Tabellen in PostgreSQL überspringen WAL-Schreibvorgänge bei Inserts, was für kurzlebige Staging-Daten genau der richtige Kompromiss ist – wir erhalten den 5- bis 10-fachen reinen Insert-Durchsatz und nehmen dafür in Kauf, die gestageten Zeilen bei einem Serverabsturz zu verlieren. In Kombination mit gebatchtem COPY (nicht INSERT) aus einem Python-Ingest-Service und der bewussten Regel „keine Indizes auf der Staging-Tabelle“ stieg der reine Ingest auf derselben RDS-Instanz von 8 Tsd. auf über 80 Tsd. Ereignisse/Sek.

Stufe 2 (dauerhaft): Ein Celery-Worker leert die Staging-Tabelle in Mikro-Batches in die echte, vollständig protokollierte, indizierte Tabelle `events`, innerhalb einer einzigen Transaktion mit idempotenten Upserts. Der auditierte Reporting-Pfad liest ausschließlich aus der dauerhaften Tabelle. Stürzt der Server mitten im Ingest ab, verlieren wir höchstens einige Sekunden an Ereignissen in der Staging-Tabelle – und die vorgelagerten Produzenten wiederholen, sodass die dauerhafte Tabelle wieder zum korrekten Zustand konvergiert.

Dazu haben wir eine kleine, aber sorgfältige Betriebsschicht ergänzt: ein Grafana-Dashboard für die Füllrate von Stufe 1, einen harten Alarm, falls der Drainer zurückfällt, Partitionsrotation auf der dauerhaften Tabelle und ein schriftliches Runbook für die drei relevanten Fehlerfälle.

  • UNLOGGED-Staging-Tabelle ohne Indizes – gezielt für reinen Insert-Durchsatz entwickelt
  • Gebatchter COPY-Ingest aus einem Python-/FastAPI-Service statt zeilenweiser INSERTs
  • Celery-Drainer, der Mikro-Batches in die dauerhafte, vollständig indizierte Events-Tabelle überführt
  • Idempotente Upserts (ON CONFLICT DO NOTHING), damit Wiederholungen der Produzenten sicher sind
  • Monatliche Partitionierung der dauerhaften Tabelle, um Vacuum- und Indexarbeit zu begrenzen
  • Ende-zu-Ende-Dashboards und Alarme für Verzögerungen in Grafana / Datadog
  • Schriftliches Runbook für RDS-Failover, Drainer-Absturz und Back-Pressure der Produzenten
  • Audit-Freigabe des Dauerhaftigkeitsprofils vor dem Launch – keine Überraschungen

Wie wir es gebaut haben

  1. 01

    Den tatsächlichen Engpass analysieren

    Bevor wir das Schema anfassten, haben wir eine Woche lang pg_stat_statements, RDS Performance Insights und einen eigenen WAL-Durchsatz-Tracker laufen lassen. Die Daten waren eindeutig: WAL-Schreibvorgänge und Indexpflege auf der einen, überladenen Events-Tabelle machten 78 % der Schreibzeit aus. CPU und Arbeitsspeicher waren nicht das Problem – die Dauerhaftigkeit war es.

  2. 02

    Den Zwei-Tabellen-Vertrag entwerfen

    Wir haben ein kurzes Design-Dokument verfasst, das genau festhielt, was uns UNLOGGED brachte, was es uns kostete und welche Garantien die vorgelagerten Producer liefern mussten (At-least-once-Zustellung, idempotente Event-IDs). Der Vertrag machte das Verlustprofil explizit, sodass das Audit-Team vorab zustimmen konnte – „bei einem RDS-Failover können bis zu N Sekunden gestagter Events verloren gehen; die dauerhafte Tabelle ist nicht betroffen.“

  3. 03

    Gebündeltes COPY, Micro-Batch-Drainer

    Wir haben zeilenweise Inserts durch einen Python-Ingest-Service ersetzt, der Events bis zu 250 ms im Speicher puffert und sie per PostgreSQL COPY in die UNLOGGED-Staging-Tabelle schreibt. Ein Celery-Drainer übernimmt Micro-Batches mit 5.000 Zeilen in einer einzigen Transaktion in die dauerhafte Tabelle, mit ON CONFLICT DO NOTHING für Idempotenz.

  4. 04

    Betrieb, Partitionierung, Runbook

    Wir haben die dauerhafte Tabelle um monatliche Partitionierung ergänzt (damit Vacuum und Index-Rebuilds günstig bleiben), Grafana-Dashboards für die Ende-zu-Ende-Verzögerung, Alarme für den Füllstand des Staging und die Verzögerung des Drainers sowie ein schriftliches Runbook zu den drei realen Fehlerszenarien: Drainer-Absturz, RDS-Failover und Producer-Back-Pressure. Die Übergabe an das On-Call-Team des Kunden erfolgte mit Shadowing.

Tech-Stack

  • PostgreSQL 16
  • Python
  • FastAPI
  • Celery
  • Redis
  • AWS RDS
  • AWS S3
  • Grafana
  • Datadog
  • Backend-Performance-Engineering
  • Python & FastAPI
  • Cloud-Lösungen
  • Datenbankarchitektur
“Wir hatten erwartet, ein Quartal für die Migration zu einer dedizierten Zeitreihendatenbank aufzuwenden. Stattdessen hat UnlockLive uns gezeigt, wie Postgres die Aufgabe übernimmt – und unsere Auditoren haben das neue Design freigegeben, bevor wir es ausgeliefert haben.”
Leiter Dateninfrastruktur (Head of Data Engineering) · Analyseplattform (Name vertraulich)

Häufig gestellte Fragen

Sind PostgreSQL-UNLOGGED-Tabellen im Produktivbetrieb sicher?

Ja – für die richtige Aufgabe. UNLOGGED-Tabellen überspringen WAL-Schreibvorgänge, was einen 5- bis 10-fachen Roh-Insert-Durchsatz bringt, im Austausch gegen ein dokumentiertes Verlustprofil: Zeilen gehen bei einem Serverabsturz verloren und werden nicht repliziert. Sie eignen sich hervorragend für kurzlebige Staging-Daten, wenn ein vorgelagertes System Ereignisse erneut abspielen kann. Für alles, was Sie verlässlich zurücklesen müssen, sind sie ungeeignet. Wir koppeln sie stets mit einer dauerhaften, protokollierten Tabelle, aus der der geprüfte Pfad tatsächlich liest.

Warum nicht einfach eine spezialisierte Zeitreihendatenbank wie TimescaleDB oder ClickHouse verwenden?

Weil die Migrationskosten und die betriebliche Angriffsfläche real sind. Wenn Ihr Team PostgreSQL bereits gut betreibt und Ihr Durchsatzziel im Bereich von zehntausenden Ereignissen pro Sekunde liegt, bringt Sie das Muster aus UNLOGGED + gebatchtem COPY oft in Wochen statt Quartalen ans Ziel. Wir empfehlen eine dedizierte TSDB, wenn die Last tatsächlich spaltenorientierten Speicher, zeitpartitionierte Indizes oder Bereichsabfragen im Millisekundenbereich im großen Maßstab erfordert.

Kann ich einer PostgreSQL-UNLOGGED-Staging-Tabelle Indizes hinzufügen?

Sie können, sollten es aber fast nie. Der ganze Sinn der Staging-Ebene ist rohe Insert-Geschwindigkeit; jeder Index, den Sie hinzufügen, kostet Ingest-Durchsatz. Wir halten die Staging-Tabelle indexfrei und legen alle Indizes auf die nachgelagerte dauerhafte Tabelle, gegen die die Analyseabfragen tatsächlich laufen.

Wie garantieren Sie keinen Datenverlust, wenn UNLOGGED-Tabellen in einer Pipeline verwendet werden?

Zwei Regeln. Erstens: Die vorgelagerten Produzenten müssen At-least-once-Zustellung mit idempotenten Ereignis-IDs liefern, damit Wiederholungen sicher sind. Zweitens: Die dauerhafte Seite der Pipeline liest immer nur aus der vollständig protokollierten nachgelagerten Tabelle, nie aus der UNLOGGED-Staging-Tabelle. Im schlimmsten Fall gehen bei einem Absturz wenige Sekunden wiederholbarer Staging-Ereignisse verloren – nie ein fehlender Bericht.

Welchen Ingest-Durchsatz erreichen Sie mit diesem Muster auf einer einzelnen PostgreSQL-Instanz?

Das hängt von Zeilengröße, Netzwerk und Instanzklasse ab, aber auf einer bescheidenen AWS RDS db.r6g.xlarge sehen wir mit genau diesem Zwei-Tabellen-Muster regelmäßig 60.000-100.000 Ereignisse pro Sekunde im Dauerbetrieb bei einer p95-Insert-Latenz der Stufe 1 unter 50 ms. Darüber hinaus würden wir die Staging-Tabelle partitionieren oder mit einem Write-Router horizontal skalieren.

Möchten Sie ein solches Ergebnis?

Sprechen Sie mit demselben Team, das gebaut hat 10-facher Ingest-Durchsatz in einer PostgreSQL-Analytics-Pipeline mit UNLOGGED-Tabellen. Wir grenzen Ihr Projekt ein, erstellen ein Festpreisangebot und zeigen Ihnen das passendste Beispiel aus unserem Portfolio.

Strategiegespräch buchen