Quote- und Sales-Tabellen-Archivierung fuer grosse Magento-Shops
AI generated
InnoDB
SQL
MySQL · Magento · Datenbankwartung · OLTP
Quote- und Sales-Tabellen-Archivierung
schlanke OLTP-Tabellen fuer grosse Magento-Shops

Ohne konsequente Tabellen-Archivierung sammeln sich in quote und sales_order Millionen verwaister Warenkorb-Zeilen und Jahre alter Bestellhistorie an, die jede Abfrage verlangsamen und Backups aufblaehen. Eine durchdachte Archivierungsstrategie trennt aktive Transaktionsdaten von historischen Datenbestaenden, ohne referenzielle Integritaet oder Compliance-Anforderungen zu gefaehrden.

16 Min. Lesezeit Tabellen-Archivierung · quote Cleanup · sales_order · Partitionierung Magento 2.4.x · MySQL 8.0 · Percona Server

1. Warum quote und sales_order ungebremst wachsen

Jeder Besucher, der einen Artikel in den Warenkorb legt, ohne die Bestellung abzuschliessen, hinterlaesst eine Zeile in quote und zugehoerige Zeilen in quote_item und quote_address. Ohne aktive Tabellen-Archivierung bleiben diese verwaisten Datensaetze fuer immer in der Datenbank, weil Magento selbst keinen aggressiven automatischen Loeschprozess mitbringt. Bei einem Shop mit hoher Abbruchrate im Checkout summiert sich das schnell auf mehrere Millionen ungenutzter Zeilen pro Jahr.

Parallel dazu waechst sales_order mit jeder abgeschlossenen Bestellung unaufhoerlich, und anders als bei quote ist hier ein Loeschen aus rechtlichen Aufbewahrungspflichten meist nicht ohne Weiteres moeglich. Genau das macht Tabellen-Archivierung im Sales-Kontext zu einer anderen Aufgabe als im Quote-Kontext: Wo Warenkoerbe geloescht werden duerfen, muessen Bestelldaten typischerweise nur aus der aktiven OLTP-Tabelle in ein Archiv verschoben werden, ohne dass die Information selbst verloren geht.

Der Effekt auf die Performance ist real messbar: Eine sales_order-Tabelle mit fuenfzig Millionen Zeilen, von denen nur zwei Prozent aus den letzten neunzig Tagen stammen, zwingt jeden Index-Scan, deutlich mehr Datenseiten zu lesen als noetig. Der InnoDB Buffer Pool kann historische, selten gelesene Daten nicht effizient cachen, wenn diese physisch zwischen aktiven Zeilen verteilt liegen. Eine gezielte Tabellen-Archivierung reduziert genau diesen Effekt, indem sie die aktive Arbeitsmenge klein und cache-freundlich haelt.

2. Abandoned-Cart-Bereinigung: quote sicher loeschen

Bevor eine Quote-Zeile geloescht wird, muss klar definiert sein, was als "abgebrochen" gilt. Ein gaengiges Kriterium: Quotes ohne zugehoerige Bestellung, deren updated_at aelter als neunzig Tage ist und die nicht zu einem eingeloggten Kunden mit aktivem Merklisten-Bezug gehoeren. Diese Kriterien sollten projektspezifisch abgestimmt werden, bevor die erste Tabellen-Archivierung produktiv laeuft, weil ein zu aggressives Loeschen aktive Warenkoerbe wiederkehrender Kunden vernichten kann.


-- Kandidaten fuer die Abandoned-Cart-Bereinigung identifizieren
SELECT q.entity_id, q.customer_email, q.updated_at, q.items_count
FROM quote q
LEFT JOIN sales_order so ON so.quote_id = q.entity_id
WHERE so.entity_id IS NULL
  AND q.is_active = 1
  AND q.updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
  AND q.customer_is_guest = 1
LIMIT 5000;

