Q&A Klassiker: Hohe Load und MySQL Slow Queries
Kategorie: Q&A · Channel: #klassiker
Format: BĂŒhnenspiel â Admin · Angreifer · Kunde
Die Situation
Uhrzeit: 14:23 Uhr (Mittagspeak)
Alarm: Load Average > 40 auf 8-Kern-Server
Symptom: Website trÀge, Shop-Bestellungen brechen ab
Kunde: meldet sich um 14:31 Uhr
đšâđ» Admin-Perspektive: Was sehe ich, was tue ich?
Schritt 1: Lagebild â wie schlimm ist es?
# Erster Blick: Load und Ressourcen
uptime
# 14:23:41 up 47 days, load average: 41.23, 38.91, 29.44
# â 1min â 5min â 15min
# â Load steigt seit ~15 Minuten kontinuierlich
# Load interpretieren:
# Load 1.0 auf 1 CPU = 100% ausgelastet
# Load 8.0 auf 8 CPUs = 100% ausgelastet
# Load 41 auf 8 CPUs = 5Ă ĂŒberlastet!
# Was genau erzeugt die Load?
top -b -n 1 | head -30
# oder moderner:
htop
# Sind es CPU-Prozesse oder Warte-Prozesse (I/O)?
# In top: %us (user) %sy (system) %wa (iowait)
# Hoher %wa â I/O ist der Engpass, nicht CPU
Schritt 2: MySQL im Fokus
# MySQL Prozessliste â was lĂ€uft gerade?
mysql -e "SHOW PROCESSLIST\G" | head -80
# oder schöner:
mysql -e "SELECT id, user, host, db, command, time, state,
LEFT(info, 80) as query
FROM information_schema.processlist
WHERE command != 'Sleep'
ORDER BY time DESC;"
# Typische Ausgabe die Probleme zeigt:
# | id | time | state | query |
# | 142 | 847 | Waiting for lock | UPDATE orders SET ... |
# | 143 | 846 | Waiting for lock | UPDATE orders SET ... |
# | 144 | 845 | Waiting for lock | UPDATE orders SET ... |
# â Lock-Contention! Ein Query blockiert alle anderen
# Wie viele Verbindungen?
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -e "SHOW VARIABLES LIKE 'max_connections';"
# Wenn Threads_connected nahe max_connections â Verbindungs-Erschöpfung
Schritt 3: Slow Query Log aktivieren und lesen
# Slow Query Log status:
mysql -e "SHOW VARIABLES LIKE 'slow_query%';"
mysql -e "SHOW VARIABLES LIKE 'long_query_time';"
# Live aktivieren (ohne Restart!):
mysql -e "SET GLOBAL slow_query_log = 'ON';"
mysql -e "SET GLOBAL long_query_time = 1;" # Queries > 1s loggen
mysql -e "SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';"
# Slow Log lesen und auswerten:
tail -50 /var/log/mysql/slow.log
# mysqldumpslow â Zusammenfassung der schlimmsten Queries:
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# -s t = sortiert nach Gesamtzeit
# -t 10 = Top 10
# pt-query-digest (Percona Toolkit) â noch besser:
pt-query-digest /var/log/mysql/slow.log | head -100
Schritt 4: Den schuldigen Query identifizieren
# EXPLAIN â was macht MySQL mit dem Query?
mysql -e "EXPLAIN SELECT * FROM orders
WHERE status = 'pending'
AND created_at > '2026-01-01'\G"
# Kritische Ausgabefelder:
# type: ALL â FULL TABLE SCAN! Kein Index genutzt
# rows: 2847291 â scannt 2,8 Millionen Zeilen
# key: NULL â kein Index
# Index fehlt? So hinzufĂŒgen (online, ohne Lock bei groĂen Tabellen):
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
# oder fĂŒr groĂe Tabellen ohne Downtime:
pt-online-schema-change --alter "ADD INDEX idx_status_created (status, created_at)" \
D=shopdb,t=orders --execute
Schritt 5: Lock-Contention lösen
# Welcher Query hÀlt einen Lock?
mysql -e "SELECT * FROM information_schema.innodb_trx\G"
mysql -e "SELECT * FROM information_schema.innodb_lock_waits\G"
# Blockierenden Prozess killen (Notfall):
mysql -e "KILL QUERY 142;" # nur den Query abbrechen
mysql -e "KILL 142;" # gesamte Verbindung trennen
# InnoDB Status â detaillierte Lock-Infos:
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 30 "LATEST DETECTED DEADLOCK"
Schritt 6: I/O-Engpass diagnose
# Ist MySQL I/O-gebunden?
iostat -x 1 5
# %util nahe 100% â Disk ist Engpass
# Welche Datei schreibt/liest am meisten?
iotop -o -b -n 3
# MySQL I/O-Statistiken:
mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';"
# Innodb_buffer_pool_reads: viele â zu wenig RAM fĂŒr Buffer Pool
# Faustregel: Buffer Pool sollte 70-80% des verfĂŒgbaren RAM nutzen
# Buffer Pool GröĂe anpassen:
# /etc/mysql/mysql.conf.d/mysqld.cnf:
# innodb_buffer_pool_size = 4G # auf Server mit 8G RAM
Schritt 7: Sofortentlastung
# Query-Cache prĂŒfen (MySQL < 8.0):
mysql -e "SHOW VARIABLES LIKE 'query_cache%';"
# Verbindungen begrenzen (wenn zu viele):
mysql -e "SET GLOBAL max_connections = 200;"
# LanglÀufer killen (alle Queries > 60 Sekunden):
mysql -e "SELECT CONCAT('KILL ', id, ';')
FROM information_schema.processlist
WHERE time > 60 AND command != 'Sleep';" | \
mysql -f
# MySQL-Status-Ăberblick:
mysqladmin status
mysqladmin extended-status | grep -E "Slow|Thread|Question|Connect"
đŠč Angreifer-Perspektive: Wie hĂ€tte ich das verursacht?
Methode 1: Slow Query Injection
Angreifer schickt absichtlich ineffiziente Queries:
â SELECT * FROM products WHERE description LIKE '%suchbegriff%'
â Kein Index auf description â Full Table Scan
â Bei 100 gleichzeitigen Requests â 100 Full Table Scans
â Load explodiert
Diagnose:
Slow Log zeigt viele identische Queries mit type: ALL
nginx access.log zeigt viele Requests auf Such-Endpoint
Methode 2: Connection Exhaustion
Angreifer öffnet viele Verbindungen ohne sie zu schlieĂen:
â max_connections erschöpft
â neue legitime Verbindungen: "Too many connections"
â Applikation kann keine DB-Verbindung aufbauen â 500
Diagnose:
mysql -e "SHOW STATUS LIKE 'Threads_connected';"
netstat -an | grep 3306 | grep ESTABLISHED | wc -l
Methode 3: Lock-Flooding
Angreifer startet viele UPDATE/INSERT Transaktionen:
â HĂ€lt Locks auf wichtigen Tabellen
â alle anderen Queries warten auf Lock-Release
â Queue wĂ€chst, Load steigt, Website hĂ€ngt
Diagnose:
SHOW ENGINE INNODB STATUS â viele wartende Transaktionen
information_schema.innodb_trx â viele aktive Transaktionen
Methode 4: Cronjob-Kollision (kein Angreifer, aber klassisch)
TÀglich um 14:00 lÀuft ein Report-Cronjob:
â SELECT mit JOIN ĂŒber 5 Tabellen, kein Index
â lĂ€uft 15 Minuten, hĂ€lt Read-Locks
â Online-Shop-Queries warten
Diagnose:
crontab -l â was lĂ€uft um 14:00?
SHOW PROCESSLIST â Report-Query seit 900 Sekunden aktiv?
đ Kunden-Perspektive: Was erlebe ich?
14:31 Uhr â Nachricht:
"Hallo, seit ca. 14:20 Uhr lÀuft unser Shop sehr langsam.
Bestellungen werden teilweise nicht abgeschlossen.
Wir sehen im Backend dass neue Bestellungen fehlen.
Das ist Mittagszeit â wir haben gerade viel Traffic.
Bitte dringend prĂŒfen!"
Was der Kunde technisch erlebt:
Browser: Seite lĂ€dt 30+ Sekunden â Timeout
"504 Gateway Timeout" bei Bestellabschluss
Applikation: Verbindung zur DB dauert > 30s
ORM wirft "Lock wait timeout exceeded"
Bestellung wird nicht gespeichert
Folgen:
â Kunde bricht ab
â Bestellung verloren
â Bei Umsatz-kritischer Zeit: direkter Schaden
Kommunikation:
14:35 Uhr:
"Wir haben eine DatenbankĂŒberlastung identifiziert.
Ursache: ein ineffizienter Query blockiert andere Prozesse.
Wir beheben das gerade. SchÀtzung: 10-20 Minuten."
14:47 Uhr:
"Die Situation hat sich normalisiert. Load ist wieder normal.
Ursache war ein fehlender Index der tÀglich um 14:00 Uhr
durch einen Report-Job ausgelöst wurde.
Wir haben den Index ergÀnzt und den Report-Job auf 02:00 Uhr verlegt."
đŻ Erkenntnis â Drei Perspektiven zusammen
| Perspektive |
Kernaussage |
| đšâđ» Admin |
SHOW PROCESSLIST + EXPLAIN zeigen wo der Brand ist |
| đŠč Angreifer |
Full Table Scans sind billige DoS-Vektoren ohne Index |
| đ Kunde |
504-Fehler um 14:00 = tÀglich um 14:00 lÀuft ein Report |
đ§ PrĂ€vention
# 1. Slow Query Log dauerhaft aktivieren
# /etc/mysql/mysql.conf.d/mysqld.cnf:
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_queries_not_using_indexes = 1
# 2. Cronjobs auf Nebenzeiten verlegen
# Keine schweren Reports zwischen 08:00 und 20:00!
# crontab: 0 2 * * * /opt/scripts/daily_report.sh
# 3. Indizes regelmĂ€Ăig prĂŒfen
mysql -e "SELECT table_name, index_name, stat_value
FROM mysql.innodb_index_stats
WHERE stat_name = 'n_diff_pfx01'
ORDER BY stat_value DESC LIMIT 20;"
# 4. Connection Pooling in der Applikation
# Direkte DB-Verbindungen pro Request â schlecht
# Connection Pool (z.B. PgBouncer fĂŒr PostgreSQL, ProxySQL fĂŒr MySQL)
# 5. Read Replicas fĂŒr Reports
# Schwere SELECT-Queries auf Read-Replica
# â kein Impact auf Master / Online-Shop
# 6. Monitoring: MySQL Metriken
# â Threads_connected, Slow_queries, Innodb_row_lock_waits
# â Alert bei Slow_queries > 10/min
đ Schnellreferenz: Hohe Load / MySQL â Einzeiler
uptime # Load anzeigen
top -b -n 1 | head -20 # Was erzeugt Load?
mysql -e "SHOW PROCESSLIST\G" # MySQL-AktivitÀt
mysql -e "SET GLOBAL slow_query_log='ON';" # Slow Log aktivieren
mysqldumpslow -s t -t 5 /var/log/mysql/slow.log # Schlimmste Queries
mysql -e "SHOW ENGINE INNODB STATUS\G" | grep -A 20 "TRANSACTIONS"
mysql -e "SELECT * FROM information_schema.innodb_trx\G" # Locks
mysql -e "KILL 142;" # Prozess killen
iostat -x 1 3 # I/O-Engpass?
iotop -o -b -n 3 # Wer schreibt/liest?