banner
Lesezeit: 8 Minuten
 
Marwin Frahm
Marwin Frahm Netzwerkanalytiker & Wireshark-Experte | Root-Cause-Analyse statt Fingerpointing
Edit Template

Praxisfall: Wenn 2.500 Datenbank-Requests eine Anwendung lahmlegen – N+1 Query im Wireshark-Trace

Es gibt Support-Tickets, die kennst du auswendig, bevor du sie öffnest: “Die Anwendung ist langsam. Bitte Netzwerk prüfen.” In diesem Praxisfall zeige ich dir, wie eine paketbasierte Analyse einen wochenlangen Streit zwischen Applikationshersteller und IT-Abteilung beendet hat – und warum das Netzwerk am Ende komplett freigesprochen wurde, obwohl alle Symptome scheinbar dagegen sprachen.

Der Fall ist vollständig anonymisiert und fiktionalisiert, das technische Muster dahinter ist aber eines der häufigsten, das mir in der Applikationsanalyse begegnet: sequentielle Datenbank-Requests, besser bekannt als N+1 Query Problem.

Die Ausgangslage: Langsame Anwendung, ratlose Teams, Fingerpointing

Ein mittelständisches Unternehmen betreibt eine branchenspezifische Fachanwendung mit einem Oracle-Datenbank-Backend. Die Anwendung ist geschäftskritisch, die Anwender arbeiten den ganzen Tag damit. Und genau diese Anwender melden seit Wochen dasselbe Problem: Das Öffnen einer einzigen Übersichtsmaske dauert über 30 Sekunden. Nicht beim ersten Start am Morgen, nicht sporadisch – sondern jedes Mal, reproduzierbar, bei jedem Anwender.

Was dann passierte, hast du vermutlich selbst schon erlebt. Der Applikationshersteller schaut kurz auf seine Software, findet nichts und verweist auf “das Netzwerk”. Die interne IT-Abteilung schaut auf ihre Monitoring-Dashboards: Bandbreitenauslastung im einstelligen Prozentbereich, Ping-Zeiten stabil, keine Interface-Errors, keine Discards. Aus ihrer Sicht ist das Netzwerk sauber – also muss es die Applikation sein.

Beide Seiten haben aus ihrer Perspektive recht. Beide Seiten haben Messwerte, die ihre Position stützen. Und genau deshalb bewegte sich seit Wochen nichts. Klassisches Fingerpointing: Jeder zeigt auf den anderen, die Anwender leiden weiter, und das Problem wird zur politischen Frage statt zur technischen.

An diesem Punkt kam ich ins Spiel – mit einem einzigen Auftrag: Beweise auf Paketebene, wo die Zeit tatsächlich verloren geht.

Warum das Monitoring nichts zeigt: Bandbreite ist nicht das Problem

Bevor wir in den Trace einsteigen, lohnt sich ein kurzer Blick darauf, warum klassisches Netzwerk-Monitoring in solchen Fällen systematisch blind ist.

Die meisten Monitoring-Systeme messen Durchsatz, Auslastung und Erreichbarkeit – typischerweise in Intervallen von einer oder fünf Minuten. Das ist völlig legitim, um Kapazitätsprobleme oder Ausfälle zu erkennen. Aber eine Anwendung, die langsam ist, weil sie auf sich selbst wartet, erzeugt in keiner dieser Metriken einen Ausschlag. Im Gegenteil: Sie überträgt sogar auffällig wenig Daten.

Das führt zu einer Situation, die auf den ersten Blick paradox wirkt: Die Anwendung ist quälend langsam, während das Netzwerk praktisch im Leerlauf ist. Wer nur auf Bandbreite schaut, sucht am falschen Ort. Warum eine Gigabit-Leitung eine Anwendung nicht automatisch schnell macht, habe ich ausführlich im Artikel Warum ist mein Netzwerk langsam trotz 1 Gbit? beschrieben – dieser Fall hier ist das Paradebeispiel dafür.