-- Abhaengige quote_item und quote_address Zeilen vor dem Hauptloeschen entfernen
DELETE qi FROM quote_item qi
INNER JOIN quote q ON q.entity_id = qi.quote_id
LEFT JOIN sales_order so ON so.quote_id = q.entity_id
WHERE so.entity_id IS NULL
  AND q.updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
  AND q.customer_is_guest = 1
LIMIT 5000;

Ein haeufiger Fehler bei der ersten Tabellen-Archivierung fuer Quotes: Man vergisst, dass quote_item_option und quote_payment ebenfalls Fremdschluessel auf die quote-Tabelle halten. Wer nur die Haupttabelle bereinigt, hinterlaesst verwaiste Zeilen in den Nebentabellen, die spaeter selbst wieder zum Wachstumsproblem werden. Eine vollstaendige Bereinigung muss alle abhaengigen Tabellen in der richtigen Reihenfolge durchlaufen, von den Blatttabellen zur Haupttabelle.

3. bin/magento cron:run und die eingebaute Quote-Cleanup-Funktion

Magento bringt mit sales_clean_quotes einen eingebauten Cron-Job mit, der ueber die Konfiguration unter Sales > Sales > Quotes im Backend gesteuert wird und Quotes nach einer definierbaren Anzahl an Tagen automatisch loescht. In der Praxis reicht dieser Standardmechanismus fuer kleine bis mittlere Shops oft aus, stoesst aber bei sehr grossen Datenmengen an Grenzen, weil der Cron-Job ohne explizites Batching arbeitet und bei mehreren Millionen zu loeschenden Zeilen lange Transaktionen erzeugt.

Fuer eine robuste Tabellen-Archivierung in grossen Shops empfiehlt sich, den Standard-Cron-Job zu deaktivieren und stattdessen ein eigenes, batch-basiertes Cleanup-Skript zu implementieren, das ueber bin/magento cron:run --group="clean_quote" hinaus explizite Kontrolle ueber Chunk-Groesse und Pausen zwischen den Batches erlaubt. Das verhindert, dass ein einzelner Cron-Lauf die Datenbank fuer mehrere Minuten mit einer einzigen riesigen Transaktion blockiert.


# Konfigurierbare Aufbewahrungsdauer fuer den eingebauten Quote-Cleanup pruefen
bin/magento config:show sales/orders/delete_quote_after

# Standard-Cron-Gruppe fuer Quote-Cleanup isoliert testen
bin/magento cron:run --group="clean_quote"

# Eigenes batch-basiertes Cleanup-Skript anstelle des Standardjobs einplanen
# (Crontab-Eintrag, ruft ein PHP-Skript mit expliziter Chunk-Steuerung auf)
*/30 * * * * /usr/bin/php /var/www/magento/bin/quote-cleanup.php --chunk-size=1000 --sleep=2

4. sales_order Archivierung: Kalt-Daten sicher auslagern

Anders als bei Quotes ist bei sales_order das Ziel selten das endgueltige Loeschen, sondern das kontrollierte Verschieben in eine separate Archivtabelle oder Archivdatenbank. Der etablierte Ansatz: Eine Tabelle sales_order_archive mit identischer Struktur wird angelegt, Bestellungen aelter als beispielsweise drei Jahre werden batch-weise dorthin kopiert und erst nach erfolgreicher Verifikation aus der aktiven Tabelle entfernt.


-- Archivtabelle mit identischer Struktur anlegen
CREATE TABLE sales_order_archive LIKE sales_order;
CREATE TABLE sales_order_item_archive LIKE sales_order_item;

-- Batch-weises Kopieren alter Bestellungen ins Archiv (Beispiel-Batch)
INSERT INTO sales_order_archive
SELECT * FROM sales_order
WHERE created_at < DATE_SUB(NOW(), INTERVAL 3 YEAR)
  AND status IN ('complete', 'closed', 'canceled')
ORDER BY entity_id
LIMIT 2000;

-- Verifikation: Zeilenzahl im Archiv gegen Quelle pruefen, bevor geloescht wird
SELECT
    (SELECT COUNT(*) FROM sales_order WHERE created_at < DATE_SUB(NOW(), INTERVAL 3 YEAR)) AS source_count,
    (SELECT COUNT(*) FROM sales_order_archive) AS archive_count;

