LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Blog-Übersicht /WordPress-Plugins & Tricks/Tutorials / WordPress: Abgelaufene Transients mit WP-CLI bereinigen
Diesen Artikel in anderen Sprachen lesen:

WordPress: Abgelaufene Transients mit WP-CLI bereinigen

WordPress: Abgelaufene Transients mit WP-CLI bereinigen

Management Summary

WordPress speichert vorübergehende Werte, damit manche Arbeit nicht bei jedem Seitenaufruf erneut anfällt. Diese sogenannten Transients können nach Ablauf ihrer Gültigkeit in der Datenbank zurückbleiben. Gezielte Bereinigung entfernt abgelaufene Einträge und erhält Beiträge, frühere Fassungen und Sprachverknüpfungen. Der Beitrag erklärt diese Pflege mit dem Befehlswerkzeug von WordPress. Dadurch können unnötige Daten entfallen; eine kleinere Datenbank bedeutet aber nicht automatisch eine schnellere Website.

Inhalt
  1. 1. Größe und Inhalt getrennt bewerten
  2. 2. Ein begrenztes Wartungsskript
  3. 3. Erst auf einer getrennten Kopie testen
  4. 4. Cron mit sichtbaren Ergebnissen einrichten
  5. 5. Revisionen und Tabellenumbau separat entscheiden
  6. Fragen und Antworten
  7. Quellen

Eine große WordPress-Datenbank ist nicht automatisch eine schlecht gepflegte Datenbank. Revisionen enthalten wiederherstellbare Textstände; Transients sind zwischengespeicherte Daten. Vor einer Bereinigung zählt deshalb der konkrete Befund: Welche Daten werden nicht mehr gebraucht, welcher Speicher wird tatsächlich belegt und welche Abfrage ist langsam?

Die folgende Anleitung automatisiert ausschließlich das Entfernen abgelaufener Datenbank-Transients. Revisionen, Beitragsmetadaten und Sprachzuordnungen bleiben erhalten. Voraussetzung sind Linux mit Bash und flock, eine geprüfte WP-CLI-Installation sowie eine einzelne WordPress-Website ohne externen Objektcache. Multisite und Redis-/Memcached-Konfigurationen benötigen ein eigenes Verfahren.

Begrenzter Umfang statt pauschalem Löschen
Begrenzter Umfang statt pauschalem Löschen

1. Größe und Inhalt getrennt bewerten

wp db size --tables --all-tables-with-prefix --human-readable --orderby=size --order=desc listet Tabellen mit dem konfigurierten Präfix nach Größe absteigend. Erst die zusätzlichen Sortieroptionen legen diese Reihenfolge fest. Die Ausgabe beweist weder Fragmentierung noch einen bestimmten Geschwindigkeitsverlust.

WordPress legt nicht bei jedem unveränderten Speichern zwingend eine neue Revision an. Beim regulären endgültigen Löschen eines Beitrags entfernt die WordPress-API auch zugehörige Metadaten und Zuordnungen. Verwaiste Datensätze sind Einträge, deren zugehöriges Objekt nicht mehr existiert. Solche Einträge können beispielsweise nach direkten SQL-Eingriffen oder fehlerhaften Erweiterungen entstehen; deren Ursache sollte vor einer Löschroutine geklärt werden.

Abgelaufene Transients werden beim Zugriff behandelt, zusätzlich besitzt WordPress eine geplante Bereinigung. Ein Rückstau kann daher auf nicht ausgeführte Cron-Ereignisse hinweisen. Bei externem Objektcache liegen Transients nicht zwingend in der Datenbank. Eine SQL-Zählung von Timeout-Zeilen bildet dann nicht den gesamten Cache ab.

2. Ein begrenztes Wartungsskript

Das Skript erhält den WordPress-Pfad über WP_PATH, einen vorhandenen privaten Arbeitsordner über STATE_DIR und optional den absoluten WP-CLI-Pfad über WP_BIN. Der Arbeitsordner muss außerhalb sämtlicher öffentlich ausgelieferter Verzeichnisse liegen und nur für das Wartungskonto zugänglich sein. Die Pfadprüfung im Skript erkennt den WordPress-Unterordner, ersetzt aber keine Prüfung zusätzlicher Webserver-Aliase.

Ohne Argument läuft audit: Tabellenumfang, Revisionsanzahl und Transient-Anzahl werden ausgegeben. Die Transient-Anzahl enthält auch noch gültige Einträge und ist keine Löschvorschau. apply erzeugt zunächst einen Datenbankexport und löscht anschließend ausschließlich abgelaufene normale und Site-Transients. Site-Transients sind hier die besonders benannten Werte einer Einzelinstallation.

