Langsame WordPress-Datenbankabfragen finden und beheben

Avatar of Andy Adams
Andy Adams am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Langsame SQL-Abfragen können die Leistung Ihrer WordPress-Website beeinträchtigen. Manchmal sind langsame Abfragen das Ergebnis schlecht formulierter SQL-Befehle, die nie so hätten ausgeführt werden sollen. Und manchmal waren langsame Abfragen eigentlich einmal schnelle Abfragen – aber mit zunehmendem Alter der Website wurden die Abfragen immer langsamer und konnten mit der wachsenden Datenbank nicht mehr Schritt halten.

Unabhängig davon, wie Ihre SQL-Abfragen langsam geworden sind, werfen wir einen Blick auf einige Möglichkeiten, problematische Abfragen in WordPress zu finden und zu beheben.

Langsame Abfragen finden

Das Finden der Quelle langsamer Abfragen besteht aus 2 Schritten

  1. Identifizieren, welche Abfragen tatsächlich die langsamen sind.
  2. Finden des Codes, der sie generiert und ausführt.

Schauen wir uns zwei Plugins und einen SaaS an, die uns bei der Suche nach langsamen Abfragen helfen können.

Query Monitor

Query Monitor ist ein Plugin, das *jede Menge* Informationen über die aktuelle Seite liefert. Neben einer ganzen Reihe von Informationen über die internen Abläufe von WordPress bietet es eine detaillierte Aufschlüsselung von

  • Wie viele Abfragen bei dieser Anfrage stattgefunden haben
  • Welche Abfragen auf der Seite am längsten gedauert haben
  • Welche Funktionen die meiste Zeit mit SQL-Abfragen verbracht haben
  • Ob diese Abfragen von Plugins, Themes oder dem WordPress-Kern stammten
Query Monitor

Query Monitor identifiziert sogar langsame Abfragen mit beängstigend roten Texten, was es super einfach macht, die problematische SQL herauszufiltern.

Red, scary SQL

Debug Bar

Ein weiteres hervorragendes Werkzeug zum Auffinden von quälend langsamer SQL ist das alte, bewährte Debug Bar Plugin. Debug Bar liefert Ihnen Informationen über die internen Abläufe von WordPress, wenn Sie eine Seite laden, mit Dingen wie

  1. WP_Query-Parameter
  2. Anfrageinformationen (einschließlich des Abgleichs von Rewrite-Regeln)
  3. Von der aktuellen Seite generierte SQL-Abfragen

Um Punkt 3 (SQL-Tracking) in Debug Bar zu aktivieren, stellen Sie sicher, dass SAVEQUERIES irgendwo auf Ihrer Website aktiviert ist – wahrscheinlich in wp-config.php – wie folgt:

if ( ! defined( 'SAVEQUERIES' ) ) {
  define( 'SAVEQUERIES', true );
}

Warnung: SAVEQUERIES hat Auswirkungen auf die Leistung Ihrer Website und sollte wahrscheinlich nicht auf einem Produktionsserver verwendet werden. Verwenden Sie es stattdessen auf einer Entwicklungsumgebung.

Das Finden langsamer SQL ist mit Debug Bar nicht ganz einfach. Zum Beispiel liefert es keine sortierbaren Tabellen oder hebt langsame Abfragen für Sie hervor. Was Debug Bar jedoch bietet, ist ein Funktions-Trace, der Sie *genau* dorthin führt, wo Sie die Quelle einer Abfrage finden.

Debug Bar function list

Dies ist eine Liste der geladenen Dateien und der Funktionen, die zur Ausführung der Abfrage führen. Meistens interessiert man sich für den letzten Eintrag in der Liste; hier wurde die langsame Abfrage ausgeführt und hier sollten Sie Ihre Suche beginnen. Das Praktische daran, den Kontext jeder einzelnen Funktion zu haben, die zu dieser Abfrage führt, ist, dass sie Aufschluss darüber geben kann, warum die SQL überhaupt ausgeführt wurde.

NewRelic

NewRelic ist ein Dienst, der die Leistung Ihrer Web-App, einschließlich WordPress, misst und überwacht. Der Dienst liefert *eine Tonne* Informationen über die Leistung Ihrer Website. Es ist leicht, sich in den Daten zu verlieren, die NewRelic liefert, von detaillierter Codeausführung bis hin zu zeilenweisen Aufschlüsselungen für SQL-Abfragen.

NewRelic UI

Es gibt zwei Hauptunterschiede zwischen NewRelic und den zuvor erwähnten Plugins

  1. NewRelic liefert viel mehr Details über die Leistung Ihres PHP, bis hin zur Anzahl der Millisekunden, die in jeder Funktion verbracht werden
  2. NewRelic verfolgt jede Anfrage an Ihre Website im Hintergrund, sodass Sie sie später zur Suche nach langsamen SQL-Abfragen heranziehen können. Die Plugins liefern Ihnen nur die aktuelle Seite.

