Vorher
Alle sagen, der Shop sei langsam; niemand weiß, welche Abfrage und welche Datei.
Nachher
Eine Aufzeichnungssitzung, dann ein Satz: Diese Anweisung läuft so oft pro Aufruf, und das hier hat sie angefordert.
„Der Shop ist langsam“ ist kein Fehlerbericht. Sie starten eine Sitzung, rufen die Seiten auf, die sich langsam anfühlen, und das Plugin sagt Ihnen, welche Anweisung wie oft läuft und welche Datei sie angefordert hat. Außerhalb einer Diagnosesitzung registriert es keinen einzigen Hook im Request-Pfad und fügt keine Abfragen hinzu; die Sitzung läuft von selbst ab. Die Pro-Stufe ergänzt sieben fertige Indizes — jeder mit Begründung und Kosten —, einen Cache für die Bestellzählung und Warnungen bei langsamen Abfragen.
Ein Shop, der seit einigen Jahren läuft, wird langsam, und der Grund liegt fast immer in der Datenbank: postmeta ist gewachsen, options ist voller autoload-Zeilen, die bei jedem einzelnen Request gelesen werden, und irgendwo setzt eine Schleife eine Abfrage pro Produkt ab. Solange niemand nachsieht, hat nichts davon einen Namen und eine Adresse.
Werkzeuge, die das beantworten, gibt es, und sie teilen einen Mangel: Sie müssen laufen, um etwas zu sehen, und im Betrieb kosten sie bei jeder Abfrage Arbeitsspeicher. Deshalb tragen sie eine Warnung vor dem Produktivbetrieb, werden mitten in einem Vorfall eingeschaltet und bleiben danach eingeschaltet.
Dieses Plugin ist andersherum gebaut. WordPress behält Abfragen nur, wenn die Konstante SAVEQUERIES definiert ist, und wpdb liest diese Konstante bei jeder Abfrage — sie muss also vor der ersten Abfrage von WordPress definiert sein, lange bevor sich fragen lässt, wer angemeldet ist. Die Antwort ist ein zweistufiges Tor. Beim Laden des Plugins: Trägt das Sitzungs-Cookie ein gültiges Token, wird SAVEQUERIES definiert, und die Abfragen sammeln sich nur im Arbeitsspeicher. Bei shutdown, wenn WordPress endlich weiß, wer die Benutzerin ist: Die Berechtigung manage_options, die Inhaberschaft der Sitzung und das Token werden erneut geprüft, und fällt eine dieser Prüfungen durch, wird der Puffer verworfen und keine einzige Zeile geschrieben. Außerhalb einer Sitzung registriert das Plugin keinen Hook im Request-Pfad und fügt keine Abfragen hinzu.
Auch die Ausgabe ist eine Deutung und keine Liste. Vierhundert Zeilen SQL sind Daten. Ein Befund ist dies: „Diese Anweisung läuft 312-mal pro Seitenaufruf, aus class-wc-product.php“, dazu ein konkreter WooCommerce-Hinweis — den Cache einmal mit update_meta_cache() füllen, statt die Meta jedes Produkts einzeln zu lesen. Jeder Befund trägt eine Schwere, damit Sie wissen, was zuerst drankommt und was warten kann.
Weil dieses Plugin an der Datenbank eines Shops arbeitet, ist Datenschutz Teil des Entwurfs und keine Schlussbemerkung: Anweisungen, die users, usermeta, options und die WooCommerce-Tabellen für Bestellungen, Sitzungen, API-Schlüssel und Zahlungstoken berühren, werden niemals wörtlich gespeichert. Die normalisierte Form — in der kein einziger echter Wert steht — wird wie üblich behalten, die Option „Beispielanweisung speichern“ ist standardmäßig aus, und das Beispiel erscheint nie in einer REST-Antwort, weil keine Ansicht es anzeigt.
Vorher
Alle sagen, der Shop sei langsam; niemand weiß, welche Abfrage und welche Datei.
Nachher
Eine Aufzeichnungssitzung, dann ein Satz: Diese Anweisung läuft so oft pro Aufruf, und das hier hat sie angefordert.
Vorher
Ein Profiler muss laufen, um etwas zu sehen, und im Betrieb kostet er bei jeder Abfrage Arbeitsspeicher.
Nachher
Außerhalb einer Sitzung wird nichts im Request-Pfad registriert und keine Abfrage hinzugefügt. Die Sitzung läuft von selbst ab.
Vorher
Die Ausgabe sind vierhundert Zeilen SQL, und die Deutung ist Ihr Problem.
Nachher
Abfragen derselben Form werden zu einer Zeile mit Zähler, und der Reiter Befunde sagt, welche Zeile zuerst drankommt.
Wählen Sie im Reiter Aufzeichnen eine Dauer — voreingestellt dreißig Minuten, höchstens zweihundertvierzig — und vergeben Sie ein Etikett, etwa „langsame Produktseite“.
Öffnen Sie in einem zweiten Tab genau die Seiten, die sich langsam anfühlen. Solange eine Sitzung läuft, sitzt ein Hinweis in der Adminleiste, damit sie niemand unbemerkt laufen lässt.
Kommen Sie in diesen Tab zurück. Jeder Befund hat eine Schwere, einen Satz in klarer Sprache und einen WooCommerce-spezifischen Hinweis. Die Sitzung endet von selbst, falls Sie es vergessen.
WooCommerce-Shops mit einigen Jahren Daten · Shops mit Tausenden Produkten oder Bestellungen · Shops, deren Adminbereich langsam geworden ist · Teams, die bei der Umstellung auf HPOS etwas verloren haben · Entwicklerinnen, die das verantwortliche Plugin benennen müssen · Agenturen, die den Shop einer Kundin betreuen · Hoster, die zeigen müssen, dass es nicht am Server liegt · alle, die aus „der Shop ist langsam“ einen Fehlerbericht machen müssen.
Alles steckt im Plugin selbst. Kein Cloud-Dienst, kein Composer und keine einzige zusätzliche Abfrage, solange die Aufzeichnung aus ist.
Bis Sie eine Sitzung starten, wird nichts aufgezeichnet. Außerhalb registriert das Plugin keinen Hook im Request-Pfad und fügt keine Abfragen hinzu. Das Sitzungstoken liegt in einem HttpOnly-Cookie mit absolutem Ablauf und erscheint nie in einer REST-Antwort.
Statt vierhundert Zeilen ein Satz: „Diese Anweisung läuft 312-mal pro Seitenaufruf, aus class-wc-product.php“ — mit Schwere und einem WooCommerce-spezifischen Hinweis für Meta-, Bestell- und Term-Muster.
Läuft eine Form innerhalb eines Seitenaufrufs viele Male, ist das die Schleife, die Zeile für Zeile nachlädt. Das Plugin holt die verantwortliche Datei aus dem Call Stack und überspringt die Frames von WordPress selbst. Der Schwellenwert liegt bei fünf Wiederholungen und ist einstellbar.
Literale werden durch ? ersetzt, damit Abfragen, die sich nur in einer ID unterscheiden, zu einer Zeile mit Zähler werden. Zeichenketten in Anführungszeichen werden vor blanken Zahlen ersetzt, und Zahlen innerhalb von Bezeichnern bleiben unangetastet — wp_woocommerce_order_items bleibt also heil, und eine IN-Liste beliebiger Länge fällt auf eine Form zusammen.
Tabellengrößen aus information_schema — ohne COUNT(*), das auf einer großen Tabelle selbst langsam ist —, das Gewicht der autoload-Optionen mit Warnschwelle und Zählungen verwaister Zeilen: Post-Meta, Term-Meta, Term-Beziehungen, Bestellpositionen, Positions-Meta und abgelaufene Sitzungen. Er zählt; er löscht nichts aus Ihrem Shop.
Inspektor und Indexer prüfen beide, ob der Shop Bestellungen in wc_orders hält oder noch in posts, und sehen entsprechend an der richtigen Stelle nach.
Sieben fertige Indizes für die WordPress- und WooCommerce-Tabellen — darunter das Paar meta_key/meta_value auf postmeta und auf dem Positions-Meta sowie das Paar Status und Datum auf der HPOS-Bestelltabelle. Jeder erklärt vor dem Anlegen, warum es ihn gibt und was er ungefähr kostet, und warnt, wenn die Tabelle groß ist.
Jeder angelegte Index wird vermerkt, und das Entfernen verweigert jeden Index, den das Plugin nicht selbst angelegt hat. Der Besitzanspruch wird geschrieben, bevor ALTER TABLE läuft — läuft PHP auf einer großen Tabelle mitten hinein in einen Timeout, bleibt kein herrenloser Index zurück.
WooCommerce berechnet die Bestellzahlen je Status auf fast jeder Adminseite neu. Dieses Modul cacht sie und invalidiert über einen Generationszähler statt über das Löschen von Schlüsseln — und ist aktuell, sobald sich eine Bestellung ändert.
Eine stündliche Prüfung, die per E-Mail meldet oder an einen Webhook sendet, wenn langsame Abfragen in einem gerade unbeobachteten Shop zunehmen — mit einer Abkühlzeit, damit aus einer langsamen Woche nicht hundert gleiche Nachrichten werden.
Anweisungen, die users, usermeta, options und die WooCommerce-Tabellen für Bestellungen, Sitzungen, API-Schlüssel und Zahlungstoken berühren, werden ausschließlich in normalisierter Form behalten, was auch immer die Einstellungen sagen. „Beispielanweisung speichern“ ist standardmäßig aus, und mit dem Filter hd_wcdbp_never_sample_tables ergänzen Sie eigene sensible Tabellen.
Webhook-Rümpfe werden mit HMAC-SHA256 signiert, sobald ein Schlüssel gesetzt ist. Loopback-, Link-Local-, private und Cloud-Metadaten-Adressen werden abgelehnt: Hostnamen werden vor der Entscheidung aufgelöst, und die Verbindung wird auf genau die geprüfte Adresse festgelegt. Der Schlüssel wird aus jeder REST-Antwort entfernt und als reines Schreibfeld dargestellt.
Alle in docs/hooks.md, jeder mit Beispiel. Darunter hd_wcdbp_snapshot, um eine Aufzeichnung zu verwerfen, hd_wcdbp_never_sample_tables für eigene sensible Tabellen, hd_wcdbp_index_recipes für einen eigenen Index und hd_wcdbp_is_licensed für einen Staging-Klon, der nicht nach Hause telefonieren soll.
Eine vollständige fa_IR-Übersetzung liegt bei, und der Adminbereich lädt admin-rtl.css, wenn is_rtl() gilt. SQL und Dateipfade sind mit dir="ltr" ausgezeichnet, damit sie in einem rechtsläufigen Adminbereich lesbar bleiben. Kein jQuery — reines ES6, geladen nur auf der eigenen Seite des Plugins.
| Funktion | Beschreibung | Enthalten |
|---|---|---|
| Auf die Sitzung begrenzte Aufzeichnung | Ein 64-stelliges Token in einem HttpOnly-Cookie mit absolutem Ablauf | |
| Zweistufiges Tor um SAVEQUERIES | Das Cookie erlaubt nur das Sammeln im Arbeitsspeicher; die Berechtigung wird bei shutdown geprüft | |
| Keine Hooks im Request-Pfad | Außerhalb einer Sitzung wird nichts registriert und keine Abfrage hinzugefügt | |
| Fingerabdruck der Abfrageform | Literale durch ? ersetzt; Tabellen- und Spaltennamen bleiben unangetastet | |
| IN-Listen fallen zusammen | Eine IN-Liste beliebiger Länge wird zu einer einzigen Form | |
| N+1-Erkennung | Mit verantwortlicher Datei aus dem Call Stack, Framework-Frames übersprungen | |
| Einstellbare Langsamkeitsschwelle | Voreingestellt 0,05 Sekunden, über hd_wcdbp_slow_threshold | |
| Einstellbare Wiederholungsschwelle | Voreingestellt fünf Wiederholungen in einem Seitenaufruf | |
| Befunde mit Schwere | Hoch, mittel oder niedrig, jeweils mit einem Satz in klarer Sprache | |
| WooCommerce-spezifische Hinweise | Etwa update_meta_cache() für das postmeta-Muster | |
| Die Liste der Aufzeichnungen | Neueste zuerst, langsamste zuerst oder alles aus einem Kontext | |
| Kontext jedes Requests vermerkt | frontend, admin, ajax, rest, cron oder cli | |
| Aufbewahrungsfenster und harte Obergrenze | Eine Sitzung auf einer stark besuchten Seite füllt sich schnell | |
| Obergrenze für Formen je Request | Eine Seite mit mehr als zweihundert hat ein größeres Problem | |
| Tabellengrößen aus information_schema | Ohne COUNT(*) auf einer großen Tabelle | |
| Gewicht der autoload-Optionen | Mit Warnschwelle und den zwanzig größten aufgelistet | |
| Zählung verwaister Zeilen | Post-Meta, Term-Meta, Term-Beziehungen, Bestellpositionen, Positions-Meta und abgelaufene Sitzungen | |
| HPOS-Bewusstsein | Im Inspektor und im Indexer | |
| Inspektor-Scan gecacht und unter Sperre | Zwei Admins gleichzeitig, ein Scan | |
| Sieben fertige Indizes | postmeta, autoload, Positions-Meta, Positionstyp, HPOS-Status und -Datum, HPOS-Meta, Sitzungsablauf | |
| Begründung und Kosten vor dem Anlegen | Dazu eine Warnung, wenn die Tabelle groß ist | |
| Besitzanspruch vor ALTER TABLE | Ein PHP-Timeout kann keinen herrenlosen Index hinterlassen | |
| Rollback nur auf eigene Indizes | Ein Index von Ihnen oder einem anderen Plugin wird nie angefasst | |
| Rezeptfelder erneut geprüft | Ein striktes Muster vor jedem ALTER TABLE | |
| Cache der Bestellzahlen je Status | Genau das, was WooCommerce auf fast jeder Adminseite neu berechnet | |
| Invalidierung per Generationszähler | Statt durch Löschen von Cache-Schlüsseln | |
| Warnungen bei langsamen Abfragen | E-Mail oder Webhook, auf stündlicher Prüfung | |
| HMAC-SHA256-Signatur am Webhook | Sobald ein Schlüssel hinterlegt ist | |
| Abkühlzeit für Warnungen | Eine langsame Woche wird nicht zu hundert gleichen Nachrichten | |
| Adressprüfung für den Webhook | Loopback, private Bereiche und Cloud-Metadaten abgelehnt, festgelegt auf die geprüfte Adresse | |
| Sensible Tabellen ohne Beispiel | users, usermeta, options und die WooCommerce-Tabellen für Bestellungen, Sitzungen, API-Schlüssel und Zahlungstoken | |
| Aufgezeichnete URLs bereinigt | password, pwd, key, token und _wpnonce werden aus dem Query-String entfernt | |
| Deinstallation entfernt die Indizes | uninstall.php entfernt immer die von diesem Plugin angelegten Indizes |
| Punkt | Minimum |
|---|---|
| WordPress | 6.2 oder neuer, getestet bis 6.8 |
| PHP | 8.0 bis 8.3 |
| WooCommerce | Erforderlich — die Befunde und die Index-Rezepte sind für die Tabellen von WooCommerce geschrieben |
| Datenbank | MySQL oder MariaDB; der Inspektor liest die Tabellengrößen aus information_schema |
| Berechtigung | manage_options zum Starten einer Sitzung und zum Lesen der Ergebnisse, änderbar über hd_wcdbp_manage_capability |
Für einen Shop, der seit einigen Jahren läuft, langsam geworden ist und bei dem niemand genau weiß, warum. Wenn Sie Tausende Produkte und Bestellungen haben und der Adminbereich oder die Produktseite zäh geworden ist, gibt dieses Plugin dem Problem einen Namen und eine Adresse. Für einen frisch gestarteten Shop mit hundert Produkten hat es wenig zu sagen — dort liegt es meist nicht an der Datenbank. Das zweite Publikum sind Entwicklerinnen und Agenturen, die einer Kundin zeigen müssen, welches Plugin oder welches Theme verantwortlich ist — mit einem Dateinamen statt einer Vermutung.
Genau dafür wurde es gebaut. Außerhalb einer Diagnosesitzung registriert das Plugin keinen Hook im Request-Pfad und fügt keine Abfragen hinzu. Während einer Sitzung entspricht der Aufwand dem von WordPress' eigenem SAVEQUERIES, und er endet, wenn die Uhr abgelaufen ist. Das Sitzungs-Cookie allein erlaubt auch niemandem, die Ergebnisse zu lesen; dafür ist weiterhin manage_options nötig. Das Einzige, was Ihre Datenbank wirklich verändert, ist der Pro-Indexer, und der läuft nie von selbst: Sie drücken einen Knopf, bestätigen, und Sie können es zurücknehmen.
Bei ausgeschalteter Aufzeichnung registriert das Plugin auf gewöhnlichen Requests keinen Hook; die Cookie-Prüfung kehrt vor jeder Abfrage zurück und rührt die Datenbank nicht an. Admin-CSS und -JS laden nur auf der eigenen Seite des Plugins. Während einer Sitzung kostet die Aufzeichnung bei jeder Abfrage Arbeitsspeicher — dasselbe, was SAVEQUERIES tut —, und die Auswertung des Snapshots geschieht bei shutdown, nie während die Seite aufgebaut wird. Wie hoch der Aufwand genau ausfällt, hängt von Ihrem Shop ab, und wir erfinden keine Zahl, die wir auf Ihrer Website nicht gemessen haben.
Beide Hälften, beide aktiv. Diagnose: auf die Sitzung begrenzte Aufzeichnung, Gruppierung nach Abfrageform, N+1-Erkennung mit Dateiname, die Ansicht Befunde und der Datenbank-Inspektor. Reparatur: der smarte Indexer mit sieben fertigen Indizes und sicherem Rollback, der Cache für die Bestellzählung und die Warnungen bei langsamen Abfragen per E-Mail oder signiertem Webhook.
Das kann es, und wir verschweigen es nicht. Je nach MySQL- oder MariaDB-Version und Tabellengröße kann ein ALTER TABLE eine Weile dauern, und Schreibzugriffe auf diese Tabelle können währenddessen langsamer oder blockiert sein. Deshalb zeigt das Plugin vor dem Anlegen die Tabellengröße und geschätzte Kosten je Index, warnt bei großen Tabellen und beginnt nie von selbst. Die Empfehlung ist schlicht: eine ruhige Stunde und ein frisches Backup. Läuft PHP mitten hinein in einen Timeout, bleibt kein herrenloser Index zurück, weil der Besitzanspruch vor der Anweisung geschrieben wird.
Mit beidem. Das Plugin prüft, ob der Shop Bestellungen in wc_orders hält oder noch in posts, und zählt und empfiehlt entsprechend. Die Rezepte decken beide Fälle ab: das Paar meta_key/meta_value auf postmeta und auf dem Positions-Meta für das alte Layout, und das Paar Status und Datum samt HPOS-Meta für das neue.
Sie werden entfernt. uninstall.php entfernt immer die Indizes, die dieses Plugin angelegt hat, unabhängig davon, ob die Option „Daten löschen“ an ist. Schemaänderungen zurückzulassen, nachdem das Plugin, das sie gemacht hat, verschwunden ist, wäre nicht zu vertreten — später wüsste niemand, was dieser Index ist und ob man ihn gefahrlos entfernen darf. Ein Index, den Sie selbst oder ein anderes Plugin angelegt hat, wird nie angefasst.
Die normalisierte Abfrageform enthält keine echten Werte und ist deshalb immer unbedenklich. Echte Werte enthalten kann nur das Beispiel — eine Ausfertigung der Anweisung genau so, wie sie lief. Anweisungen, die users, usermeta, options und die WooCommerce-Tabellen für Bestellungen, Sitzungen, API-Schlüssel und Zahlungstoken berühren, werden nie mit Beispiel gespeichert, was auch immer die Einstellungen sagen. Die Beispiel-Option selbst ist standardmäßig aus, das Beispiel erscheint nie in einer REST-Antwort, weil keine Ansicht es anzeigt, und aus jeder aufgezeichneten URL werden password, pwd, key, token und _wpnonce vor dem Speichern entfernt.
Weil kein Plugin sie sehen kann. WordPress führt während des Bootstraps Abfragen aus, bevor eine einzige Plugin-Datei eingebunden ist, und SAVEQUERIES müsste vorher definiert sein, um sie zu erfassen. Das Plugin schreibt das auf den Bildschirm, statt eine unvollständige Liste als vollständig auszugeben. Wenn Sie diese ebenfalls brauchen, müssen Sie SAVEQUERIES in wp-config.php definieren — in einem Live-Shop keine gute Idee.
Das Plugin rührt Ihre Entscheidung nicht an und nutzt sie einfach. Dafür sagt es Ihnen auf dem Bildschirm klar, dass in diesem Zustand jeder Request der Website aufgezeichnet wird und nicht nur Ihre Sitzung — was ein Live-Shop in aller Regel nicht möchte.
Nein. Das Plugin bringt einen eigenen kleinen Autoloader mit und hat zur Laufzeit keine Abhängigkeiten; es werden keine Daten an einen Cloud-Dienst gesendet. Der einzige ausgehende Aufruf ist die Lizenzprüfung und Aktualisierung der Pro-Stufe gegen hatamidev.com; der Updater akzeptiert nur eine Paket-URL auf hatamidev.com über https, sofern Sie nicht selbst einen Host über hd_wcdbp_update_hosts ergänzen. Für einen Staging-Klon, der nicht nach Hause telefonieren soll, gibt es den Filter hd_wcdbp_is_licensed.
Das entscheiden Sie. In den Einstellungen gibt es eine Option zum Löschen der Daten, und sie ist standardmäßig aus; schalten Sie sie ein, entfernt das Löschen des Plugins beide Aufzeichnungstabellen und sämtliche Einstellungen. Die Indizes sind die Ausnahme und werden immer entfernt. Das Deaktivieren räumt geplante Ereignisse ab und beendet eine laufende Sitzung, rührt gespeicherte Daten aber nie an.
Fehlerbehebung im Plugin, Hilfe bei Installation und Einrichtung sowie Unterstützung beim Lesen der Befunde und bei Entscheidungen über Indizes. Die Optimierung des Shops selbst, das Umschreiben des Codes eines anderen Plugins, das sich als verantwortlich erweist, und Probleme, die von anderen Themes oder Plugins verursacht werden, liegen außerhalb des Supports und werden separat vereinbart.