Die entscheidende Metrik in solchen Fällen ist nicht Bandbreite, sondern Latenz in Kombination mit dem Kommunikationsverhalten der Applikation. Und genau das sieht man nur auf Paketebene.

Der Trace: Was Wireshark auf Paketebene sichtbar macht

Das Vorgehen war bewusst beidseitig angelegt: ein Mitschnitt am Client, ein Mitschnitt serverseitig – zeitlich synchronisiert, während ein Anwender exakt den problematischen Maskenaufbau durchführt. Der beidseitige Trace ist wichtig, weil er später die Frage beantwortet, wo gewartet wird: im Netz, am Server oder am Client. Der relevante Mitschnitt lief unter der internen Bezeichnung Trace 07/23.

Der erste Blick galt den üblichen Verdächtigen – und die waren alle unschuldig:

  • Kein Packet Loss: Keine einzige Retransmission, keine Duplicate ACKs, keine Zero-Window-Ereignisse.
  • Unauffällige Round Trip Time: Die RTT zwischen Client und Datenbankserver lag stabil bei etwa 10 bis 15 Millisekunden – für die gegebene Infrastruktur ein völlig normaler Wert. Wie du RTT sauber misst und bewertest, findest du im Beitrag Hohe Latenz im Firmennetz – RTT analysieren.
  • Saubere TCP-Verbindung: Handshake unauffällig, Window-Größen ausreichend, keine Anzeichen für Engpässe auf Transportebene.

Nach zwanzig Minuten war klar: Das Netzwerk transportiert jedes Paket schnell und fehlerfrei. Und trotzdem dauert die Maske 30 Sekunden. Also musste die Zeit woanders stecken.

Erste Auffälligkeit: Ping-Pong statt Datenstrom

Wer einen TCP-Stream einer performanten Datenübertragung anschaut, sieht typischerweise Bursts: Der Server schickt viele volle Segmente hintereinander, der Client bestätigt, der Durchsatz steigt. Wie man solche Streams liest und interpretiert, habe ich im Grundlagenartikel Wie lese ich einen TCP Stream richtig? beschrieben.

Dieser Trace sah völlig anders aus. Statt Bursts zeigte sich ein streng abwechselndes Muster: Client sendet einen kleinen Request (wenige hundert Bytes), Server antwortet mit einem kleinen Response, Client sendet den nächsten Request. Immer schön der Reihe nach, niemals parallel, niemals überlappend. Ein permanentes Ping-Pong.

Im IO-Graph war das eindrucksvoll sichtbar: keine Durchsatzspitzen, sondern eine flache Linie aus tausenden Mini-Transaktionen. Die Verbindung war zu keinem Zeitpunkt auch nur ansatzweise ausgelastet – sie war beschäftigt mit Warten.

Das Muster erkennen: 1 Query für die Liste, N Queries für die Details

Der inhaltliche Blick in die Datenbank-Kommunikation machte das Muster dann greifbar. Zu Beginn des Maskenaufbaus setzt die Anwendung eine einzige Abfrage ab, die eine Liste von Datensätzen zurückliefert – die Einträge, die in der Übersichtsmaske angezeigt werden sollen.

Und dann kommt der entscheidende Teil: Für jeden einzelnen Listeneintrag folgt eine separate Detailabfrage. Ein Eintrag, ein Query. Der nächste Eintrag, der nächste Query. Bei rund 2.500 Einträgen in der Liste ergibt das rund 2.500 zusätzliche Einzelabfragen – jede für sich winzig, jede für sich schnell beantwortet, aber jede wartet auf die vollständige Antwort der vorherigen, bevor sie abgeschickt wird.

Damit war das Muster eindeutig identifiziert: das klassische N+1 Query Problem. Eine Abfrage für die Liste, N Abfragen für die Details.

Das N+1-Query-Problem einfach erklärt

