Der WordPress Query Cache verspricht kürzere Ladezeiten, verursacht in der Praxis jedoch oft Invalidierungen, Latenz und inkonsistente Inhalte. Ich zeige, warum dieser Cache bei WordPress-Setups häufig Performance frisst und wie ich stattdessen stabile Geschwindigkeit erreiche.
Zentrale Punkte
- Invalidierung: Häufige Schreibvorgänge leeren den Cache und erzeugen Overhead.
- Latenz: Externe Caches fügen Verbindungszeit hinzu, die oft jede Ersparnis frisst.
- Inkonsistenz: Veraltete Einträge führen zu alten Preisen, falschen Listen oder leeren Warenkörben.
- RAM: Der Cache konkurriert mit PHP, MySQL und Nginx/Apache um Speicher.
- Alternativen: Page Caching, OPcache und saubere Queries liefern den verlässlichen Gewinn.
Wie der WordPress Query Cache wirklich arbeitet
MySQL legt Ergebnisse von SELECT-Abfragen anhand des exakten SQL-Textes im Cache ab, liefert diese bei identischer Anfrage wieder aus und spart sich so die Ausführung. In WordPress treffen jedoch fortlaufend INSERTs, UPDATEs und DELETEs ein, was den Query Cache auf betroffene Tabellen sofort invalidiert. Ich sehe in Logs regelmäßig eine Endlosschleife: füllen, leeren, erneut füllen – dabei verbrennt der Server CPU-Zeit ohne spürbaren Nutzen. Zusätzlich kollidiert der MySQL-Query-Cache mit WordPress-eigenen Mechanismen wie Transients und dem Objekt-Cache, was die Latenz erweitert statt sie zu senken. Wer den WordPress Query Cache einschaltet, baut deshalb oft eine doppelte Caching-Schicht auf, die schneller kippt, als sie tragen kann.
Begriffsklärung: Was hier wirklich gecacht wird
Ich unterscheide drei Ebenen, die häufig vermischt werden:
- MySQL Query Cache: Ergebnis-Cache für identische SELECT-Statements. Jeder Schreibvorgang auf betroffene Tabellen macht Einträge ungültig. In modernen OLTP-Workloads wie WordPress ist das kontraproduktiv. In neueren MySQL-Versionen ist dieser Cache zudem obsolet; in MariaDB existiert er noch, aber ich schalte ihn auch dort ab.
- InnoDB Buffer Pool: Kein Ergebnis-Cache, sondern ein Seiten-Cache für Daten- und Indexseiten. Er ist der robuste, bewährte Pfad für wiederkehrende Lesezugriffe. Eine solide Größe des Buffer Pools bringt oft mehr als jeder Ergebnis-Cache.
- WordPress Objekt-Cache/Transients: Anwendungsseitiger Cache (häufig Redis/Memcached), in dem vorbereitete Strukturen wie WP_Query-Ergebnisse, Optionen oder Fragment-HTML liegen. Dieser Layer hilft nur dann, wenn Lesezugriffe die Regel sind und Invalidierung zuverlässig funktioniert.
Im Alltag beobachte ich, dass der Buffer Pool und ein gut gewählter Page Cache die größten Hebel sind. Ein zusätzliches Query-Caching sorgt selten für Netto-Gewinn, sondern erhöht Komplexität, Latenz und das Risiko von Inkonsistenzen.
Warum er bremst statt hilft
Auf geteilten Hosts oder bei WooCommerce erzeugt der Cache spürbare Verzögerung, weil jeder Netzwerkgang zu Redis oder Memcached Zeit kostet und kaum Treffer bringt. Selbst auf schnellen Maschinen verliere ich häufig Millisekunden pro Request, die sich bei Traffic summieren und Time-to-First-Byte aufblähen. Dazu kommt das Risiko veralteter Ergebnisse, wenn Invalidierungshaken fehlen oder Plugins fehlerhaft arbeiten – plötzlich sieht ein Kunde einen falschen Preis oder verpasste Lagerbestände. Wer genauer hinschauen will, dem lege ich meinen Erfahrungsbericht zu Object Cache bremst ans Herz, denn ähnliche Muster gelten für Query-Caching-Ebenen. Im Mittel erreicht ein sauberer Direktzugriff auf die Datenbank mit gutem Schema und OPcache bessere und stabilere Antwortzeiten.
Cache-Stampede, TTL und Koordination
Ein wiederkehrendes Muster in WordPress-Stacks ist die Cache-Stampede: Ein Eintrag läuft ab, viele Requests stellen gleichzeitig dieselbe teure Abfrage und erzeugen Spikes. Query- und Objekt-Caches ohne Koordination verschärfen das. Ich nutze drei Strategien, um das zu vermeiden:
- Locking/Coalescing: Ein Request baut den Eintrag, die anderen warten kurz oder liefern den alten Wert (stale-while-revalidate) und aktualisieren im Hintergrund.
- Sinnvolle TTLs: Keine beliebigen 24h-Standards. Produktlisten oder Preisfragmente erhalten kurze TTLs (Sekunden/Minuten), Katalog-Metadaten längere.
- Ereignisbasierte Invalidierung: Statt stumpfer Zeitabläufe nutze ich Hooks (z. B. bei Post-Updates), um nur betroffene Schlüssel zu löschen – oder besser: Page-Cache gezielt zu erneuern.
Wichtig: In WordPress werden Transients bei aktivem persistentem Objekt-Cache effektiv dauerhaft gespeichert. Wer hier keine saubere Invalidierung hat, baut sich Inkonsistenzen und schwer reproduzierbare Fehlerbilder.
Typische Symptome auf produktiven Sites
Wenn der WordPress Query Cache schadet, erkenne ich das zuerst an einer schwankenden Antwortzeit, die ohne Codeänderung plötzlich hoch und runter geht. Abends, wenn mehr Bestellungen einlaufen, häufen sich Invalidierungen und die Seite fällt in eine Spirale aus Cache-Miss und erneuten Einträgen. Das Monitoring zeigt dann volatile CPU-Spitzen bei MySQL, während PHP-FPM auf neue Ergebnisse wartet. Kunden melden gleichzeitig Mismatchs wie doppelte Kommentare, leere Warenkörbe oder verspätete Aktualisierungen von Produkt-Widgets. In Logs tauchen außerdem vermehrt Lock-Warnungen auf, weil konkurrierende Prozesse dieselben Tabellen ständig beschreiben und damit den Cache entwerten.
Ebenen des Cachings: Reihenfolge statt Stapeln
Ich priorisiere Caches nach Wirkungskette:
- Browser-/CDN-/Edge-Cache für vollständig öffentliche Seiten, differenziert nach Cookies/Headers.
- Page Cache im Stack (Webserver/Plugin), idealerweise mit Preload und gezieltem Purge auf Events.
- OPcache für konsistent kompilierten PHP-Bytecode.
- Objekt-Cache nur selektiv für teure, selten ändernde Objekte.
- Datenbank mit starkem Schema, Indizes und großem Buffer Pool.
Wer diese Reihenfolge einhält, senkt nicht nur TTFB, sondern vor allem die Varianz – das, was Nutzer als „Ruckeln“ wahrnehmen.
Bessere Optionen für echte Geschwindigkeit
Ich gewinne verlässlich Performance, wenn ich zuerst Page Caching aktiviere, OPcache sauber konfiguriere und dann Datenbankabfragen straffe. Page Caching liefert HTML aus, senkt Datenbank-Load gegen null und glättet Lastspitzen. OPcache kompiliert PHP einmalig, wodurch PHP-FPM weniger Arbeit leisten muss und TTFB sinkt. Ein objektbasierter Cache mit Redis lohnt sich nur, wenn Serverressourcen großzügig vorhanden sind und die Anwendungslogik wenige Schreibzugriffe pro Seite erzeugt. Mit dieser Reihenfolge eliminiere ich Engpässe an der Quelle und halte die Zahl der beweglichen Teile überschaubar, statt einen fragilen Zwischenspeicher zu pflegen.
| Maßnahme | Hauptnutzen | Risiko/Besonderheit |
|---|---|---|
| Page Caching (statisches HTML) | Sehr niedrige TTFB, kaum DB-Last | Inhalte müssen bei Änderungen gezielt erneuert werden |
| OPcache (PHP-Bytecode) | Weniger CPU-Zeit pro Request | Benötigt konsistente Deploy- und Warmup-Strategie |
| Redis Object Cache | Schneller Zugriff auf häufige Objekte | Hilft nur bei Treffern; frisst RAM, braucht sauberes Key-Design |
| MySQL Query Cache | Theoretisch weniger Query-Ausführung | Hoher Invalidierungsaufwand, Inkonsistenzrisiko, zusätzlicher Overhead |
Praktische Anleitung: Deaktivieren und Alternativen testen
Ich starte mit MySQL und schalte den Query Cache auf Systemebene ab, indem ich in der Konfiguration query_cache_type auf 0 und query_cache_size auf 0 setze. Danach räume ich in WordPress auf: Falls ein Drop-in oder eine Konstante Objekt-Caching erzwingt, deaktiviere ich es testweise mit define('WP_CACHE', false);. Anschließend messe ich mit Werkzeugen wie Query Monitor, Blackfire oder simplen Metriken (TTFB, Queries/Request, CPU) die tatsächliche Auswirkung pro Seite. Erst wenn Page Caching gesetzt ist und PHP/OPcache sauber laufen, beurteile ich gezielt, ob ein kleiner Redis-Layer einzelne Hotspots entlastet. Mit dieser Reihenfolge erhalte ich reproduzierbare Ergebnisse und sichere Stabilität, statt auf Zufallstreffer zu hoffen.
Konkrete Konfigurationen, die sich bewährt haben
Ein paar Defaults, mit denen ich regelmäßig stabile Zugewinne erziele (immer am eigenen System validieren):
- MySQL/MariaDB:
Der Buffer Pool trägt die Hauptlast. Das Slow-Log zeige ich Entwicklern und entferne systematisch N+1- und SELECT *-Muster.[mysqld] query_cache_type=0 query_cache_size=0 innodb_buffer_pool_size=60-70%_vom_RAM innodb_flush_log_at_trx_commit=1 slow_query_log=1 long_query_time=0.2 log_queries_not_using_indexes=1 - OPcache:
Wichtig ist ein konsistentes Deploy (z. B. atomic Symlinks) und ein Warmup nach Releases.opcache.enable=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=100000 opcache.validate_timestamps=1 opcache.revalidate_freq=60 ; JIT bei klassischen WordPress-Stacks eher aus: opcache.jit=0 - PHP-FPM:
So werden Lecks und Fragmentierung abgefedert, ohne Kaltstarts zu provozieren.pm=dynamic pm.max_children=<nach CPU/RAM> pm.max_requests=500-1000 process_idle_timeout=10s - Redis (falls genutzt):
Ich akzeptiere Redis nur lokal oder im selben AZ/Host – über langsame Netze wird daraus schnell ein Latenzverstärker.maxmemory <RAM-Budget> maxmemory-policy volatile-lru tcp-backlog 511 ; lokal bevorzugt per UNIX-Socket für minimale Latenz
Datenbank sauber halten: Indizes, Queries, Plugins
Bevor ich Caches staple, optimiere ich Abfragen und Indizes, weil die größte Zeitersparnis aus guter Datenarbeit kommt. Überlange JOINs, SELECT *, fehlende WHERE-Bedingungen und Sortierungen ohne Index kosten mehr Zeit als jeder Cache einsparen kann. Ich prüfe regelmäßig Plugins, die viele Optionen in wp_options ohne Autoload-Strategie speichern, und beseitige überflüssige Erweiterungen. Ein gezielter Helfer kann sein, die eigenen SQL-Muster zu sichten und zu straffen – einen Einstieg liefert Datenbankabfragen optimieren. Mit sauberer Query-Disziplin sinkt der Druck auf den Server messbar, und der vermeintliche Nutzen des WordPress Query Cache erledigt sich von selbst, weil nichts mehr zu kaschieren bleibt.
Hosting-Faktoren, die Caching schlagen
Gute CPU-Leistung, schnelle NVMe-SSDs, genügend RAM und aktuelle MySQL-Versionen machen mehr aus als ein fragiler Query Cache. Außerdem spielt die Webserverkonfiguration eine große Rolle: Keep-Alive, HTTP/2 oder HTTP/3, sinnvolle Timeouts und ein schlanker PHP-Prozesspool. Ich achte darauf, dass OPcache großzügig dimensioniert ist, damit der Bytecode der häufigen Skripte komplett hineinpasst. Gleichzeitig begrenze ich Cron-Jobs und Hintergrundaufgaben, die während Hauptverkehrszeiten Query-Stürme auslösen. So entsteht eine solide Basisleistung, auf der ich Page Caching und gezieltes Objekt-Caching punktgenau nutzen kann, ohne mich in spröden Workarounds zu verlieren.
Messmethoden: So beurteile ich den Effekt
Ich messe zunächst die Baseline ohne Query Cache: kalter Start, warmer Start, dann 50 bis 200 Requests mit JMeter oder k6. Anschließend aktiviere ich gezielt nur eine Stellschraube, niemals mehrere zugleich, und wiederhole die Lasttests. Ich erfasse Metriken wie TTFB, P95/P99-Latenz, Queries pro Request, Cache-Hit-Rates und CPU/IO-Werte. Ein echter Gewinn liegt für mich vor, wenn Median und P95 fallen, Fehlerquoten sinken und die Varianz kleiner wird. In so gut wie allen WordPress-Projekten zeigt dieses Verfahren, dass der WordPress Query Cache die Streuung erhöht und den mittleren Antwortwert verschlechtert.
Tuning-Playbook: Schwellenwerte und Checks
- Hit-Rate: Unter ~60% Objekt-Cache-Treffer auf Produktivverkehr lohnt es selten. Ich deaktiviere dann konsequent und messe erneut.
- Redis-Latenz: >1 ms median lokal ist zu viel. Per UNIX-Socket und kurzer Pipeline lassen sich Sub-Millis erreichen.
- DB-Wartezeiten: Steigt Threads_running unter Last stark an, prüfe ich zuerst Indizes/Queries – nicht den Cache hochdrehen.
- Varianz: Eine sinkende P95 ist mir wichtiger als eine kosmetisch bessere Median-Statistik.
- Invalidierungen: Bei jedem Content- oder Preis-Update beobachte ich, wie viele Keys fallen. Breite Löschungen sind ein Anti-Pattern.
- Warmup: Nach Deploys/Page-Purges prewarme ich kritische Routen (Startseite, Kategorie, Checkout) automatisiert.
Kompatibilität und Risiken bei Plugins
Einige Erweiterungen überschreiben Cache-Schlüssel, räumen Transients zu früh ab oder ignorieren notwendige Hooks, was zu verwaisten Einträgen führt. In Multisite-Umgebungen werden Probleme sichtbarer, weil mehr Schreibereignisse parallel anfallen und Invalidierung häufiger einschlägt. E-Commerce-Workflows mit dynamischen Preisen, Gutscheinen und Warenkorb-Fragmenten sind besonders empfindlich: Schon wenige Millisekunden Verzögerung kippen Checkout-Metriken. Ich isoliere deshalb Caching-Probleme, indem ich Plugins schrittweise deaktiviere und an klaren Messpunkten verifiziere. Wenn ein Add-on den Query Cache voraussetzt, verzichte ich darauf und wähle eine Lösung, die ohne anfällige Zwischenschicht sauber arbeitet.
Operative Sicherheit: Rollback und Wartung
Ich halte Caching-Änderungen rückrollbar. Das heißt: Feature-Flags für Objekt-/Page-Cache, getrennte Konfigurationsdateien und eine Checkliste für Notfälle. Wenn unter Last etwas kippt, ziehe ich zuerst den Query/Objekt-Cache und lasse Page Cache + OPcache arbeiten. Danach:
- Flush gezielt statt global: Nur betroffene Keys löschen, nicht das komplette Redis entleeren.
- WP-CLI nutzen:
Vorher Metriken sichern, danach erneut messen.wp cache flush wp transient delete --all - Logs rotieren und Slow-Query-Log auswerten, statt am Cache-Regler zu drehen.
Kurzbilanz: Was ich einstelle und warum
Ich schalte den WordPress Query Cache ab, weil Invalidierung, Latenz und Inkonsistenz den theoretischen Gewinn auffressen. Stattdessen setze ich auf Page Caching, OPcache und saubere Datenbankarbeit, die konstant und ohne Überraschungen liefert. Redis nutze ich selektiv, wenn ein klarer Hotspot existiert und genügend RAM bereitsteht. Wer objektiv misst, erkennt schnell: Gut strukturierte Queries, starke Hostressourcen und HTML-Caching bringen die ruhigen, schnellen Antworten, die jede Site braucht. So halte ich Performance planbar, spare Serverkosten in Euro und vermeide Fehlerbilder, die sich mit keinem Query Cache verlässlich abfangen lassen.


