23 Punkte von GN⁺ 2026-01-23 | 4 Kommentare | Auf WhatsApp teilen
  • Für Datenwiederherstellung und Compliance wird häufig Soft Delete auf Basis einer archived_at-Spalte verwendet, doch mit der Zeit nehmen Komplexität und Ineffizienz zu
  • Dieser Ansatz macht Abfragen, Indizes, Migrationen und Restore-Logik komplexer, und da die meisten archivierten Daten nie wieder gelesen werden, entsteht unnötige Last für die Datenbank
  • Als Alternativen werden anwendungsereignisbasierte Archivierung, triggerbasierte Archivierung und WAL-(Change-Data-Capture)-basierte Archivierung vorgestellt
  • Die einzelnen Ansätze unterscheiden sich bei betrieblicher Komplexität, Infrastruktur-Anforderungen und Einfachheit der Wiederherstellung; insbesondere der WAL-basierte Ansatz erfordert Integration mit externen Systemen wie Kafka
  • Für neue Projekte ist der triggerbasierte Ansatz die am besten ausbalancierte Wahl in Bezug auf Einfachheit und Wartbarkeit

Probleme von Soft Deletes

  • Üblicherweise werden Daten logisch gelöscht, indem eine boolesche Spalte deleted oder eine Zeitstempelspalte archived_at verwendet wird
    • Daten können wiederhergestellt werden, wenn ein Kunde sie versehentlich gelöscht hat
    • In manchen Fällen ist eine Aufbewahrung aus regulatorischen oder Audit-Zwecken erforderlich
  • Eine archived_at-Spalte verursacht jedoch Komplexität in Abfragen, Betrieb und Anwendungscode
    • Die meisten archivierten Daten werden nie wieder gelesen
    • Durch Probleme im API-Verhalten oder Automatisierungs-Tools wie Terraform können sich Millionen unnötiger Zeilen ansammeln
  • Wenn keine Bereinigungsjobs für archivierte Daten eingerichtet sind, kommt es zu Leistungseinbußen bei Datenbank-Backups und -Restores
  • In Abfragen und Indizes müssen archivierte Daten herausgefiltert werden, und es besteht ein Risiko von Datenlecks
  • Bei Migrationen ist es schwierig, alte Daten zu verarbeiten oder Standardwerte zu ändern
  • Die Restore-Logik wird komplexer, und wenn Aufrufe externer Systeme nötig sind, können Bugs entstehen
  • Insgesamt wirkt der archived_at-Ansatz zwar einfach, verursacht aber langfristig hohe Wartungskosten

Archivierung auf Anwendungsebene

  • Beim Löschen wird ein Event veröffentlicht, das an SQS gesendet wird, damit ein anderer Service die Daten in S3 archiviert
  • Vorteile
    • Vereinfachung der primären Datenbank und des Anwendungscodes
    • Bereinigung externer Ressourcen kann asynchron erfolgen, was Leistung und Stabilität verbessert
    • Durch Serialisierung als JSON können Daten in einer anwendungsfreundlichen Struktur archiviert werden
  • Nachteile
    • Durch Bugs im Anwendungscode kann es zu Verlust archivierter Daten kommen
    • Betriebliche Infrastruktur-Komplexität durch Message Queues und Ähnliches nimmt zu
    • Für in S3 archivierte Daten werden Werkzeuge für Suche und Wiederherstellung benötigt

Triggerbasierte Archivierung

  • Ein Trigger vor dem Löschen kopiert die Zeile als JSON in eine separate Archive-Tabelle
    • Beispiel-Tabelle: archive(id, table_name, record_id, data, archived_at, caused_by_table, caused_by_id)
  • Bei Löschungen per Foreign-Key-Cascade werden zur Nachverfolgung der Löschursache Session-Variablen (archive.cause_table, archive.cause_id) verwendet
    • So lässt sich abfragen, welcher übergeordnete Datensatz untergeordnete Daten gelöscht hat
  • Vorteile
    • Live-Tabellen bleiben sauber, eine archived_at-Spalte ist nicht nötig
    • Die Bereinigung der Archivtabelle (WHERE archived_at < NOW() - INTERVAL '90 days') ist einfach
    • Effiziente Abfragen und Indizes bleiben erhalten, Migrationen werden vereinfacht
    • Geringere Backup-Größe
  • Die Archivtabelle kann über einen separaten Tablespace oder zeitbasierte Partitionierung verwaltet werden