Das N+1-Pattern entsteht meist nicht aus Böswilligkeit, sondern aus der Art, wie viele Anwendungen und Frameworks mit Daten umgehen. Die Logik ist aus Entwicklersicht naheliegend: “Hole mir die Liste. Und für jedes Element der Liste hole mir die Details.” In einer Schleife programmiert, funktioniert das in der Entwicklungsumgebung tadellos – denn dort liegt die Datenbank auf demselben Rechner oder im selben Rack, und die RTT beträgt Bruchteile einer Millisekunde.

Im echten Unternehmensnetz sieht die Welt anders aus. Zwischen Client und Datenbank liegen Switches, Router, vielleicht eine Firewall, vielleicht ein WAN-Segment. Selbst ein hervorragend funktionierendes Netz hat dann eine RTT von 10, 12 oder 15 Millisekunden. Für eine Abfrage ist das irrelevant. Für 2.500 streng sequentielle Abfragen ist es fatal.

Die Mathematik dahinter: Requests × RTT = Wartezeit

Der Kern des Problems lässt sich in einer einzigen Formel ausdrücken:

Gesamtzeit = Anzahl sequentieller Requests × RTT

Setzen wir die Werte aus dem Trace ein:

2.500 Requests × 12 ms RTT = 30.000 ms = 30 Sekunden

Das ist exakt die Wartezeit, die die Anwender erleben – 30 Sekunden reine Netzwerk-Wartezeit bei praktisch 0 % Netzwerkauslastung. Kein einziges Paket ging verloren, kein einziges Paket war langsam. Die Zeit entsteht ausschließlich dadurch, dass die Anwendung 2.500 Mal hintereinander eine komplette Runde durch das Netz wartet, bevor sie den nächsten Schritt macht.

Und hier liegt auch die unbequeme Wahrheit für alle, die reflexartig mehr Bandbreite fordern: Diese Rechnung enthält keine Bandbreite. Ob die Leitung 100 Mbit/s oder 10 Gbit/s hat, ändert am Ergebnis nichts. Die einzige netzwerkseitige Stellschraube wäre die RTT – und die war mit 12 ms bereits sehr gut. Selbst eine Halbierung auf utopische 6 ms würde die Maske immer noch 15 Sekunden lang laden lassen. Die eigentliche Stellschraube ist die Anzahl der sequentiellen Requests – und die kontrolliert allein die Applikation.

Die Beweisführung: So grenzt du Netzwerk vs. Applikation sauber ab

Ein Befund ist nur so gut wie seine Beweisführung. Gerade in einer Fingerpointing-Situation muss die Analyse so belastbar sein, dass beide Seiten sie akzeptieren. Das Vorgehen dafür ist immer dasselbe und lässt sich auf jeden vergleichbaren Fall übertragen.

Die Kernfrage lautet: Wer verursacht die Wartezeit – das Netz, der Server oder der Client? Dafür zerlegst du jede einzelne Transaktion in ihre Zeitanteile:

  • Netzlaufzeit: Zeit zwischen dem Absenden eines Pakets und dem zugehörigen TCP-ACK der Gegenseite. Das ist die reine Transportzeit.
  • Serverseitige Antwortzeit: Zeit zwischen dem Eintreffen des Requests am Server (im serverseitigen Trace) und dem Absenden der Antwort. Das ist die Verarbeitungszeit der Datenbank.
  • Clientseitige Denkzeit: Zeit zwischen dem Empfang einer vollständigen Antwort und dem Absenden des nächsten Requests. Das ist die Zeit, die die Applikation selbst verbraucht.

In diesem Fall zeigte die Zerlegung ein klares Bild: Die Datenbank antwortete auf jede Einzelabfrage in unter einer Millisekunde. Die Netzlaufzeit lag konstant bei den erwarteten 12 ms. Die Summe der Wartezeit ergab sich fast vollständig aus RTT × Anzahl der Requests – plus einem kleinen Anteil clientseitiger Verarbeitungszeit zwischen den Requests. Weder Netz noch Datenbankserver hatten ein Performanceproblem. Das Kommunikationsmuster war das Problem.

