MySQL Slow Query Log: langsame Abfragen finden

Das Slow Query Log schreibt jede Abfrage mit, die länger als long_query_time braucht — in MySQL und MariaDB liegt die Vorgabe bei 10 Sekunden, der Minimalwert bei 0, und der Wert darf Dezimalstellen haben. Einschalten geht im laufenden Betrieb mit SET GLOBAL, auswerten mit mysqldumpslow; wer nichts schreiben will, bekommt dieselbe Antwort aus performance_schema.events_statements_summary_by_digest. Stand: Oktober 2026, alle Namen und Vorgabewerte am 22. Oktober 2026 im MySQL-8.4-Referenzhandbuch und in der MariaDB-Serverdokumentation geprüft.

Einschalten, ohne den Server neu zu starten

Das Protokoll ist laut Handbuch standardmäßig aus. Diese drei Anweisungen schalten es ein, legen die Datei fest und setzen die Schwelle auf eine halbe Sekunde:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';
SET GLOBAL long_query_time = 0.5;

SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';

Die Falle steckt in der dritten Zeile. long_query_time hat den Gültigkeitsbereich „Global und Session“, und eine Sitzungsvariable wird beim Verbindungsaufbau aus dem globalen Wert gefüllt. Ihre Konsole, die vor dem SET GLOBAL schon offen war, arbeitet deshalb weiter mit 10 Sekunden — ein Verbindungspool unter Umständen stundenlang. Neu verbinden. Und der Dateipfad muss dem Benutzer gehören, unter dem der Server läuft.

Dauerhaft eintragen

SET GLOBAL überlebt keinen Neustart. Dauerhaft gilt nur, was im Abschnitt [mysqld] der Konfigurationsdatei steht — je nach Paket /etc/mysql/my.cnf oder eine Datei unter /etc/mysql/mysql.conf.d/ beziehungsweise /etc/mysql/mariadb.conf.d/:

[mysqld]
slow_query_log         = 1
slow_query_log_file    = /var/log/mysql/mysql-slow.log
long_query_time        = 0.5
min_examined_row_limit = 100

MariaDB: gleiche Funktion, teils andere Namen

MariaDB hat die Variablen ab Version 10.11.0 umbenannt; die alten Namen bleiben als Alias nutzbar, beide Schreibweisen funktionieren also. Dazu kommen drei Schalter, die MySQL nicht hat:

Zweck MySQL MariaDB ab 10.11 Vorgabe
Protokoll an slow_query_log log_slow_query aus
Datei slow_query_log_file log_slow_query_file host_name-slow.log
Schwelle (Sekunden) long_query_time log_slow_query_time 10.000000
Mindestzahl gelesener Zeilen min_examined_row_limit log_slow_min_examined_row_limit 0
Detailtiefe — log_slow_verbosity leer
Nur bestimmte Fälle — log_slow_filter admin,filesort,full_join,full_scan,…
Nur jede n-te Abfrage — log_slow_rate_limit 1

log_slow_verbosity nimmt unter anderem query_plan, explain und all und schreibt damit den Ausführungsplan ins Protokoll. Das MySQL-Gegenstück ist log_slow_extra, eingeführt in MySQL 8.0.14 und nur bei Ausgabe in eine Datei wirksam.

Welche Schwelle sinnvoll ist

Ob eine Abfrage im Protokoll landet, entscheidet der Server laut Handbuch in vier Schritten: keine Verwaltungsanweisung (oder log_slow_admin_statements ist an), mindestens long_query_time Sekunden gedauert (oder log_queries_not_using_indexes ist an und es wurde kein Index benutzt), mindestens min_examined_row_limit Zeilen gelesen, nicht durch log_throttle_queries_not_using_indexes unterdrückt.

Praktisch heißt das: 10 Sekunden sind zu hoch, weil Abfragen, die eine Anwendung ausbremsen, im Bereich von Zehntelsekunden liegen und erst in der Summe wehtun. 0 schreibt jede Abfrage mit. Auch log_queries_not_using_indexes ist scharf — das Handbuch warnt, dass das Protokoll damit schnell wächst, weil jeder harmlose Zugriff auf eine kleine Tabelle hineinläuft: einschalten, zehn Minuten laufen lassen, wieder ausschalten. Gegen das Rauschen hilft min_examined_row_limit, das in Zeilen zählt und nicht in Sekunden; alternativ begrenzt log_throttle_queries_not_using_indexes solche Einträge pro Minute, Vorgabe 0 für keine Begrenzung.

Einen Eintrag lesen

So sieht ein Eintrag aus — nachgebaut nach dem im MySQL-Handbuch dokumentierten Format, nicht aus einer Messung:

# Time: 2026-10-22T08:41:19.372914Z
# User@Host: appuser[appuser] @ localhost []  Id:   214
# Query_time: 4.182553  Lock_time: 0.000128 Rows_sent: 25  Rows_examined: 1842301
SET timestamp=1792658479;
SELECT * FROM bestellungen WHERE kunden_nr = 'K-10428' ORDER BY erstellt_am DESC LIMIT 25;

Vier Felder zählen:

  • Query_time — Laufzeit der Anweisung in Sekunden.
  • Lock_time — Zeit in Sekunden, die auf Sperren gewartet wurde. Ein hoher Wert hier zeigt auf eine andere Transaktion, nicht auf diese Abfrage.
  • Rows_sent — Zeilen, die an den Client gingen.
  • Rows_examined — Zeilen, die die Serverschicht dafür angesehen hat.

Der wichtigste Satz dieses Beitrags: Nicht Query_time allein verrät den fehlenden Index, sondern das Verhältnis von Rows_examined zu Rows_sent. Oben stehen 1.842.301 gelesene Zeilen für 25 ausgelieferte — der Server hat die ganze Tabelle durchgesehen, um 25 Zeilen zu finden. Wie groß sie ist, zeigen die Abfragen aus Größe von Datenbank und Tabelle ermitteln. Ist das Verhältnis eng und Query_time trotzdem hoch, suchen Sie nicht nach einem Index, sondern nach Sperren, Sortierung auf der Platte oder einem überlasteten Server.

Auswerten statt lesen

Ab ein paar hundert Einträgen liest niemand mehr die Datei. mysqldumpslow liegt den MySQL-Distributionen bei und verdichtet gleichartige Abfragen zu einer Zeile, indem es Zahlen durch N und Zeichenketten durch 'S' ersetzt:

# Die zehn Abfragen mit der höchsten Abfragezeit
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

# Vorgabe ist -s at, also die durchschnittliche Abfragezeit
mysqldumpslow -s at -t 10 /var/log/mysql/mysql-slow.log

# Nur Abfragen auf eine bestimmte Tabelle
mysqldumpslow -g 'bestellungen' /var/log/mysql/mysql-slow.log

-s t sortiert nach Abfragezeit, -s at nach durchschnittlicher Abfragezeit (die Vorgabe), -t 10 zeigt die ersten zehn Zeilen, -a schaltet das Zusammenfassen ab. In MariaDB heißt das Werkzeug mariadb-dumpslow und ist unter Linux per Symlink weiter als mysqldumpslow erreichbar. Mehr zeigt pt-query-digest aus dem Percona Toolkit: Es liest auch General Log, Binary Log und SHOW PROCESSLIST und gibt je Abfrageklasse Perzentile und Verteilungen aus statt nur Summen.

Der Weg ohne Protokoll

performance_schema beantwortet dieselbe Frage sofort und ohne Schreiblast. Die Zeitwerte stehen dort in Pikosekunden, also durch 1.000.000.000.000 teilen, um Sekunden zu erhalten:

SELECT LEFT(DIGEST_TEXT, 100)                   AS abfrage,
       COUNT_STAR                               AS aufrufe,
       ROUND(SUM_TIMER_WAIT / 1000000000000, 3) AS gesamt_s,
       ROUND(AVG_TIMER_WAIT / 1000000000000, 4) AS mittel_s,
       SUM_ROWS_EXAMINED                        AS zeilen_gelesen,
       SUM_ROWS_SENT                            AS zeilen_geliefert
FROM   performance_schema.events_statements_summary_by_digest
ORDER  BY SUM_TIMER_WAIT DESC
LIMIT  10;

Zwei Einschränkungen: Die Tabellen der Performance Schema sind laut Handbuch In-Memory-Tabellen ohne dauerhaften Speicher, beim Herunterfahren wird ihr Inhalt verworfen — die Auswertung reicht nur bis zum letzten Serverstart zurück. Und die Zeilenzahl begrenzt performance_schema_digests_size; läuft die Tabelle über, landet alles Weitere in einer Sammelzeile mit DIGEST = NULL. In MariaDB ist die Performance Schema zudem standardmäßig aus und nicht im Betrieb einschaltbar — das braucht einen Neustart.

Was danach kommt

Die gefundene Abfrage gehört als Nächstes in ein EXPLAIN, für die tatsächlichen statt der geschätzten Zeilenzahlen in EXPLAIN ANALYZE — das führt die Anweisung aus und gibt es seit MySQL 8.0.18. In MariaDB heißt dasselbe Werkzeug ANALYZE <Anweisung> und ergänzt die Spalten r_rows und r_filtered. Vorsicht: ANALYZE UPDATE und ANALYZE DELETE ändern wirklich Daten.