#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
export PATH=/usr/local/bin:/usr/bin:/bin
: "${WP_PATH:?Set the WordPress directory}"
: "${STATE_DIR:?Set a private existing directory outside every webroot}"
WP_BIN="${WP_BIN:-/usr/local/bin/wp}"
MODE="${1:-audit}"
[[ "$MODE" == audit || "$MODE" == apply ]] || { echo "Use audit or apply" >&2; exit 2; }
[[ -x "$WP_BIN" && -d "$WP_PATH" && -d "$STATE_DIR" ]] || { echo "Invalid paths" >&2; exit 2; }
WP_PATH=$(realpath "$WP_PATH")
STATE_DIR=$(realpath "$STATE_DIR")
case "$STATE_DIR/" in "$WP_PATH/"*) echo "State directory is inside WordPress" >&2; exit 2;; esac
exec 9>"$STATE_DIR/maintenance.lock"
flock -n 9 || { echo "Another maintenance run holds the lock" >&2; exit 75; }
trap 'status=$?; echo "FAILED (exit $status); inspect the last completed step" >&2; exit "$status"' ERR
wp_run() { "$WP_BIN" --path="$WP_PATH" "$@"; }
wp_run core is-installed
wp_run eval 'if (is_multisite() || wp_using_ext_object_cache()) { WP_CLI::error("Example requires single-site without external object cache"); }'
wp_run db size --tables --all-tables-with-prefix --human-readable --orderby=size --order=desc
printf '%s\n' "Revision count (not scheduled for deletion):"
wp_run post list --post_type=revision --post_status=inherit --format=count
printf '%s\n' "Database transient count (includes unexpired entries):"
wp_run transient list --format=count
if [[ "$MODE" == audit ]]; then
    printf '%s\n' "Audit finished; no cleanup commands executed."
    exit 0
fi
wp_run eval 'global $wpdb; $rows=$wpdb->get_results("SHOW TABLE STATUS"); if ($wpdb->last_error || !$rows) { WP_CLI::error("Table engine check failed"); } foreach ($rows as $row) { if ($row->Engine !== null && $row->Engine !== "InnoDB") { WP_CLI::error("Backup example requires InnoDB tables"); } }'
partial=$(mktemp "$STATE_DIR/backup-$(date -u +%Y%m%dT%H%M%SZ)-XXXXXX.sql.partial")
wp_run db export "$partial" --single-transaction --skip-lock-tables
[[ -s "$partial" ]] || { echo "Empty backup; cleanup blocked" >&2; exit 1; }
backup="${partial%.partial}"
mv -- "$partial" "$backup"
printf 'Backup created: %s\n' "$backup"
wp_run transient delete --expired
wp_run transient delete --expired --network
printf '%s\n' "Expired-transient cleanup completed."

Die Sperre verhindert parallele Aufrufe dieses Skripts mit demselben Arbeitsordner. Andere Wartungswerkzeuge oder laufende Websitezugriffe werden dadurch nicht gesperrt. Ein Fehler beendet den Lauf; bereits erledigte Schritte werden nicht automatisch rückgängig gemacht. Scheitert die zweite Transient-Bereinigung, kann die erste bereits erfolgreich gewesen sein.

Der Export nutzt eine konsistente Transaktion für InnoDB-Tabellen. Währenddessen darf die Struktur der betroffenen Tabellen nicht verändert werden, etwa durch Hinzufügen oder Entfernen von Spalten. Die Engine-Prüfung kontrolliert das verwendete Speicherverfahren der Tabellen und lehnt andere Verfahren als InnoDB ab. Ein erfolgreicher Export und eine nicht leere Datei ersetzen keinen Wiederherstellungstest. Der Dump enthält möglicherweise personenbezogene Daten und Geheimnisse; Aufbewahrung, Zugriff, freier Speicher und eine getrennte Sicherung sind vor regelmäßigem Betrieb festzulegen. Fehlgeschlagene Teil-Dateien bleiben zur Untersuchung erhalten.

3. Erst auf einer getrennten Kopie testen

Das Skript gehört in eine administrativ verwaltete Datei, die das ausführende Konto lesen und ausführen, aber nicht verändern kann. Der private Arbeitsordner benötigt Schreibrechte für dieses Konto. WP-CLI muss WordPress-Konfiguration und Datenbank erreichen können. Eine pauschale Ausführung als root ist dafür nicht erforderlich.

Ein erster Aufruf erfolgt mit den tatsächlichen Pfaden und audit. Auch dieser Modus lädt WordPress und Erweiterungen; das ist keine Garantie für eine vollständig schreibfreie Plugin-Initialisierung. Vor apply sind Export und Wiederherstellung auf einer getrennten Testinstanz zu erproben. Danach werden Fehlerfälle wie ungültiger Pfad, fehlgeschlagener Export und ein zweiter gleichzeitiger Lauf geprüft.