Fuer den Zugriff auf archivierte Bestellungen im Kundenkonto oder im Backend braucht es entweder eine dedizierte Read-Only-Ansicht auf beide Tabellen per UNION, oder einen expliziten Wechsel des Reporting-Zeitraums, der Nutzer informiert, dass aeltere Bestellungen separat abgerufen werden. Diese Tabellen-Archivierung bewahrt die vollstaendige Historie, entlastet aber die produktive sales_order-Tabelle spuerbar, weil Indizes kleiner werden und mehr davon in den Buffer Pool passt.

5. Partitionierung als Alternative zur Tabellen-Archivierung

Statt Daten physisch in eine andere Tabelle zu verschieben, kann sales_order mit MySQL Range-Partitionierung nach created_at in mehrere logische Partitionen aufgeteilt werden, wobei die Tabelle fuer Abfragen weiterhin als Einheit erscheint. Der Vorteil gegenueber klassischer Tabellen-Archivierung: Kein zusaetzlicher ETL-Prozess noetig, Magento selbst muss nicht angepasst werden, weil die Partitionierung fuer die Anwendungsschicht transparent bleibt.

Der Nachteil: Partitionierung allein reduziert weder die Gesamtdatenmenge noch die Backup-Groesse, sie verbessert primaer die Performance von Abfragen, die sich auf eine bestimmte Partition eingrenzen lassen, etwa "alle Bestellungen der letzten dreissig Tage". Fuer Shops, die vor allem Query-Performance auf aktuelle Daten optimieren wollen, ohne alte Daten aus dem direkten Zugriff zu entfernen, ist Partitionierung oft die pragmatischere Ergaenzung zur eigentlichen Tabellen-Archivierung, nicht deren Ersatz.

6. Fremdschluessel und referenzielle Integritaet beim Loeschen

sales_order ist Ziel zahlreicher Fremdschluessel aus Tabellen wie sales_order_item, sales_order_payment, sales_order_address, sales_invoice, sales_shipment und sales_creditmemo. Jede Tabellen-Archivierung muss diese Abhaengigkeitskette vollstaendig abbilden, sonst entstehen entweder verwaiste Zeilen in den Kindtabellen oder Fremdschluesselverletzungen, die den gesamten Loeschprozess abbrechen lassen.

Ein bewaehrtes Muster: Vor jedem produktiven Loesch- oder Verschiebelauf wird zunaechst eine Trockenlauf-Abfrage ausgefuehrt, die alle betroffenen Kindtabellen auflistet und deren Zeilenzahl fuer die zu archivierenden Order-IDs zaehlt. Erst wenn diese Zahlen mit den Erwartungen uebereinstimmen, startet der eigentliche Archivierungslauf. Fuer die Reihenfolge gilt grundsaetzlich: von den am weitesten entfernten Blatttabellen zur Haupttabelle sales_order, niemals umgekehrt.

7. Batch-Deletes ohne Replikationslag und Lock-Eskalation

Ein einzelnes DELETE ohne LIMIT auf mehreren Millionen Zeilen sperrt in InnoDB potenziell sehr viele Zeilen gleichzeitig und erzeugt auf Replikas einen erheblichen Replikationslag, weil die Aenderung dort seriell nachvollzogen werden muss. Die etablierte Loesung fuer jede grossflaechige Tabellen-Archivierung ist ein Batch-Loop mit kleinen Chunks und kurzen Pausen zwischen den Batches, die dem Replikationsprozess Zeit zum Aufholen geben.


-- Batch-Delete-Loop-Muster (als Pseudocode-Kommentar, real in PHP oder Bash gesteuert)
-- Wiederholt ausfuehren, bis affected_rows = 0
DELETE FROM quote
WHERE is_active = 1
  AND updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
  AND entity_id IN (
    SELECT entity_id FROM (
      SELECT entity_id FROM quote
      WHERE is_active = 1
        AND updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
      LIMIT 1000
    ) AS batch
  );