Aufräumen

Das Protokoll gehört nicht dauerhaft an, schon gar nicht mit einer niedrigen Schwelle:

SET GLOBAL slow_query_log = 'OFF';
FLUSH SLOW LOGS;

FLUSH SLOW LOGS schließt die Protokolldatei und öffnet sie neu — der saubere Weg, nachdem Sie die Datei weggeschoben oder gelöscht haben. Auf Tabellen hat die Anweisung keine Wirkung: Steht log_output auf TABLE, schreibt der Server in mysql.slow_log, und diese Tabelle wächst mit. Leeren Sie sie mit TRUNCATE TABLE mysql.slow_log; oder tauschen Sie sie per RENAME TABLE gegen eine leere Kopie, wenn die Einträge bleiben sollen. Dass vergessene Protokolle Platten füllen, ist kein Spezialfall — dieselbe Geschichte erzählen die Binary Logs, nur in größerem Maßstab.

Wenn eine Anwendung langsam ist und niemand im Haus die Datenbank liest, ist diese Messung unser erster Schritt — wie wir Anwendungen und Datenbanken betreuen, steht unter Webentwicklung.

Quellen

Häufige Fragen

Wie schalte ich das Slow Query Log ohne Neustart ein?

Mit SET GLOBAL slow_query_log = ON, dazu bei Bedarf SET GLOBAL slow_query_log_file und SET GLOBAL long_query_time. Alle drei sind laut Variablenübersicht des MySQL-Handbuchs dynamisch, der Server muss also nicht neu starten. Wichtig: long_query_time hat den Gültigkeitsbereich Global und Session, und eine Sitzungsvariable wird beim Verbindungsaufbau aus dem globalen Wert gefüllt. Ihre schon offene Verbindung arbeitet deshalb weiter mit dem alten Wert — neu verbinden. Nach einem Serverneustart ist die Einstellung wieder weg, dauerhaft gilt nur, was in der Konfigurationsdatei steht.

Wo liegt die Datei mit dem Slow Query Log?

Dort, wo slow_query_log_file hinzeigt: SELECT @@slow_query_log_file; nennt den Pfad. Wenn kein Name gesetzt ist, heißt die Datei laut MySQL-Handbuch host_name-slow.log und der Server legt sie im Datenverzeichnis an, sofern kein absoluter Pfad angegeben wurde. In MariaDB ab 10.11 heißt dieselbe Variable zusätzlich log_slow_query_file. Steht log_output auf TABLE, gibt es keine Datei — dann liegen die Einträge in der Tabelle mysql.slow_log.

Warum bleibt mein Slow Query Log leer?

Vier Ursachen deckt die Prüfreihenfolge im MySQL-Handbuch ab. Erstens ist die Vorgabe von long_query_time 10 Sekunden — so langsam ist kaum eine Abfrage, die eine Anwendung ausbremst. Zweitens muss eine Abfrage mindestens min_examined_row_limit Zeilen gelesen haben. Drittens hat NONE in log_output Vorrang vor jedem anderen Ziel. Viertens behält eine Verbindung, die vor dem SET GLOBAL schon offen war, ihren alten Wert für long_query_time.

Welcher Wert für long_query_time ist sinnvoll?

Die Vorgabe von 10 Sekunden ist in der Praxis zu hoch, der Minimalwert 0 schreibt jede Abfrage mit und lässt die Datei schnell volllaufen. Nehmen Sie als Richtwert die Antwortzeit, die Ihre Anwendung noch verträgt — der Wert wird in Sekunden angegeben und darf Dezimalstellen haben, 0.5 ist also erlaubt. Dann die Größe der Protokolldatei nach einigen Minuten prüfen und den Wert nachziehen. Gegen Rauschen hilft min_examined_row_limit, das in Zeilen zählt, nicht in Sekunden.

Bremst das Slow Query Log den Server?

Jeder Eintrag ist ein Schreibvorgang, bei einer sehr niedrigen Schwelle also messbare Last und eine schnell wachsende Datei. Besonders deutlich wird das mit log_queries_not_using_indexes: das MySQL-Handbuch weist ausdrücklich darauf hin, dass das Protokoll dann schnell wächst, und bietet log_throttle_queries_not_using_indexes als Begrenzung pro Minute an. Wer gar nicht schreiben will, nimmt performance_schema.events_statements_summary_by_digest — dieselbe Frage, ohne Protokolldatei.

Fragen zu diesem Thema?

Wir unterstützen Sie bei SEO, KI-Einführung und E-Commerce.

Projekt besprechen →