Nach einer Bereinigung zählen nicht nur Dateigrößen. Startseite, Bearbeitung, Sprachwechsel, Kategorien und ausgewählte Funktionen benötigen eine Kontrolle. Transient-Löschung kann neue Cache-Berechnungen auslösen und vorübergehend Last erzeugen. WP-CLI nutzt weiterhin PHP und die Datenbank; „keine Ressourcenbelastung“ ist deshalb keine zutreffende Zusage.

4. Cron mit sichtbaren Ergebnissen einrichten

Nach erfolgreichen Tests kann die Crontab des vorgesehenen Wartungskontos feste Werte für WP_PATH, STATE_DIR und WP_BIN enthalten. Ein Beispielplan ist 0 3 * * 0 /bin/bash /opt/scripts/wp-transients.sh apply. Dieser Ausdruck plant den Lauf sonntags um 03:00 Uhr in der Zeitzone des Cron-Dienstes. Pfad und Zeitpunkt sind an die Installation anzupassen; /opt/scripts/wp-transients.sh ist der gewählte Ablageort des gezeigten Skripts.

Ausgaben dürfen nicht einfach nach /dev/null verschwinden. Cron-Mail benötigt einen funktionierenden Mailtransport; alternativ braucht es ein nur für berechtigte Konten zugängliches Protokoll mit begrenzter Dateigröße und Aufbewahrungsdauer und eine tatsächlich geprüfte Fehlerbenachrichtigung. Zusätzlich sollte ein ausbleibender erfolgreicher Lauf erkennbar sein. Ein ausgeschalteter Rechner oder ein gestoppter Cron-Dienst führt keine Aufgabe aus.

Der Plan richtet sich nach der vom installierten Cron verwendeten Zeitzone. Sommerzeitbehandlung unterscheidet sich zwischen Implementierungen. Eine feste Zeitangabe allein garantiert weder sekundengenauen Start noch erfolgreiche Ausführung. Der WP-CLI-Pfad und die eingeschränkte Cron-Umgebung müssen unter dem tatsächlichen Konto getestet werden.

5. Revisionen und Tabellenumbau separat entscheiden

Eine Revisionsgrenze wie define( 'WP_POST_REVISIONS', 3 ); erhält einen begrenzten Verlauf. Eine vorhandene Definition wird angepasst statt doppelt angelegt. Die Begrenzung wird beim Anlegen neuer Revisionen angewandt; das bloße Setzen der Konstante bereinigt nicht sofort den gesamten Bestand. Autosaves und Filter von Erweiterungen sind zusätzlich zu beachten.

wp db optimize ist keine notwendige wöchentliche Ergänzung. Bei MySQL-InnoDB kann OPTIMIZE einen Tabellenneuaufbau auslösen; auch Online-DDL, also Strukturänderungen während des laufenden Datenbankbetriebs, benötigt in bestimmten Phasen Sperren. Speicherbedarf, Tabellenmerkmale, Engine und Version bestimmen das Verhalten. MariaDB besitzt eigene Varianten. Eine Verkleinerung der Datenbankdatei oder eine bestimmte Beschleunigung ist nicht garantiert. Messbare Probleme und ein geplantes Wartungsfenster sind die Grundlage.

Fragen und Antworten

Warum fehlt die pauschale Löschung von Taxonomie-Verknüpfungen?

term_relationships.object_id bezeichnet nicht immer einen Beitrag. Polylang nutzt die Tabelle unter anderem für Sprach- und Übersetzungszuordnungen von Kategorien und Schlagwörtern. Eine Verknüpfung nur gegen die Beitragstabelle kann gültige Beziehungen löschen; zufällig gleiche IDs können den Fehler teilweise verdecken. Solche Reparaturen benötigen eine fachlich geprüfte, spezifische Auswahl.

Wird die Datenbank dadurch um 40 bis 60 Prozent kleiner?

Eine allgemeine Prozentzahl ist unbelegt. Entfernte Zeilen, intern wiederverwendbarer Speicher und an das Betriebssystem zurückgegebener Speicher sind unterschiedliche Größen. Auch die Laufzeit konkreter Abfragen muss separat gemessen werden.

Reicht die Sicherung im selben Ordner?

Der lokale Export hilft vor einer Änderung, schützt aber nicht vor dem Ausfall dieses Datenträgers. Eine getrennte Sicherung und ein erprobter Wiederherstellungsweg bleiben erforderlich. Eine Wiederherstellung über eine inzwischen weiterbenutzte Datenbank kann neuere Änderungen überschreiben und muss geplant werden.

Quellen

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Kontakt

Eine Frage oder eine Idee für ein Webprojekt? Schreib mir kurz, worum es geht. Das Formular nutzt reCAPTCHA zum Schutz vor Spam.

Kommentar schreiben

Erfahrungen mit anderen Plugins oder Hosting-Umgebungen und Rückfragen zur Einrichtung sind hier willkommen.

Die E-Mail-Adresse wird nicht veröffentlicht. Pflichtfelder sind mit einem Stern versehen.