Welche Wireshark-Filter und -Funktionen dabei helfen

Die wichtigste Einzelmetrik für diese Art von Analyse ist der Zeitabstand zwischen aufeinanderfolgenden Paketen innerhalb eines TCP-Streams:

tcp.time_delta

Dieses Feld kannst du dir als eigene Spalte in Wireshark einblenden (Rechtsklick auf das Feld unter “Timestamps” im TCP-Header → “Apply as Column”). Sortierst oder filterst du danach, siehst du sofort, zwischen welchen Paketen die Zeit verloren geht – und auf welcher Seite der Verbindung.

Um gezielt größere Lücken innerhalb der Datenbank-Verbindung zu finden, hilft ein Filter wie dieser:

tcp.stream == 5 && tcp.time_delta > 0.010

Damit siehst du alle Pakete im betreffenden Stream, vor denen mehr als 10 ms Pause lag. In diesem Fall lieferte der Filter tausende Treffer – und jeder einzelne war ein Request, der brav auf die Antwort seines Vorgängers gewartet hatte.

Für die Gesamtbetrachtung sind außerdem drei Bordmittel von Wireshark Gold wert:

  • Statistics → Conversations: Zeigt dir pro TCP-Verbindung die Anzahl der Pakete, Bytes und die Dauer. Eine Verbindung mit zehntausenden kleinen Paketen und minimalem Datenvolumen über 30 Sekunden ist bereits der halbe Befund.
  • Statistics → IO Graph: Visualisiert das Ping-Pong-Muster eindrucksvoll – ideal auch für den Bericht an nicht-technische Entscheider. Eine flache Linie statt Durchsatz-Bursts erzählt die ganze Geschichte in einem Bild.
  • Statistics → TCP Stream Graphs → Round Trip Time: Belegt, dass die RTT konstant und unauffällig war – der Freispruch für das Netzwerk in grafischer Form.

Wenn du mit Display-Filtern noch nicht sattelfest bist: Im Beitrag Wireshark – Die 10 wichtigsten Display-Filter findest du das Handwerkszeug, das du für genau solche Analysen brauchst.

Das Ergebnis: Klare Fakten statt Schuldzuweisungen

Am Ende der Analyse stand ein Bericht, der beide Streitparteien an einen Tisch brachte – nicht mit Meinungen, sondern mit Paketdaten. Die Kernaussagen im Überblick:

Verdacht Befund im Trace Bewertung
Packet Loss / Retransmissions Keine Retransmissions, keine Duplicate ACKs Netzwerk entlastet
Hohe Latenz im Netz RTT konstant 10–15 ms, keine Ausreißer Netzwerk entlastet
Langsamer Datenbankserver Antwortzeit pro Query unter 1 ms Server entlastet
Datenzugriffsmuster der Applikation ~2.500 streng sequentielle Einzel-Queries pro Maskenaufbau (N+1) Ursache bestätigt

Die Empfehlungen an den Applikationshersteller ergaben sich direkt aus dem Befund: Batching der Detailabfragen (die Details für alle Listeneinträge in einer oder wenigen Abfragen laden, z. B. per Join oder IN-Liste), Prefetching bzw. eine höhere Array-Size beim Abholen von Ergebnismengen, und generell die Abkehr von Einzel-Fetches in Schleifen.

Das Bemerkenswerte: Sobald die Paketdaten auf dem Tisch lagen, war der Konflikt innerhalb eines einzigen Termins beigelegt. Der Hersteller konnte den Befund nachvollziehen und nahm die Optimierung in seine Entwicklung auf. Die IT-Abteilung war rehabilitiert. Und die Anwender bekamen eine realistische Perspektive, statt weiter zwischen den Fronten zu stehen. Niemand kann mit einem Wireshark-Trace diskutieren, in dem jede Millisekunde nachvollziehbar zugeordnet ist.