-- Zwischen Batches: Replikationslag pruefen, bevor der naechste Batch startet
SHOW REPLICA STATUS\G
-- Bei Seconds_Behind_Source > 5: Batch-Loop kurz pausieren

Die Chunk-Groesse von tausend Zeilen ist ein bewaehrter Startwert fuer eine sichere Tabellen-Archivierung, sollte aber anhand der beobachteten Lock-Wartezeiten und des Replikationslags feinjustiert werden. Ein zusaetzlicher Sicherheitsmechanismus ist das Pruefen von Threads_running vor jedem Batch: Steigt dieser Wert ueber einen definierten Schwellenwert, sollte der Batch-Prozess automatisch pausieren, aehnlich der Drosselungslogik von Online-Schema-Change-Tools.

8. Backup- und Compliance-Anforderungen bei Bestelldaten

Bestelldaten unterliegen in vielen Laendern gesetzlichen Aufbewahrungspflichten von sechs bis zehn Jahren, was eine physische Loeschung im engeren Sinne meist ausschliesst. Jede Tabellen-Archivierung fuer sales_order muss deshalb sicherstellen, dass archivierte Daten weiterhin vollstaendig und wiederherstellbar bleiben, auch wenn sie aus der aktiven OLTP-Tabelle entfernt wurden.

Ein separates Backup-Regime fuer Archivtabellen ist sinnvoll: Waehrend die aktive sales_order-Tabelle taeglich vollstaendig gesichert wird, reicht fuer die selten geaenderte Archivtabelle ein monatlicher Vollbackup-Zyklus mit inkrementellen Sicherungen dazwischen. Das reduziert die Backup-Fenster und den Speicherbedarf spuerbar, ohne die Wiederherstellbarkeit der historischen Daten zu gefaehrden. Vor jeder produktiven Tabellen-Archivierung sollte zudem ein dokumentierter Restore-Test der Archivdaten erfolgen, um im Ernstfall die Wiederherstellungszeit realistisch einschaetzen zu koennen.

9. Monitoring: Tabellengroesse, Fragmentierung, OPTIMIZE TABLE

Nach jeder groesseren Tabellen-Archivierung bleibt physischer Speicherplatz in Form von Fragmentierung zurueck, weil InnoDB geloeschte Seiten nicht automatisch an das Betriebssystem zurueckgibt. Der freie Platz innerhalb der Tabelle wird zwar fuer neue Zeilen wiederverwendet, die Datei selbst schrumpft aber nicht von allein.


-- Fragmentierung nach einer grossen Tabellen-Archivierung pruefen
SELECT
    table_name,
    ROUND(data_length / 1024 / 1024, 1) AS data_mb,
    ROUND(data_free / 1024 / 1024, 1) AS free_mb,
    ROUND(data_free / NULLIF(data_length, 0) * 100, 1) AS fragmentation_pct
FROM information_schema.tables
WHERE table_schema = 'magento_prod'
  AND table_name IN ('quote', 'sales_order', 'sales_order_item')
ORDER BY fragmentation_pct DESC;

-- Tabelle nach grosser Archivierung defragmentieren (Online-DDL, aber I/O-intensiv)
OPTIMIZE TABLE sales_order;

OPTIMIZE TABLE baut die Tabelle intern neu auf und gibt dabei freien Speicherplatz an das Betriebssystem zurueck, ist aber selbst eine I/O-intensive Operation, die auf grossen Tabellen wieder wie ein Online-Schema-Change-Tool behandelt werden sollte, am besten ausserhalb der Stosszeiten. Nach jeder groesseren Tabellen-Archivierung gehoert die Fragmentierungspruefung in den regulaeren Wartungsplan, nicht nur als einmalige Reaktion auf spuerbare Performance-Probleme.