WAL-(Change-Data-Capture)-basierte Archivierung

  • Die WAL-Logs von PostgreSQL werden gelesen, um Delete-Events in externe Systeme zu streamen
    • Typisches Tool: Debezium (integriert mit Kafka)
    • Beispielpfad: PostgreSQL → Debezium → Kafka → Consumer → Archive Storage
  • Leichtgewichtigere Alternativen
    • pgstream: sendet WAL direkt an Webhooks oder Message Queues
    • wal2json: gibt WAL als JSON aus
    • pg_recvlogical: integriertes Tool für logische Replikation in PostgreSQL
  • Betriebliche Komplexität
    • Kafka-basierte Setups erfordern Monitoring, Incident-Handling und Tuning
    • Wenn der Consumer verzögert ist, können sich WAL-Dateien ansammeln → Risiko von zu wenig Festplattenspeicher
    • Mit der Einstellung max_slot_wal_keep_size in PostgreSQL 13+ lässt sich das begrenzen
    • Monitoring und Alerts für Replikationsslot-Lag sind essenziell
  • Vorteile
    • Alle Änderungen lassen sich ohne Änderungen am Anwendungscode erfassen
    • Streaming zu verschiedenen Zielen (S3, Data Warehouse, Suchindex) ist möglich
    • Keine zusätzliche Last auf der primären Datenbank
  • Nachteile
    • Hohe betriebliche Komplexität und Infrastrukturkosten
    • Bei Consumer-Lag kann Datenverlust auftreten oder eine erneute Synchronisierung nötig werden
    • Bei Schemaänderungen ist Abstimmung zwischen Quelle und Consumer erforderlich

Idee eines Replikats, das Löschungen nicht verarbeitet

  • Es wird die Idee vorgeschlagen, ein PostgreSQL-Replikat zu betreiben, das DELETE-Abfragen ignoriert
    • So könnten alle nicht gelöschten Daten kumulativ archiviert werden
    • Archivierte Daten wären direkt abfragbar
  • Mögliche Probleme
    • Es könnte unmöglich sein, Löschinformationen eindeutig zu unterscheiden
    • Bei der Anwendung von Migrationen besteht Konfliktgefahr
    • Speicherbedarf und Betriebskosten steigen

Fazit

  • In neuen Projekten ist die triggerbasierte Archivierung die praktikabelste Wahl
    • Sie ist einfach einzurichten und hält Live-Tabellen sauber
    • Auch ohne separate Infrastruktur lassen sich Archivdaten leicht einsehen und verwalten
  • Wenn bereits komplexe Infrastruktur vorhanden ist oder Streaming zu mehreren Zielen benötigt wird, ist ein WAL-basierter Ansatz geeignet

4 Kommentare

 
love7peace 2026-01-23

Soweit ich gelernt habe, führt ein triggerbasierter Ansatz zu zusätzlicher Last auf der DB ...? Dass man Trigger empfiehlt, überrascht mich daher.

 
nemorize 2026-01-24

Wenn die Last, die durch Trigger in diesem Ausmaß entsteht, zum Problem wird, ist die Situation auch ohne Trigger bereits voller Probleme.

 
cherrycoder 2026-01-23