Was du aus diesem Fall mitnehmen kannst

  • Unauffälliges Monitoring beweist nicht, dass das Netzwerk unschuldig ist – aber auch nicht das Gegenteil. Bandbreiten- und Ping-Dashboards sind für Latenz-getriebene Applikationsprobleme schlicht das falsche Werkzeug.
  • Latenz schlägt Bandbreite. Bei sequentiellen Request-Response-Mustern bestimmt allein RTT × Anzahl der Requests die Wartezeit. Mehr Bandbreite hilft exakt gar nichts.
  • Beidseitig tracen lohnt sich. Nur mit Client- und Server-Mitschnitt kannst du Netzlaufzeit, Server-Antwortzeit und Client-Denkzeit sauber voneinander trennen.
  • tcp.time_delta ist deine wichtigste Spalte. Wer Zeitlücken im Stream sichtbar macht, findet die Ursache – egal ob sie im Netz, am Server oder in der Applikation liegt.
  • Das N+1-Pattern ist häufiger, als du denkst. Es versteckt sich in Fachanwendungen, ORM-Frameworks und Eigenentwicklungen – und fällt fast immer erst auf, wenn zwischen Anwendung und Datenbank echte Netzwerklatenz liegt.
  • Fakten beenden Fingerpointing. Eine saubere paketbasierte Beweisführung ist oft günstiger als Wochen des Stillstands zwischen Hersteller, IT und Fachbereich.

Fazit: Paketdaten lügen nicht

Dieser Fall zeigt exemplarisch, warum sich der Blick auf die Paketebene lohnt, wenn Monitoring und Bauchgefühl nicht weiterhelfen. Wochenlang standen sich zwei Parteien mit plausiblen, aber unvollständigen Messwerten gegenüber. Der Wireshark-Trace hat die Diskussion in einem Termin beendet – nicht weil er lauter argumentiert hätte, sondern weil er jede einzelne Millisekunde der Wartezeit einer Ursache zuordnen konnte.

Das Netzwerk war unschuldig. Die Datenbank war unschuldig. Schuldig war ein Kommunikationsmuster, das in der Entwicklungsumgebung unsichtbar bleibt und im Unternehmensnetz zur Bremse wird: 2.500 sequentielle Requests, jeder eine RTT lang wartend.

Wenn du selbst gerade vor einer langsamen Anwendung stehst, deren Monitoring keinerlei Auffälligkeiten zeigt: Genau dann ist der richtige Zeitpunkt für einen Blick in den Trace. Denn am Ende gilt, was in jedem meiner Fälle gilt – Paketdaten lügen nicht.

Das Problem ist reproduzierbar – aber die Ursache bleibt im Dunkeln?

Dann braucht es einen zweiten Blick auf die Pakete. Wireshark kennt die Antwort – manchmal fehlt nur die Erfahrung, sie darin zu lesen.

Als spezialisierter Netzwerkanalyst helfe ich dir dabei:

  • Ursachen präzise zu lokalisieren – statt auf Verdacht Hardware zu tauschen.
  • Komplexe Packet-Traces auszuwerten und in klare Handlungsempfehlungen zu übersetzen.
  • Die Schuldfrage objektiv zu klären – mit Beweisen auf Paketebene, die das Fingerpointing zwischen Netzwerk- und Applikationsteam beenden.

Oft führt ein gemeinsamer, fokussierter Blick auf die Mitschnitte schneller zum Ziel als tagelanges Rätselraten. Im ersten Gespräch schauen wir uns gemeinsam die Ausgangslage an – Symptome, bisheriges Troubleshooting und mögliche nächste Schritte – bevor wir entscheiden, wie wir weiter vorgehen.

Schreib mir eine kurze Nachricht mit deiner Herausforderung – ich melde mich für ein unverbindliches Erstgespräch.

Edit Template

Schreibe einen Kommentar

Your email address will not be published. Required fields are marked *