Datenkategorie Loeschen erlaubt? Empfohlene Strategie
Abandoned quote (Gast, >90 Tage) Ja Batch-Delete via Cron oder eigenes Skript
Aktive quote (eingeloggt) Nein Ausschliessen, laengere Aufbewahrung
sales_order (> 3 Jahre) Nur mit Archivierung In Archivtabelle verschieben
sales_order (< gesetzliche Frist) Nein Aktive Tabelle oder Partitionierung
sales_order_grid (Anzeige-Cache) Ja, regenerierbar Synchron zu sales_order archivieren

10. Zusammenfassung

Eine wirksame Tabellen-Archivierung unterscheidet klar zwischen loeschbaren Daten wie verwaisten Quotes und aufbewahrungspflichtigen Daten wie abgeschlossenen Bestellungen. Abandoned-Cart-Bereinigung kann aggressiv per Batch-Delete erfolgen, sobald klare Kriterien definiert sind. Fuer sales_order ist Verschieben in eine Archivtabelle statt Loeschen die richtige Strategie, ergaenzt durch Partitionierung fuer bessere Query-Performance auf aktuellen Daten.

Technisch entscheidend sind Batch-Groessen, die Lock-Eskalation vermeiden, sorgfaeltiges Handling der Fremdschluessel-Kette in der richtigen Reihenfolge und regelmaessige OPTIMIZE TABLE-Laeufe nach grossen Archivierungsaktionen. Wer diese Bausteine kombiniert, haelt die produktiven OLTP-Tabellen schlank, ohne die referenzielle Integritaet oder gesetzliche Aufbewahrungspflichten zu verletzen.

Quote- und Sales-Tabellen-Archivierung, das Wichtigste auf einen Blick

Quote-Cleanup

Klare Kriterien fuer abandoned carts definieren, alle abhaengigen Tabellen in der richtigen Reihenfolge bereinigen.

sales_order archivieren statt loeschen

Kalte Bestelldaten in Archivtabellen verschieben, gesetzliche Aufbewahrungspflichten bleiben gewahrt.

Batch statt Big-Bang

Kleine Chunks mit Pausen vermeiden Replikationslag und Lock-Eskalation bei jeder Tabellen-Archivierung.

Nach der Archivierung optimieren

OPTIMIZE TABLE gibt Speicherplatz frei und reduziert Fragmentierung nach grossen Loesch- oder Verschiebelaeufen.

11. FAQ: Quote- und Sales-Tabellen-Archivierung

1Was ist Tabellen-Archivierung?
Trennt aktive Transaktionsdaten von historischen Daten, entweder durch Loeschen oder durch Verschieben ins Archiv.
2Darf ich alte quote-Zeilen loeschen?
Nach klarer Definition von abgebrochen, aktive Warenkoerbe eingeloggter Kunden sollten laenger aufbewahrt werden.
3Kann ich sales_order loeschen?
In der Regel nicht wegen Aufbewahrungspflichten. Verschieben in eine Archivtabelle ist die richtige Strategie.
4Reicht der eingebaute Cron-Job?
Fuer kleine Shops ja, bei sehr grossen Datenmengen ist ein eigenes batch-basiertes Skript robuster.
5Archivierung vs. Partitionierung?
Archivierung reduziert die Datenmenge physisch, Partitionierung teilt logisch auf ohne Mengenreduktion.
6Wie Replikationslag vermeiden?
Batch-Deletes mit kleinen Chunks und Pausen, Replikationslag zwischen Batches kontinuierlich pruefen.
7Welche Tabellen haengen ab?
sales_order_item, sales_order_payment, sales_invoice, sales_shipment, sales_creditmemo und weitere.
8Zugriff auf archivierte Bestellungen?
Ueber eine Read-Only UNION-Ansicht oder expliziten Zeitraumwechsel im Reporting.
9Was macht OPTIMIZE TABLE?
Baut die Tabelle neu auf, reduziert Fragmentierung, gibt Speicherplatz frei, ist I/O-intensiv.
10Backup vor Archivierung noetig?
Ja, unbedingt, plus regelmaessige Restore-Tests der Archivdaten zur Sicherstellung der Wiederherstellbarkeit.