Regulierung ist eben immer mit Kosten verbunden. Aber letztlich müssen die Verbraucher sie ohnehin tragen.

 
GN⁺ 2026-01-23
Hacker-News-Kommentare
  • In der Bankendomäne, in der ich arbeite, hatte ich eher das Gefühl, dass Soft Delete vorteilhaft ist
    Wenn es eine Spalte deleted_at gibt, sind Abfragen klar formuliert, und auch Analyse-Queries oder Admin-Seiten können mit demselben Datensatz arbeiten
    Löschungen sind in den meisten Fällen selten, und es kam fast nie vor, dass per Soft Delete gelöschte Zeilen Performance-Probleme verursacht haben
    Außerdem bleiben Beziehungen unverändert erhalten, wodurch auch die Wiederherstellung (Undo) einfach ist
    Ich gehe sogar noch weiter und bevorzuge es, Zeilen vollständig unveränderlich (immutable) zu machen und bei Updates neue Zeilen hinzuzufügen
    Wenn man Logs hinterlassen will, halte ich einen Ansatz für gut, bei dem man per DB-Trigger bei INSERT/UPDATE/DELETE Einträge in eine Replikat-Tabelle schreibt

    • Da hast du recht, aber ich denke, das gilt nur dann, wenn Löschungen selten sind
      In Tabellen, die ich gesehen habe und bei denen 50–70 % per Soft Delete gelöscht waren, gab es eindeutig Performance-Einbußen
      Letztlich hängt Soft Delete vom Kontext ab, und eine vorherige Analyse ist nötig
    • In Postgres kann man auch einstellen, dass nur nicht per Soft Delete gelöschte Daten indiziert werden
      In den meisten Fällen ist das nicht nötig, kann aber beim RAM-Sparen helfen
    • In Banken ist Soft Delete nur eine Zwischenlösung, die mangelnde Auditierbarkeit (auditability) kaschiert
      Die eigentliche Lösung ist Event Sourcing, bei dem alle Änderungen als Events aufgezeichnet werden müssen
      Die Performance ist schlechter, lässt sich aber mit Snapshots und Synchronisation (Sync) kompensieren
    • Wenn man eine DB mit unveränderlicher Struktur betreiben will, kann man ein System wie Datomic in Betracht ziehen
      Mit der Time-Travel-Funktion lässt sich der frühere Zustand vollständig abfragen
    • Als ich früher bei einem Versicherer gearbeitet habe, wurden auch dort alle Tabellen als Append-only-Log betrieben
      Der aktuelle Zustand lag in der Zeile mit dem größten Zeitstempel, und vergangene Zustände konnten per Filter abgefragt werden
      Dieser Ansatz ermöglicht eine starke Verlaufskontrolle
  • Die größte Falle bei Soft Delete ist die Komplexität der Abfragen
    Anfangs denkt man, man müsse nur WHERE deleted_at IS NULL hinzufügen, aber nach ein paar Monaten tauchen wegen vergessener Filter Geisterdaten in Reports auf
    Man kann das mit Views lösen, aber am Ende muss man trotzdem parallele Zugriffsmuster beibehalten, und wenn gelöschte Daten abgefragt werden sollen, muss die Abstraktion umgangen werden
    Event Sourcing ist sauberer, bringt aber eine hohe Betriebslast mit sich, weshalb die meisten einen hybriden Ansatz wählen

    • Views sind ein ausreichend mächtiges Werkzeug
      Das Problem ist, dass viele SWE- und BI-Ingenieure mit SQL und Schemadesign nicht vertraut sind
      Ein noch häufigeres Problem als Soft Delete ist der Umgang mit Type 2 Slowly Changing Dimension
      Die meisten erstellen unnötigerweise Audit-Tabellen und wiederholen ineffiziente UPDATEs/INSERTs
      Datenbanken sind eigentlich wunderschöne Systeme, aber es ist schade, dass sie so wenig Respekt bekommen
  • Ich fände es gut, wenn Soft Delete als eingebaute DB-Funktion bereitgestellt würde
    Ideal wäre, wenn man es auf Tabellenebene aktivieren und eine Strategie für die Löschbehandlung wählen könnte

    • Tatsächlich bieten Systeme wie Iceberg, Delta Lake, BigQuery bereits Time Travel
      Viele Teams implementieren es wegen individueller Anforderungen am Ende aber doch mit SCD (Slowly Changing Dimension)
  • Meiner Erfahrung nach war ein triggerbasierter Ansatz am stabilsten
    Archivtabellen sollten append-only bleiben, und die Wiederherstellung sollte in der Anwendungsschicht behandelt werden
    Updates werden als Soft Delete behandelt, und der Trigger erfasst den vorherigen Zustand
    Trigger müssen unbedingt im BEFORE-Zeitpunkt ausgeführt werden, und die Logik sollte einfach bleiben
    Partitionen werden typischerweise monatlich angelegt, bei hoher Schreiblast sollte man sie täglich aufteilen

  • Ich wünsche mir, dass sich DBs von stateful → stateless weiterentwickeln
    Ich bevorzuge eine Struktur, bei der alle Änderungen als Append-only-Events aufgezeichnet und die benötigten Daten über Views dargestellt werden
    Ideal wäre, wenn die DB materialisierte Indizes automatisch verwalten würde
    Einige moderne DBs bieten so etwas bereits, aber bisher fehlt noch Entwicklung speziell für OLTP

  • In einem früheren Unternehmen, in dem ich gearbeitet habe, wurde Soft Delete in allen Systemen eingesetzt
    Ich erinnere mich auch daran, dass ein Professor sagte: „In der Geschäftswelt werden Daten niemals gelöscht

    • Vollständiges Löschen bedeutet, die eigenen zukünftigen Fähigkeiten zur Datenanalyse selbst einzuschränken
      Speicher ist billig, deshalb sollte man Daten niemals löschen
    • Interessant ist allerdings, dass der Professor nichts über Änderungen gesagt hat
  • Datenbanken sind Orte, an denen Fakten gespeichert werden
    Das Erstellen eines Datensatzes ist ein neuer Fakt, und das Löschen ist ein weiterer Fakt
    Wenn man eine Zeile aber physisch löscht, verschwindet der Fakt
    In den meisten Fällen ist eine solche Löschung nicht wünschenswert

    • Wenn Daten allerdings ein Gut mit Abflussrisiko sind, kann im Gegenteil sogar eine Massenlöschung nötig sein
      Man muss Aufbewahrungskosten und Sicherheitsrisiken berücksichtigen
    • Wenn eine DB nicht unveränderlich ist, erzeugen Änderungen selbst bereits verschwundene Fakten
      Die Entscheidung, Daten dauerhaft aufzubewahren, sollte sorgfältig getroffen werden
    • Persönlich denke ich, dass ein Datenspeicher nur zwei Operationen unterstützen sollte: Lesen und Einfügen
      Dafür ist es wichtig, den Lebenszyklus der Daten zu verstehen
  • Bei Firezone wurde Soft Delete anfangs für Audit-Logs verwendet, dann aber wegen Migrationsproblemen aufgegeben
    Stattdessen wurde auf einen Ansatz umgestellt, bei dem mit Postgres CDC (Change Data Capture) Events in eine separate schreiboptimierte Tabelle ausgelagert werden
    Ich denke, Soft Delete ist für Benutzer-Wiederherstellungsfunktionen nützlich, aber für Audit oder Compliance ungeeignet

    • In einfachen Projekten ist es effizienter, statt DB-Änderungen gleich die API-Aufrufe selbst zu auditieren
  • Es ist sauber, über Tabellen mit Soft-Delete-Feld eine View zu legen, die gelöschte Zeilen ausblendet
    So muss sich die Anwendung nicht um den Löschstatus kümmern

    • Mit RLS (Row Level Security) in Postgres kann man per Soft Delete gelöschte Zeilen automatisch ausblenden
      Die Anwendung liest, schreibt und löscht weiterhin auf derselben Tabelle
  • Es gibt die Frage, wie man mit Schema Drift umgeht
    Wenn man Daten, die beim Löschen im damaligen Schema serialisiert wurden, später wiederherstellen will, werden Schemaänderungen zum Problem

    • Meiner Erfahrung nach wird auf archivierte Objekte fast nie zugegriffen
      Meist werden sie nur innerhalb weniger Tage nach dem Löschen wiederhergestellt, sodass Schemaänderungen wenig Einfluss haben
      Alte Archive auf ein neues Modell zu migrieren, war eine komplexe und fehleranfällige Aufgabe
      Letztlich hängt die passende Strategie davon ab, wie das System genutzt wird