von verwaisten Warenkörben bis zur Wachstums-Routine
Magento-Datenbanken wachsen selten durch Produktdaten, sondern durch Logs, verwaiste Warenkörbe und URL-Rewrites, die niemand aktiv löscht. Wer die üblichen Verdächtigen kennt, Tabellengrößen regelmäßig misst und eine wiederholbare Bereinigungsroutine etabliert, verhindert, dass Backups, Deployments und Reindex-Läufe unter dem Datenbank-Wachstum leiden.
Inhaltsverzeichnis
- 1. Warum Magento-Datenbanken ungebremst wachsen
- 2. Die üblichen Verdächtigen: Logs, Quotes, URL-Rewrites
- 3. quote und quote_item: verwaiste Warenkörbe
- 4. url_rewrite-Wachstum durch Kategorie- und Produktkombinationen
- 5. Monitoring-Routine mit information_schema
- 6. Wachstumstrend über Zeit protokollieren
- 7. Automatisierte Bereinigungsroutine per Cron
- 8. Partitionierung und Archivierung bei großen Shops
- 9. Kapazitätsplanung: DB-Größe, Backup und Performance
- 10. Zusammenfassung
- 11. FAQ
1. Warum Magento-Datenbanken ungebremst wachsen
Das Datenbank-Wachstum in einem Magento-Shop folgt selten dem Produktkatalog. Ein Shop mit 10.000 Produkten kann trotzdem eine 40-Gigabyte-Datenbank haben, weil transaktionale und protokollierende Tabellen ohne aktives Zutun ständig neue Zeilen anhäufen. Jeder Warenkorb, jede Besucher-Session, jede generierte URL und jeder abgeschlossene Cron-Job hinterlässt Spuren in der Datenbank, und Magento löscht diese Spuren standardmäßig nur teilweise automatisch.
Das Problem beim Datenbank-Wachstum ist selten technischer Natur, es ist ein Betriebsversäumnis: Die eingebauten Cleanup-Mechanismen existieren, sind aber häufig nicht aktiviert oder falsch konfiguriert. Ein Shop, der seit fünf Jahren im Betrieb ist und nie eine Bereinigungsroutine hatte, akkumuliert Millionen Zeilen in Tabellen, die für den eigentlichen Geschäftsbetrieb irrelevant sind. Das Datenbank-Wachstum wirkt sich dabei nicht nur auf den Speicherplatz aus, sondern verlangsamt Backups, Reindex-Läufe und sogar einfache Admin-Abfragen, weil MySQL bei jeder Query größere Indizes durchsuchen muss.
Der erste Schritt gegen unkontrolliertes Datenbank-Wachstum ist, die Tabellen zu identifizieren, die strukturell zum Wachstum neigen, und sie von den Tabellen zu unterscheiden, die proportional zum echten Geschäftsvolumen wachsen. Nur Erstere sind Kandidaten für aktives Bereinigen, Letztere (wie sales_order) sollten aus rechtlichen und betriebswirtschaftlichen Gründen erhalten bleiben.
2. Die üblichen Verdächtigen: Logs, Quotes, URL-Rewrites
Vier Tabellengruppen sind für den Großteil des ungeplanten Datenbank-Wachstums in Magento-Shops verantwortlich. Erstens Log- und Report-Tabellen wie report_event, report_viewed_product_index und customer_visitor, die bei jedem Seitenaufruf oder jeder Kundeninteraktion neue Zeilen schreiben, ohne dass ein Standard-Cleanup automatisch greift, wenn es nicht explizit aktiviert wurde. Zweitens die Quote-Tabellen (quote, quote_item, quote_address), die für jeden Warenkorb, auch für jeden nie abgeschlossenen Gast-Warenkorb, Zeilen anlegen und standardmäßig nie automatisch gelöscht werden.
Drittens url_rewrite, das für jede Kombination aus Produkt, Kategorie und Store-View einen eigenen Eintrag benötigt und bei aktivierten SEO-Funktionen wie kategoriebasierten Produkt-URLs multiplikativ wächst. Viertens die Cron- und Session-bezogenen Tabellen wie cron_schedule und in älteren Setups die datenbankbasierte Session-Speicherung, die bei hohem Traffic in kurzer Zeit Millionen Zeilen erzeugen kann. Alle vier Gruppen haben gemeinsam, dass ihr Datenbank-Wachstum proportional zum Traffic ist, nicht zum Produktkatalog, weshalb ein wachsender Shop mit stabilem Sortiment trotzdem ein beschleunigtes Wachstum in genau diesen Tabellen zeigt.
-- Quick overview: the usual suspects for uncontrolled database growth
SELECT table_name,
table_rows,
ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb
FROM information_schema.TABLES
WHERE table_schema = 'magento'
AND table_name IN (
'report_event', 'report_viewed_product_index', 'customer_visitor',
'quote', 'quote_item', 'url_rewrite', 'cron_schedule',
'catalog_compare_item', 'sales_order_status_history'
)
ORDER BY size_mb DESC;
3. quote und quote_item: verwaiste Warenkörbe
Jeder Besuch eines Online-Shops, bei dem ein Produkt in den Warenkorb gelegt wird, erzeugt eine Zeile in quote mit zugehörigen Zeilen in quote_item und quote_address. Bricht der Besucher den Kaufprozess ab, was bei Gästen die absolute Mehrheit der Fälle ist, bleibt dieser Warenkorb als verwaiste Zeile in der Datenbank bestehen. Magento löscht abgelaufene Quotes standardmäßig nicht automatisch, es sei denn, der Cron-Job für Quote-Bereinigung ist explizit konfiguriert und aktiv.
Bei einem Shop mit hohem Traffic und niedriger Konversionsrate, was für die meisten E-Commerce-Shops typisch ist, kann die quote-Tabelle innerhalb weniger Monate mehrere Millionen Zeilen erreichen, während nur ein Bruchteil davon tatsächlich zu einer Bestellung führt. Dieses Datenbank-Wachstum ist besonders tückisch, weil quote_item_option und quote_address für jede einzelne Quote weitere Zeilen anlegen, sodass ein Warenkorb mit fünf Artikeln und Konfigurationsoptionen leicht zwanzig oder mehr Zeilen über mehrere Tabellen hinweg erzeugt.
-- Find abandoned quotes older than 90 days that never converted to an order
SELECT COUNT(*) AS abandoned_quotes
FROM quote
WHERE is_active = 0
AND updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
AND entity_id NOT IN (SELECT quote_id FROM sales_order);
-- Estimate the row multiplier: quote_item + quote_address per quote
SELECT
(SELECT COUNT(*) FROM quote) AS quotes,
(SELECT COUNT(*) FROM quote_item) AS quote_items,
(SELECT COUNT(*) FROM quote_address) AS quote_addresses;
4. url_rewrite-Wachstum durch Kategorie- und Produktkombinationen
Die Tabelle url_rewrite wächst nicht linear mit der Produktanzahl, sondern multiplikativ mit der Anzahl der Kategorien, in denen ein Produkt sichtbar ist, kombiniert mit der Anzahl der Store-Views. Ist "Use Categories Path for Product URLs" aktiviert, erzeugt Magento für jedes Produkt in jeder zugeordneten Kategorie eine eigene URL, was bei Produkten mit mehreren Kategoriezuordnungen zu einem deutlich überproportionalen Datenbank-Wachstum in url_rewrite führt.
Hinzu kommt, dass alte, nicht mehr aktive Redirects (redirect_type ungleich 0) bei jeder URL-Änderung eines Produkts oder einer Kategorie zusätzlich in der Tabelle verbleiben, um SEO-Wert zu erhalten. Über Jahre akkumulieren sich so tausende Redirect-Einträge, von denen viele nie mehr aufgerufen werden. Ein Shop mit 50.000 Produkten, drei Store-Views und durchschnittlich zwei Kategoriezuordnungen pro Produkt kann allein dadurch 300.000 Basis-Einträge plus einen unbekannten Anteil an historischen Redirects in url_rewrite ansammeln.
-- Breakdown of url_rewrite by entity type and store, to see where growth concentrates
SELECT entity_type, store_id, COUNT(*) AS rows_count
FROM url_rewrite
GROUP BY entity_type, store_id
ORDER BY rows_count DESC;
-- Count stale redirects that are no longer referenced by any current entity URL
SELECT COUNT(*) AS old_redirects
FROM url_rewrite
WHERE redirect_type <> 0
AND is_autogenerated = 0;
5. Monitoring-Routine mit information_schema
Ohne regelmäßige Messung bleibt Datenbank-Wachstum unsichtbar, bis es zum akuten Problem wird, meist wenn ein Backup fehlschlägt oder ein Deployment-Fenster nicht mehr ausreicht. Die Tabelle information_schema.TABLES liefert für jede Tabelle data_length, index_length und die geschätzte Zeilenanzahl über table_rows, was für ein monatliches Monitoring vollkommen ausreicht. Wichtig ist, nicht nur die absolute Größe zu betrachten, sondern die Wachstumsrate zwischen zwei Messzeitpunkten, weil eine große, aber stabile Tabelle unkritischer ist als eine kleine Tabelle mit exponentiellem Wachstum.
Eine einfache, aber wirksame Monitoring-Routine läuft wöchentlich per Cron, schreibt die zehn größten Tabellen in eine eigene Log-Tabelle und alarmiert, wenn eine Tabelle innerhalb einer Woche um mehr als einen definierten Schwellenwert wächst. Das identifiziert Ausreißer wie einen fehlerhaft konfigurierten Import, der ungewollt Millionen Zeilen in catalog_product_entity_varchar schreibt, deutlich früher als eine rein reaktive Betrachtung beim nächsten Backup-Problem. Für die Gesamtgröße der Datenbank genügt eine einzelne Summenabfrage über data_length und index_length, die sich gut als Kennzahl für die Kapazitätsplanung eignet.
6. Wachstumstrend über Zeit protokollieren
Ein einzelner Snapshot der Tabellengrößen zeigt nur den Status quo, nicht die Dynamik des Datenbank-Wachstums. Sinnvoller ist eine eigene, kleine Log-Tabelle, in die ein wöchentlicher Cron-Job die aktuellen Größen der wichtigsten Tabellen einträgt. Über einen Zeitraum von mehreren Monaten lässt sich daraus eine echte Wachstumskurve ableiten, die zeigt, ob das Wachstum linear, saisonal oder exponentiell verläuft, und welche Tabelle den größten Anteil am Gesamtwachstum trägt.
Diese historische Sicht ist besonders wertvoll bei der Bewertung, ob eine Bereinigungsmaßnahme tatsächlich wirkt. Nach der Aktivierung eines Quote-Cleanup-Cronjobs sollte die Wachstumsrate von quote sichtbar zurückgehen, was sich nur mit historischen Messwerten nachweisen lässt. Ohne diese Protokollierung bleibt jede Aussage über die Wirksamkeit einer Bereinigungsmaßnahme reine Vermutung.
-- Own tracking table for growth history, populated weekly via cron
CREATE TABLE db_growth_log (
log_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
table_name VARCHAR(64) NOT NULL,
table_rows BIGINT UNSIGNED NOT NULL,
size_mb DECIMAL(12,2) NOT NULL,
measured_at DATE NOT NULL,
PRIMARY KEY (log_id),
KEY IDX_TABLE_DATE (table_name, measured_at)
) ENGINE=InnoDB;
-- Weekly insert job (called from a cron wrapper)
INSERT INTO db_growth_log (table_name, table_rows, size_mb, measured_at)
SELECT table_name, table_rows,
ROUND((data_length + index_length) / 1024 / 1024, 2), CURDATE()
FROM information_schema.TABLES
WHERE table_schema = 'magento';
7. Automatisierte Bereinigungsroutine per Cron
Manuelle Bereinigung skaliert nicht, deshalb muss jede Erkenntnis über Datenbank-Wachstum in eine automatisierte Routine überführt werden. Eine praktische Struktur ist ein eigenes Wartungsskript, das per crontab.xml oder System-Cron regelmäßig läuft und in klar getrennten Schritten arbeitet: abgelaufene Quotes löschen, alte Report-Events bereinigen, verwaiste URL-Rewrites entfernen und die Wachstums-Log-Tabelle aktualisieren.
Wichtig ist, jede Löschoperation in überschaubaren Batches auszuführen (zum Beispiel 5.000 Zeilen pro Durchlauf mit kurzer Pause dazwischen), statt eine einzelne DELETE-Anweisung über Millionen Zeilen laufen zu lassen, die lange Locks erzeugt und die produktive Datenbank während der Ausführung spürbar verlangsamt. Ein Wartungsfenster außerhalb der Stoßzeiten reduziert zusätzlich das Risiko, dass die Bereinigungsroutine mit dem regulären Shop-Betrieb kollidiert.
#!/usr/bin/env bash
# db-growth-cleanup.sh: batched cleanup routine, called from cron
set -euo pipefail
MAGENTO_ROOT="/var/www/html"
BATCH_SIZE=5000
cd "$MAGENTO_ROOT"
# Remove abandoned quotes older than 90 days that never became an order
bin/magento maintenance:allow-ips 127.0.0.1 >/dev/null 2>&1 || true
mysql magento -e "
DELETE FROM quote
WHERE is_active = 0
AND updated_at < DATE_SUB(NOW(), INTERVAL 90 DAY)
AND entity_id NOT IN (SELECT quote_id FROM sales_order)
LIMIT ${BATCH_SIZE};
"
echo "[OK] cleanup batch of up to ${BATCH_SIZE} rows completed"
8. Partitionierung und Archivierung bei großen Shops
Bei sehr großen Shops mit Millionen Bestellungen reicht reines Löschen alter Daten oft nicht aus, weil rechtliche Aufbewahrungspflichten das Löschen von Bestelldaten verbieten. Hier hilft Datenbank-Wachstum zu kontrollieren, ohne Daten zu verlieren: Partitionierung nach Datum für Tabellen wie sales_order_status_history oder report_event reduziert die effektive Arbeitsmenge, die MySQL bei Abfragen durchsuchen muss, weil der Optimizer alte Partitionen bei zeitlich eingegrenzten Queries komplett überspringen kann.
Für Tabellen ohne rechtliche Aufbewahrungspflicht, aber mit historischem Wert für Reporting, ist eine Archivierung in eine separate Datenbank oder ein separates Data-Warehouse-System sinnvoller als reines Löschen. Ein monatlicher Job verschiebt Zeilen älter als ein definierter Schwellenwert aus der produktiven Tabelle in eine Archivtabelle, wodurch die produktive Datenbank klein und performant bleibt, während historische Auswertungen weiterhin möglich sind.
9. Kapazitätsplanung: DB-Größe, Backup und Performance
Unkontrolliertes Datenbank-Wachstum hat einen direkten Zusammenhang mit Backup-Zeiten: Ein mysqldump über eine 60-Gigabyte-Datenbank dauert deutlich länger als über eine bereinigte 15-Gigabyte-Datenbank, und dieselbe Differenz gilt für die Wiederherstellungszeit im Ernstfall. Wachsende Backup-Fenster kollidieren irgendwann mit Wartungsfenstern oder Deployment-Zeitplänen, was die Kapazitätsplanung zu einem betrieblichen, nicht nur technischen Thema macht.
Auch die InnoDB-Buffer-Pool-Dimensionierung hängt direkt von der Datenbankgröße ab: Passt die aktive Arbeitsmenge nicht mehr in den Buffer Pool, steigt die Disk-I/O-Last spürbar an, was sich in langsameren Admin-Abfragen und Reindex-Läufen zeigt. Eine regelmäßige Kapazitätsplanung, die Datenbank-Wachstum gegen verfügbaren RAM, Backup-Fenster und Storage-Kosten abgleicht, verhindert, dass diese Themen erst bei einem akuten Ausfall sichtbar werden.
Vergleich: Tabellentypen nach Wachstumsverhalten
| Tabellengruppe | Wachstumstreiber | Bereinigungsstrategie | Aufbewahrungspflicht |
|---|---|---|---|
| quote, quote_item | Abgebrochene Warenkörbe | Batched DELETE nach 90 Tagen | Keine |
| report_event | Seitenaufrufe, Interaktionen | Eingebautes Log Cleaning | Keine |
| url_rewrite | Kategorie × Produkt × Store | Alte Redirects prüfen und entfernen | SEO-Rücksicht nötig |
| sales_order | Echte Bestellungen | Nicht löschen, ggf. archivieren | Gesetzlich vorgeschrieben |
| cron_schedule | Abgeschlossene Cron-Jobs | history_cleanup_every | Keine |
Mironsoft
Magento-Datenbankwartung und Kapazitätsplanung
Datenbank-Wachstum außer Kontrolle geraten?
Wir bauen Monitoring für eure Tabellengrößen auf, richten automatisierte Bereinigungsroutinen ein und planen Kapazität so, dass Backups und Deployments zuverlässig in ihren Zeitfenstern bleiben.
Wachstums-Audit
Größte Tabellen und Wachstumstreiber identifizieren
Cleanup-Automatisierung
Batched Bereinigungs-Cronjobs ohne Lock-Risiko
Kapazitätsplanung
Backup-Fenster, Buffer Pool und Storage vorausschauend planen
10. Zusammenfassung
Das Datenbank-Wachstum in Magento-Shops folgt selten dem Produktkatalog, sondern wird von transaktionalen und protokollierenden Tabellen wie quote, report_event und url_rewrite getrieben. Verwaiste Warenkörbe, ungebremste Event-Protokollierung und die multiplikative Natur von URL-Rewrites bei aktivierten Kategorie-Pfaden sind die häufigsten Ursachen für Datenbanken, die deutlich größer sind, als es der eigentliche Geschäftsbetrieb erfordern würde.
Ein wirksames Gegenmittel gegen unkontrolliertes Datenbank-Wachstum besteht aus drei Bausteinen: regelmäßiges Monitoring über information_schema.TABLES mit historischer Protokollierung, batched Bereinigungsroutinen per Cron statt einmaliger manueller Eingriffe, und eine Kapazitätsplanung, die Backup-Zeiten und Buffer-Pool-Dimensionierung vorausschauend an die tatsächliche Datenbankgröße anpasst. Wo rechtliche Aufbewahrungspflichten das Löschen verbieten, ersetzt Partitionierung und Archivierung das reine Löschen als Strategie gegen weiteres Wachstum.
Datenbank-Wachstum in Magento-Shops: Das Wichtigste auf einen Blick
Häufigste Treiber
quote, report_event, customer_visitor und url_rewrite wachsen proportional zum Traffic, nicht zum Katalog.
Monitoring
information_schema.TABLES wöchentlich in eine eigene Log-Tabelle schreiben, um Trends zu erkennen.
Bereinigung
Batched DELETE-Operationen per Cron, niemals eine einzelne Löschung über Millionen Zeilen.
Rechtliche Grenzen
sales_order und verwandte Tabellen archivieren statt löschen, wenn Aufbewahrungspflichten bestehen.