Es ist erwähnenswert, dass NewRelic eine kostenlose Tarifstufe hat, die allgemeine Informationen über die Leistung Ihrer Website liefert, aber Sie müssen auf einen kostenpflichtigen Tarif upgraden, um die Extras für die Überwachung einzelner Anfragen und das Auffinden langsamer Abfragen zu erhalten.

Eine langsame Abfrage mit EXPLAIN verstehen

Bisher haben wir Tools zum Finden langsamer Abfragen behandelt. Lassen Sie uns nun herausfinden, *warum* diese Abfragen die Dinge verlangsamen.

Das MySQL-Schlüsselwort EXPLAIN kann helfen, zu erklären, was passiert. Das Hinzufügen von EXPLAIN am Anfang einer Abfrage zeigt, wie MySQL eine Abfrage ausführt. Bei komplizierten Abfragen kann EXPLAIN helfen, langsame Punkte in Ihren SQLs zu identifizieren, wie z. B. langsame Unterabfragen oder ineffiziente Operationen.

Zum Beispiel, wenn Sie eine Abfrage hätten, die so aussieht:

SELECT slow_column FROM slow_table

Sie könnten diese Abfrage mit folgendem einfach mit EXPLAIN erklären:

EXPLAIN SELECT slow_column FROM slow_table

Hier ist, wie die Ausgabe von EXPLAIN in phpMyAdmin aussieht:

MySQL EXPLAIN

Ich gebe zu, ich verstehe nicht alle internen Abläufe von MySQL, aber das Ausführen von EXPLAIN auf Abfragen liefert dennoch Einblicke, wie MySQL meine SQL ausführt. Verwendet die Abfrage einen Index? Scannt sie die gesamte Tabelle? Selbst bei einfachen Abfragen liefert EXPLAIN einen kleinen Einblick, um zu verstehen, was vor sich geht.

Sie können EXPLAIN von der MySQL-Kommandozeile oder Ihrem bevorzugten MySQL-Tool aus ausführen.

Langsame Abfragen beheben

Nachdem wir nun wissen, dass unsere Abfrage langsam ist und EXPLAIN uns mitgeteilt hat, *warum* sie es ist, schauen wir uns einige Optionen zur Behebung dieser trägen Probleme an.

Option 1: Die Abfrage ändern

Auf CSS-Tricks hatten wir eine Abfrage, die den Bearbeitungsbildschirm für Beiträge auf Schneckentempo verlangsamte. Die Abfrage war Teil der Custom Fields-Metabox. Hier ist die SQL:

SELECT meta_key
FROM wp_postmeta
GROUP BY meta_key
HAVING meta_key NOT LIKE '\\_%'
ORDER BY meta_key
LIMIT 100

Dieser spezielle Teil der SQL-Abfrage ruft eine Liste von meta_keys aus der Tabelle wp_postmeta ab, die nicht mit einem Unterstrich (_) beginnen. Die GROUP BY-Klausel bedeutet, dass jedes Ergebnis eindeutig ist.

Wenn wir diese Abfrage 5 Mal ausführen, hier ist, wie lange sie dauert:

1,7146 Sek.
1,7912 Sek.
1,8077 Sek.
1,7708 Sek.
1,8456 Sek.

Könnten wir eine andere Abfrage schreiben, um dasselbe Ergebnis zu erzielen? Wir müssen *eindeutige* meta_keys auswählen. Eindeutig ist ein Synonym für *distinct*, was zufällig eine SQL-Anweisung ist!

Mit der DISTINCT-Anweisung können wir Folgendes tun:

SELECT DISTINCT meta_key 
FROM wp_postmeta 
WHERE meta_key NOT LIKE '\\_%' 
ORDER BY meta_key

Wenn wir unsere umgeschriebene Abfrage ein paar Mal ausführen, erhalten wir folgende Ergebnisse:

0,3764 Sek.
0,2607 Sek.
0,2661 Sek.
0,2751 Sek.
0,2986 Sek.

Dies ist kaum ein wissenschaftlicher Vergleich, zeigt aber eine deutliche Verbesserung!

Option 2: Einen Index hinzufügen

Wenn Sie eine SQL-Abfrage auf einer Standard-MySQL-Tabelle ausführen, muss MySQL die gesamte Tabelle durchsuchen, um herauszufinden, welche Zeilen für diese spezielle Abfrage relevant sind. Wenn Ihre Tabelle *wirklich groß* wird, dauert dieses Scannen eine ganze Weile.

Dafür sind MySQL-Indizes da. Indizes nehmen die Daten einer Tabelle und organisieren sie so, dass Daten *viel* leichter gefunden werden können. Durch die Organisation der Daten auf eine bestimmte Weise helfen Indizes dabei, die Menge an Scans zu reduzieren, die MySQL für jede Abfrage durchführt.

Indizes können zu einzelnen Spalten oder über mehrere Spalten hinweg hinzugefügt werden. Die Syntax sieht so aus:

CREATE INDEX wp_postmeta_csstricks ON wp_postmeta (meta_key)

Mit einem Index auf meta_key sieht die Zeit der ursprünglichen SQL-Abfrage so aus:

0,0042 Sek.
0,0024 Sek.
0,0031 Sek.
0,0026 Sek.
0,0020 Sek.

Das ist wirklich blitzschnell!

Ein Wort der Vorsicht zu Indizes: Jedes Mal, wenn INSERT eine Zeile erstellt oder UPDATE auf einer indizierten Tabelle verwendet wird, wird der Index neu berechnet, was eine kostspielige Operation sein kann. Indizes machen das *Lesen* aus der Tabelle schneller, aber das *Schreiben* in die Tabelle ist langsamer. Ein gut platzierter Index kann Ihre Abfragen beschleunigen, aber übertreiben Sie es nicht mit den Indizes, ohne die Gesamtauswirkungen des Indexes auf Ihre Datenbank zu überwachen.

Option 3: Abfrageergebnisse cachen

Wir wissen, dass wir eine langsame Abfrage haben. Anstatt die Abfrage zu ändern, was wäre, wenn wir einfach die *Ergebnisse* der Abfrage speichern würden? Auf diese Weise würden wir begrenzen, wie oft die Abfrage ausgeführt wird, und wir bekämen die meiste Zeit einen „Freifahrtschein“.

Um die Abfrage zu cachen, könnten wir die WordPress Transients API verwenden. Transients werden zum Speichern von Ergebnissen von teuren Operationen verwendet, wie z. B.:

  • Anfragen an externe Websites (z. B. Abrufen aktueller Facebook-Posts)
  • Langsame Verarbeitungsteile (z. B. Suchen nach großen Zeichenketten mit einem regulären Ausdruck)
  • Langsame Datenbankabfragen!

Das Speichern eines Abfrageergebnisses mit Transients sieht ungefähr so aus:

if ( false === ( $results = get_transient( 'transient_key_name' ) ) ) {
  $results = ...; // Do the slow query to get the results here
  // 60 * 60 is the expiration in seconds - in this case, 3600 seconds (1 hour)
  set_transient( 'transient_key_name', $results, 60 * 60 ); 
}

Das Speichern von Abfrageergebnissen in einem Transient wie diesem bedeutet, dass die Abfrage nur etwa einmal pro Stunde ausgeführt wird. Das führt uns zur *GROSSEN TRANSIENT-WARNUNG*: **Seien Sie vorsichtig mit Transients für Dinge, die sich häufig ändern**.

Wenn Sie eine Abfrage mit Ergebnissen haben, die sich nicht oft ändern, ist die Verwendung von Transients eine raffinierte Möglichkeit, die Datenbank nicht so häufig abfragen zu müssen.

Auswahl eines Ansatzes

Wir haben drei Optionen skizziert, und es gibt wahrscheinlich 17 weitere Möglichkeiten, dieses Problem mit langsamen Abfragen zu lösen. Welchen Ansatz wählen wir?

Wenn ich an Code arbeite, der nicht mein eigener ist, folge ich lieber dem Motto des Programmierers: „Mach das einfachste, was funktionieren kann.“

Option 1 (Neufassung der Abfrage) lieferte hervorragende Ergebnisse, aber was ist, wenn die neu formulierte Abfrage nicht immer die gleichen Ergebnisse liefert? Wir könnten unwissentlich unseren Code mit einer leicht fehlerhaften Abfrage ruinieren.

Option 2 (Hinzufügen eines Indexes) ist nicht immer möglich, abhängig von der Tabelle und den Spalten, die von der Abfrage verwendet werden. Im Fall von WordPress-Core-Tabellen müssten Sie sich Sorgen über Nebenwirkungen von Indizes machen:

  • Erwartet die Core-Update-Routine zusätzliche Indizes?
  • Verlangsamt das Hinzufügen eines Indexes andere Abfragen, wie z. B. INSERT und UPDATE?

Option 3 (Caching der Ergebnisse über Transients) hat minimale Auswirkungen – wir ändern die ursprüngliche Abfrage nicht und müssen die Datenbankstruktur nicht ändern.

Meistens verwende ich Option 3. In Ihrem speziellen Fall könnten Sie eine andere Option wählen, abhängig von der Abfrage, die Sie beheben, oder der spezifischen Website, die SQL-Probleme hat. Es gibt keine Einheitslösung für die meisten Leistungsprobleme, daher können Sie meiner Wahl widersprechen oder alle drei gleichzeitig ausprobieren!

Bleiben Sie dran

Wir haben hier ein echtes Problem beschrieben. Die Custom Fields-Box auf CSS-Tricks war tatsächlich ein Übeltäter für einige sehr langsame Datenbankabfragen. Wir haben auch verschiedene Wege zu potenziellen Lösungen skizziert, aber wir haben Ihnen keine tatsächliche codierte Lösung geliefert. Wir werden bald einen zweiten Beitrag dazu veröffentlichen, der Ihnen hoffentlich die Werkzeuge für die Behebung Ihrer eigenen langsamen Abfragen an die Hand gibt, nachdem Sie sie entdeckt haben.