MySQL: EXPLAIN, SHOW PROCESSLIST, Query-Optimierung
Kategorie: Datenbank · Channel: #klassiker
SHOW PROCESSLIST â der erste Blick
# Was lÀuft gerade in MySQL?
mysql -e "SHOW PROCESSLIST\G"
mysql -e "SHOW FULL PROCESSLIST\G" # vollstÀndige Query-Texte
# Nur aktive Queries (kein Sleep):
mysql -e "SELECT id, user, host, db, command, time, state,
LEFT(info, 100) as query
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC;"
PROCESSLIST Felder verstehen
| Feld |
Bedeutung |
Alarm wenn |
time |
Sekunden seit Start |
> 30s = Slow Query |
state |
Was tut der Query gerade |
"Waiting for lock" = Problem! |
command |
Sleep/Query/Connect |
viele Query = hohe Last |
info |
SQL-Text |
NULL bei Sleep |
Kritische States
Waiting for lock â Lock-Contention, anderer Query blockiert
Waiting for table lock â ALTER TABLE oder LOCK TABLE lĂ€uft
Copying to tmp table â GROUP BY/ORDER BY ohne Index = langsam
Sorting result â filesort, kein passender Index
Sending data â groĂe Ergebnismengen
Locked â MyISAM-Lock (InnoDB: "Waiting for lock")
EXPLAIN â Query-Plan lesen
EXPLAIN zeigt wie MySQL einen Query intern ausfĂŒhrt.
Es verĂ€ndert nichts â nur Analyse.
# EXPLAIN fĂŒr SELECT
mysql -e "EXPLAIN SELECT * FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01'\G"
# EXPLAIN ANALYZE (MySQL 8.0+) â fĂŒhrt Query aus, zeigt echte Zeiten
mysql -e "EXPLAIN ANALYZE SELECT * FROM orders
WHERE status = 'pending'\G"
# FĂŒr UPDATE/DELETE auch möglich:
mysql -e "EXPLAIN UPDATE orders SET status='done'
WHERE created_at < '2020-01-01'\G"
EXPLAIN Ausgabe â die kritischen Felder
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: orders
partitions: NULL
type: ALL â KRITISCH: Full Table Scan!
possible_keys: NULL â kein Index verfĂŒgbar
key: NULL â kein Index genutzt
key_len: NULL
ref: NULL
rows: 2847291 â scannt 2,8 Mio Zeilen!
filtered: 11.11
Extra: Using where â Filter nach dem Scan
type â das wichtigste Feld
system â Tabelle hat genau 1 Zeile (ideal)
const â Primary Key oder Unique Key Lookup (sehr gut)
eq_ref â JOIN mit Unique/PK (gut)
ref â Index-Lookup, mehrere mögliche Zeilen (gut)
range â Index-Bereichssuche (WHERE id > 100) (ok)
index â Full Index Scan (besser als ALL, aber noch langsam)
ALL â Full Table Scan â PROBLEM! Index fehlt oder wird nicht genutzt
Faustregel: type = ALL mit rows > 1000 â Index prĂŒfen!
Extra â weitere Hinweise
Using index â Index Covering Scan (gut! keine Tabellenzugriffe)
Using where â Filter nach dem Scan (ok)
Using filesort â Sortierung ohne Index (langsam bei groĂen Tabellen)
Using temporary â TemporĂ€re Tabelle (GROUP BY/ORDER BY ohne Index)
Using index condition â Index Condition Pushdown (gut, MySQL 5.6+)
NULL â nichts besonderes
Index fehlt â erkennen und beheben
# EXPLAIN zeigt type: ALL und key: NULL
# â Index anlegen
# Einfacher Index:
ALTER TABLE orders ADD INDEX idx_status (status);
# Zusammengesetzter Index (fĂŒr mehrere WHERE-Felder):
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
# Reihenfolge wichtig: Gleichheits-Felder zuerst, dann Range-Felder!
# Index prĂŒfen:
SHOW INDEX FROM orders;
# EXPLAIN nochmal â jetzt sollte type: ref oder range kommen:
mysql -e "EXPLAIN SELECT * FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01'\G"
# type: range, key: idx_status_created, rows: 42 â viel besser!
# Online Index (ohne Tabellen-Lock, groĂe Tabellen):
ALTER TABLE orders
ADD INDEX idx_status_created (status, created_at),
ALGORITHM=INPLACE, LOCK=NONE;
Slow Query Log
# Status prĂŒfen:
mysql -e "SHOW VARIABLES LIKE 'slow_query%';"
mysql -e "SHOW VARIABLES LIKE 'long_query_time';"
# Live aktivieren (kein Restart):
mysql -e "SET GLOBAL slow_query_log = 'ON';"
mysql -e "SET GLOBAL long_query_time = 1;"
mysql -e "SET GLOBAL log_queries_not_using_indexes = 'ON';"
mysql -e "SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';"
# Dauerhaft in /etc/mysql/mysql.conf.d/mysqld.cnf:
# slow_query_log = 1
# slow_query_log_file = /var/log/mysql/slow.log
# long_query_time = 1
# log_queries_not_using_indexes = 1
# Auswerten:
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# -s t = nach Gesamtzeit sortieren
# -t 10 = Top 10
Lock-Probleme diagnostizieren
# Wer hÀlt einen Lock?
mysql -e "SELECT * FROM information_schema.innodb_trx\G"
# Wer wartet auf wen?
mysql -e "SELECT * FROM information_schema.innodb_lock_waits\G"
# InnoDB Status (detailliert):
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 30 "LATEST DETECTED DEADLOCK"
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 20 "TRANSACTIONS"
# Blockierende Query killen:
mysql -e "KILL QUERY 142;" # nur Query abbrechen, Verbindung bleibt
mysql -e "KILL 142;" # gesamte Verbindung trennen
Distro-Unterschiede: MySQL Pfade und Service
| Aspekt |
Debian/Ubuntu |
RHEL/Rocky |
FreeBSD |
| Config |
/etc/mysql/mysql.conf.d/mysqld.cnf |
/etc/my.cnf |
/usr/local/etc/mysql/my.cnf |
| Daten |
/var/lib/mysql/ |
/var/lib/mysql/ |
/var/db/mysql/ |
| Logs |
/var/log/mysql/ |
/var/log/mysql/ |
/var/log/mysql/ |
| Service |
systemctl restart mysql |
systemctl restart mysqld |
service mysql-server restart |
| Socket |
/var/run/mysqld/mysqld.sock |
/var/lib/mysql/mysql.sock |
/tmp/mysql.sock |
# Ubuntu/Debian:
systemctl status mysql
journalctl -u mysql
# RHEL/Rocky:
systemctl status mysqld
journalctl -u mysqld
# FreeBSD:
service mysql-server status
tail -f /var/log/mysql/mysqld.err
Schnellreferenz: MySQL Diagnose â Einzeiler
# Prozesse
mysql -e "SHOW FULL PROCESSLIST\G" | grep -A 5 "time: [3-9][0-9]"
mysql -e "SELECT id,time,state,LEFT(info,80) FROM information_schema.processlist WHERE command!='Sleep' ORDER BY time DESC;"
# EXPLAIN
mysql -e "EXPLAIN SELECT * FROM tabelle WHERE feld = 'wert'\G"
# Locks
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 10 "LOCK"
mysql -e "SELECT * FROM information_schema.innodb_trx\G"
# Slow Log
tail -50 /var/log/mysql/slow.log
mysqldumpslow -s t -t 5 /var/log/mysql/slow.log
# Index fehlt?
mysql -e "EXPLAIN SELECT ...\G" | grep "type: ALL"
# Verbindungen
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -e "SHOW VARIABLES LIKE 'max_connections';"
# Prozess killen
mysql -e "KILL 142;"