- 1 Section
- 10 Lessons
- unbegrenzt
- Debugging & Fehleranalyse10
- 1.1Fehlertypen: Syntaxfehler, Laufzeitfehler, Logikfehler
- 1.2Systematische Fehlersuche: Duck-Debugging, Divide & Conquer
- 1.3Debugger-Grundkonzepte: Breakpoints, Watches, Call Stack
- 1.4Debugging in Java mit IntelliJ / Eclipse
- 1.5Debugging in Python mit VS Code und pdb
- 1.6Debugging in JavaScript: Browser DevTools
- 1.7Logging: Fehler protokollieren und auswerten
- 1.8Fehler in Datenbankabfragen finden
- 1.9Fehler in Netzwerkverbindungen und APIs debuggen
- 1.10Praxisaufgaben: Fehlerhafte Programme korrigieren
Fehler in Datenbankabfragen finden
Datenbanken sind das Rückgrat fast jeder Anwendung – und entsprechend häufig die Quelle von Bugs. Das tückische: viele DB-Bugs lassen sich mit dem normalen Code-Debugger nicht finden, weil das Problem im SQL liegt, in der Datenbank-Engine, oder zwischen App und DB. Es braucht eigene Techniken.
Diese Lektion zeigt vier Hauptarten von DB-Bugs: falsche Ergebnisse (Query liefert falsche Daten), fehlende Daten (Filter zu strikt), Performance-Probleme (Query langsam) und Connection-Bugs (Pool erschöpft, Timeouts). Plus die Werkzeuge dafür: EXPLAIN, EXPLAIN ANALYZE, Query-Profiler, Slow-Query-Log. Grundlagen zu SQL findest du in K33-36.
1) Welche DB-Bugs gibt es?
Bevor wir Werkzeuge anschauen, ein Überblick über die häufigsten Bug-Sorten. Jede hat eigene Such-Strategien:
OR ohne Klammern bei mehreren AND, falscher Vergleichsoperator, NULL-Vergleich mit = statt IS.WHERE x = NULL liefert NIE Treffer (NULL ist nicht gleich NULL). Auch: NULL != 'X' liefert NICHT alle Nicht-X. Muss IS NULL sein.IS NULL/IS NOT NULL verwendenWHERE id = '42' kann je nach DB scheitern oder einen FullScan auslösen.EXPLAIN nutzen, Indizes prüfen2) Query schrittweise testen
Eine der besten Techniken bei „Query liefert falsche Daten": schrittweise reduzieren. Bau die Query Stück für Stück auf und schau bei jedem Schritt was rauskommt. Klick durch:
3) Das EXPLAIN-Statement
Wenn eine Query langsam ist, willst du wissen was die Datenbank intern tut. Macht sie einen Full-Table-Scan? Nutzt sie den richtigen Index? Hier kommt EXPLAIN ins Spiel – ein Befehl den jede relationale DB versteht (PostgreSQL, MySQL, MariaDB, SQL Server, Oracle). Er zeigt den Execution Plan – den Plan wie die DB die Query ausführen wird:
Was lesen wir hier? Seq Scan bedeutet „sequenzieller Scan" – die Datenbank liest jede einzelne Zeile der Tabelle und prüft den Filter. Bei kleinen Tabellen ok, bei großen Tabellen mit Millionen Zeilen tödlich. Die Lösung: einen Index auf city anlegen:
Jetzt steht Index Scan – die DB nutzt den Index und springt direkt zu den richtigen Zeilen. Cost runter von 15 auf 8. Bei großen Tabellen kann das den Unterschied zwischen 30 Sekunden und 30 Millisekunden bedeuten.
4) EXPLAIN ANALYZE
EXPLAIN zeigt den geplanten Ablauf. EXPLAIN ANALYZE führt die Query wirklich aus und misst die echte Dauer. Sehr nützlich – aber Vorsicht: bei UPDATE/DELETE die Query wird wirklich ausgeführt! In einer Transaktion mit ROLLBACK kann man das umgehen.
Du siehst jetzt die tatsächlichen Millisekunden pro Schritt. Damit findest du den Engpass: welcher Schritt frisst die meiste Zeit? Meist sind es Sequential-Scans auf großen Tabellen oder fehlende JOIN-Indizes.
5) Execution-Plan visualisiert
Ein realer EXPLAIN-Output ist oft eine verschachtelte Baumstruktur. Hier ein Beispiel mit erkennbarem Engpass:
orders.customer_id würde der Plan einen Index Scan nutzen – statt 10ms vielleicht 1ms. Bei großen Tabellen ist das der Hebel.Bei komplexen Plänen helfen Visualisierer wie explain.depesz.com (PostgreSQL) oder MySQL Workbench's eingebauter Visual Explain. Sie färben teure Operationen rot ein und zeigen die Bottlenecks sofort.
6) Das N+1-Problem
Ein extrem häufiger Bug in ORM-basierten Anwendungen. Du holst eine Liste von Objekten – aber für jedes Objekt wird automatisch eine weitere Query abgesetzt, um Relationen zu laden:
Wenn du 1000 User hast: 1001 Queries. Eine für die User-Liste, dann 1000 für die Orders-Listen. Selbst wenn jede Query nur 5ms braucht: das sind 5 Sekunden pro Request. Tödlich.
Wie erkennen?
- Slow-Query-Log: du siehst hunderte ähnliche Queries hintereinander
- ORM-Logging aktivieren: Hibernate hat
show_sql=true, JPA dasselbe – druckt alle generierten Queries - Tools wie Hibernate Statistics, p6spy oder JDBC-Profiler zeigen Query-Count pro Request
Fix: JOIN FETCH bzw. Eager Loading:
7) Slow-Query-Log aktivieren
Jede DB hat ein Slow-Query-Log: alle Queries die länger als ein Schwellwert (z.B. 500ms) brauchen, werden protokolliert. Damit findest du langsame Queries automatisch.
PostgreSQL macht das ähnlich über log_min_duration_statement = 500 in postgresql.conf. Output landet im DB-Log. Tools wie pt-query-digest (Percona-Toolkit) oder pgBadger aggregieren das in Statistiken: „Die Top-10 langsamsten Queries der letzten Woche, sortiert nach Gesamt-Dauer".
8) NULL-Falle
Der wahrscheinlich häufigste Bug in SQL: jemand vergleicht mit NULL. Beachte:
Der Grund: in SQL ist NULL nicht „unbekannt = unbekannt" – sondern „unbekannt mit unbekannt zu vergleichen ist unbekannt, also nicht wahr". Three-Valued Logic: TRUE, FALSE, UNKNOWN. Filter erwarten TRUE – UNKNOWN-Treffer fliegen raus.
9) SQL-Injection erkennen
Eine besondere Bug-Klasse: SQL-Injection. Tritt auf wenn User-Input direkt in SQL eingebaut wird:
Fix: Prepared Statements. Werte werden separat an die DB übergeben und nie als Code interpretiert:
10) Connection-Pools debuggen
Ein häufiger Bug bei größeren Anwendungen: der Connection-Pool ist erschöpft. Die App scheint zu hängen, neue Requests warten ewig. Ursache: Connections werden geöffnet aber nie zurückgegeben.
So debuggst du das:
- Pool-Metriken im Log: HikariCP (Java) loggt regelmäßig
active=20, idle=0, waiting=15. Wenn waiting hoch und idle 0: Leak! - DB-Connection-Liste prüfen:
SELECT * FROM pg_stat_activity(PostgreSQL) bzw.SHOW PROCESSLIST(MySQL) zeigt alle aktiven Connections und was sie tun - Pool-Leak-Detection: HikariCP hat
leakDetectionThreshold=30000– Connection 30s offen ohne return wird gemeldet - Code-Pattern: nie manuell schließen. Java:
try-with-resources. Python:with-Statement. Sonst leakt's bei Exceptions.
11) Debug-Tipps pro DB-System
| DB | Wichtige Tools |
|---|---|
| PostgreSQL | EXPLAIN ANALYZE, pg_stat_activity, pg_stat_statements, pgBadger (Log-Analyzer), explain.depesz.com |
| MySQL/MariaDB | EXPLAIN, SHOW PROCESSLIST, Slow-Query-Log, Percona pt-query-digest, MySQL Workbench Visual Explain |
| SQL Server | Query Plan in SSMS (Ctrl+M), sys.dm_exec_query_stats, Extended Events |
| Oracle | EXPLAIN PLAN FOR ..., DBMS_XPLAN.DISPLAY, AWR-Reports |
| MongoDB | db.collection.explain(), MongoDB Compass (GUI), Profiler-Collection |
| Redis | SLOWLOG GET, MONITOR (für Live-Trace), INFO |
12) Häufige Anti-Pattern
Was du nicht tun solltest:
- SELECT * in Production-Code: holt mehr Daten als nötig. Lieber konkrete Spalten. Bei Schema-Änderungen breakt SELECT * auch oft Code.
- Queries in Schleifen: Klassisches N+1. Lieber einen JOIN oder einen einzigen Query mit IN-Klausel.
- WHERE auf Funktionen:
WHERE LOWER(email) = 'anna@x.de'verhindert Index-Nutzung. Lösung: Werte vor dem Vergleich normalisieren, oder Function-based Index anlegen. - OR statt UNION:
WHERE x = 1 OR y = 2mit zwei verschiedenen Indices nutzt oft keinen Index.UNIONder zwei Einzel-Queries ist schneller. - Implizite Type-Casts:
WHERE id = '42'mit id als INT kann FullScan auslösen. - COUNT(*) auf riesigen Tabellen: muss alle Zeilen lesen. Falls Schätzung reicht:
EXPLAINnutzen für die ungefähre Zeilenzahl.
Zusammenfassung
DB-Bug-Klassen: falsche WHERE-Klauseln, JOIN-Probleme, NULL-Fallen, Type-Mismatch, Slow Queries, N+1-Problem, SQL-Injection, Connection-Leaks. Methodik für logische Bugs: Query schrittweise reduzieren, jeden Zwischenschritt prüfen (Divide-and-Conquer auf SQL). EXPLAIN zeigt den geplanten Ausführungsplan – Sequenz-Scan = oft schlecht, Index-Scan = oft gut. EXPLAIN ANALYZE führt aus und zeigt echte ms pro Schritt. Slow-Query-Log sammelt alle langsamen Queries automatisch. N+1-Problem beim ORM: 1 + N Queries statt 1 mit JOIN FETCH. NULL-Falle: = NULL nie wahr, immer IS NULL. SQL-Injection verhindern via Prepared Statements – nie String-Konkatenation! Mehr zu Secure Coding in K11. Connection-Pool-Debugging: Pool-Metriken loggen, leakDetectionThreshold, try-with-resources. Tools pro DB: pgBadger (PostgreSQL), pt-query-digest (MySQL), Workbench Visual Explain, db.explain() (MongoDB). Mehr zu SQL in K33-36.
