SQL-Performance & Datenbankoptimierung

Langsame Datenbank? Ich finde die Stellen, an denen sich alles staut.

Seit rund 30 Jahren arbeite ich mit SQL-Datenbanken – vom Controlling-Report bis zu Milliarden Datensätzen in der Halbleiterproduktion. Hunderte Abfragen, Prozeduren und Nachtläufe habe ich in dieser Zeit beschleunigt. Auf dieser Seite zeige ich, wie ich vorgehe, was ich erlebt habe und was sich messen lässt.

Ihre langsamste Abfrage prüfen lassen

~30 Jahre

SQL in der Praxis

Datenbankentwicklung, Performance-Tuning und Datenmodellierung in Konzern-IT, Produktion, Medizinstudien (FDA-konform) und Mittelstand – Schwerpunkt MS SQL Server (T-SQL, SSIS, SSRS) sowie Oracle (PL/SQL), MySQL, PostgreSQL und Redis.

64 Mrd. Zeilen

Hochlast & Milliarden-Bestände

Bei NXP Semiconductors wurden monatlich rund 1,4 Mrd. neue Seriennummern gegen 64 Mrd. Bestandszeilen auf Duplikate geprüft – über Insert-only-Tabellen, entkoppelte Redis-Queues, verteilte Satelliten-Datenbanken und Batch-Migration im laufenden 24/7-Betrieb.

bis über ×600

Nachgemessen im eigenen SQL-Labor

Statt Bauchgefühl nutze ich ein automatisiertes Messlabor für Ausführungspläne, Logical Reads, Plan-Cache und Sperren – Index-Matrix und 6 weitere Labor-Messreihen ansehen.

Aus der Praxis

Drei Fälle, die immer wiederkehren

Die Beispiele sind anonymisiert (Kundendaten gebe ich grundsätzlich niemals weiter). Werte aus der Erinnerung sind als Größenordnung angegeben – die exakten Ursachen und Wirkungen habe ich im Laborabschnitt darunter an 10 Millionen Zeilen reproduzierbar nachgestellt und gemessen.

Prozedur · Sperren

Der Fehler, der nur in der Produktion auftrat

Ein Kunde in Wien erlebte im laufenden Betrieb Blockierungen durch Transaktionen, die sich im Testsystem nie nachstellen ließen. Ursache war eine Stored Procedure, die ihre Abfrage je nach Fall per String-Verkettung zusammensetzte und dynamisch ausführte.

Ich habe sie in einen Verteiler (Delegator) und zwei Prozeduren mit festem, parametrisiertem T-SQL aufgeteilt. Damit prüft SQL Server die Abfragen vorab und nutzt für jeden Zweig einen stabilen Ausführungsplan im Plan-Cache. Der Fehler ist danach nie wieder aufgetreten.

Index · ~×100

Tage am Code, Minuten am Index

Ein Entwickler hatte mehrere Tage versucht, eine langsame Abfrage im C#-Code zu beschleunigen. Die Ursache lag in der Datenbank: Durch einen fehlenden abdeckenden Index musste SQL Server Millionen Zeilen per Scan und Key Lookup lesen.

Nach Anlage des passenden Index lief die Abfrage rund hundertmal schneller – von Minuten Wartezeit auf wenige Millisekunden, ohne eine einzige Zeile Programmcode in der Anwendung zu ändern.

Nachtlauf · Stunden

Von mehreren Stunden auf gut eine Stunde

Nächtliche Reports und ETL-Strecken, die morgens nicht fertig sind, bleiben selten allein: Wer lange läuft, hält Exklusiv- und Seitensperren (`LCK_M_*`) und bremst die Prozesse neben sich – die wiederum andere blockieren.

Solche Nachtläufe habe ich regelmäßig von mehreren Stunden auf gut eine Stunde gebracht, indem ich zu große Transaktionen aufgelöst, Schleifen durch mengenorientiertes SQL ersetzt und die wenigen Engpässe im Ausführungsplan beseitigt habe.

Gemessen statt behauptet

Was ein passender Index ausmacht – Stufe für Stufe gemessen

Dieselbe Suchabfrage (`WHERE Nachname = … AND Stadt = … AND GeschaeftsJahr >= …`) auf einer Tabelle mit 1 und 10 Millionen Zeilen, jeweils mit einer besseren Indexstufe. Die Messung läuft vollautomatisch per PowerShell- und T-SQL-Skript und ist jederzeit reproduzierbar.

Ausgangszustand (Stufe 0 · Kein Index) 1.684 ms Laufzeit · 350.018 Reads
Optimiert (Stufe 4 · Covering Index) 2,7 ms Laufzeit · 11 Reads
Zeiteinsparung & Effizienz ×624 schneller (-99,8 % Zeit · -99,997 % I/O)
Laufzeit und logische Lesevorgänge (8-KB-Seiten) je Indexstufe bei 1 Mio. und 10 Mio. Zeilen (Faktor gegenüber Stufe 0)
Indexstufe Laufzeit
1 Mio. Zeilen
Laufzeit
10 Mio. Zeilen
Logical Reads
10 Mio. Zeilen
Operatoren im Ausführungsplan
(10 Mio. Zeilen)
0 · Kein Index (Heap / PK-Scan)173 ms1.684 ms350.018Clustered Index Scan, Parallelism
1 · Index auf nur eine Spalte165 ms ×1,051.670 ms ×1,01350.167 ×1,00Clustered Index Scan (Tipping Point)
2 · Ein Einzel-Index je Spalte61 ms ×2,896 ms ×17,53.872 ×902× Index Seek + Merge Join + Key Lookup
3 · Zusammengesetzter Index30 ms ×5,753 ms ×322.628 ×133Index Seek + Key Lookup (Nested Loops)
4 · Covering Index (INCLUDE)2,3 ms ×752,7 ms ×62411 ×31.820Reiner Index Seek (kein Key Lookup)
5 · Gefilterter Covering Index1,3 ms ×1333,3 ms ×5104 ×87.505Reiner Index Seek (Index nur 86 MB statt 1.159 MB)

Messverfahren: Microsoft SQL Server, 10 Mio. Zeilen (ca. 2,7 GB Basistabelle), kalter Buffer Pool vor jedem Lauf (CHECKPOINT; DBCC DROPCLEANBUFFERS), Ausführungsplan vorab erzeugt und getrennt von parse and compile time über SET STATISTICS IO, TIME, XML ON ausgewertet, Mittelwert aus mindestens drei Durchläufen. Laufzeiten im einstelligen Millisekundenbereich schwanken; die logischen Lesevorgänge (Logical Reads) sind die exakt reproduzierbare Vergleichszahl.

Zwei Details aus dem Labor, die in der Praxis oft übersehen werden: Erstens ignoriert der Query Optimizer in Stufe 1 den Einzel-Index auf Nachname komplett (271.384 Treffer bei 10 Mio. Zeilen liegen über dem Tipping Point für Key Lookups) und fällt auf einen vollen Clustered Index Scan zurück. Zweitens schrumpft der Index durch den Filter in Stufe 5 von 1.159 MB auf nur 86 MB (und reduziert die INSERT-Zeit von 9,4 s auf 1,5 s) – wird die Abfrage jedoch parametrisiert ohne OPTION (RECOMPILE) aufgerufen, darf SQL Server den gefilterten Index wegen Parameter Sniffing nicht nutzen und fällt wieder auf 349.977 Reads (1.657 ms) zurück!

Weitere Beweise aus meinem SQL-Labor

Sechs Engpässe, die ich im Labor exakt nachgemessen habe

Ein fehlender Index ist nur der Anfang. Für meine Beratungs- und Optimierungspraxis habe ich ein eigenes Test-Framework aufgebaut, das typische Architekturentscheidungen an 1 bis 10 Millionen Zeilen inklusive Showplan-XML, CPU-Zeit und Memory Clerks vermisst.

Collation & Data Type Precedence · 10 Mio. Zeilen

Implizite Konvertierung: 3 Reads gegen 27.469 Reads

Wenn eine Anwendung einen Unicode-Parameter (@p nvarchar, Standard in .NET/EF) gegen eine varchar-Spalte schickt, hängt das Verhalten an der Sortierung (Collation):

  • Optimal: 1,0 ms (0 ms CPU) · 3 Reads (Index Seek)
  • Collation-Mismatch: 318 ms (+2.120 ms CPU) · 27.469 Reads (Index Scan)
  • Typ-Mismatch (int): 287 ms (+1.864 ms CPU) · 27.469 Reads (Index Scan)

Unter SQL-Sortierung oder bei varchar = int muss SQL Server jede einzelne Zeile umwandeln (Plan-Warnung: CONVERT_IMPLICIT). Aus 3 Seitengriffen und 0 ms CPU werden 27.469 Reads und über 2 Sekunden verbrannte CPU-Zeit je Einzelabfrage.

Mengenübergabe & Plan-Cache · 10 Mio. Zeilen

Lange IN-Listen (2.000 IDs) und Ad-hoc-Flut

Wie übergibt man Hunderte oder Tausende IDs aus C# an SQL Server? Ich habe vier Verfahren bei 10 bis 2.000 IDs und bei 50.000 Einzelaufrufen gegeneinander gemessen:

  • TVP (2.000 IDs): 172 ms (0 ms Compile) · 200 KB Cache
  • Literal-Liste: 591 ms (196 ms Compile) · 6.376 KB Cache
  • 50.000 Ad-hoc: 781 MB Plan-Müll vs. 0,1 MB (parametrisiert)

Einzelparameter (@p1 … @p1000) brechen schon bei 1.000 IDs durch Merge Interval / Concatenation auf 565 ms und 7,1 MB Plan-Größe ein. Table-Valued Parameters (TVP) liefern konstant einen wiederverwendbaren Plan ohne Kompilierlast.

Datenmodell & Speicher · 1 Mio. Kind- / 50.000 Elternzeilen

Zusammengesetzter Textschlüssel vs. Surrogate Key (int)

Ein dreiteiliger Fachschlüssel (Mandant + Bereich + Artikelnummer, im Schnitt 56 Zeichen) wiederholt sich in jeder Kindtabelle und in jedem Fremdschlüssel-Index:

  • Technischer int-Key: 0,3 ms · 108 MB Speicher (Faktor ×2,83 schlanker)
  • 3-teiliger Textschlüssel: 18,3 ms · 305 MB Speicher (60× langsamer)

Der technische 4-Byte-Schlüssel spart fast zwei Drittel des Speicher- und Buffer-Pool-Bedarfs (während der Fachschlüssel als UNIQUE-Constraint auf der Elterntabelle erhalten bleibt). Für bestehende Kundendatenbanken habe ich ein Katalog-Skript entwickelt, das diese Ersparnis vorab rein aus sys.dm_db_partition_stats und sys.index_columns ohne Lese-Last auf den Nutzdaten berechnet.

SARGability · 10 Mio. Zeilen

Führender Platzhalter (LIKE '%berg'): von 4,2 s auf 3,3 ms

Eine Endungssuche wie WHERE Firma LIKE '%berg' ist nicht SARGable – selbst mit Index auf Firma liest SQL Server alle 49.062 Indexseiten (21.297 ms CPU-Zeit, 4.231 ms Laufzeit im Parallel-Scan).

  • REVERSE + Index: 3,3 ms (0 ms CPU) · 45 Reads
  • LIKE '%berg' (Scan): 4.231 ms (21.297 ms CPU) · 49.062 Reads

Mit einer berechneten Spalte FirmaRev AS REVERSE(Firma) und einem Index darauf wird aus der Endungssuche eine Präfixsuche (LIKE REVERSE(@p)). Die Lesevorgänge sinken von 49.062 auf 45 Reads (Faktor ×1.090 weniger I/O), die Laufzeit von 4.231 ms auf 3,3 ms (über ×1.200 schneller).

Server- & Datenbankgrenzen · 1 Mio. Zeilen

Linked Server vs. OPENQUERY und Collation-Falle

Abfragen über Server- und Datenbankgrenzen hinweg kippen je nach Selektivität und Sortierung in völlig unterschiedliche Ausführungspläne:

  • Gleiche Collation: 13 ms · 66 Reads (Index Seek)
  • Abweichende Collation: 1.383 ms · 7.882 Reads (Faktor ×119 I/O)
  • OPENQUERY (Punkt): 5,9 ms · (vs. 4-teilig: 210 ms · ×35 schneller)

Bei Einzelabfragen schiebt OPENQUERY den Filter auf den Zielserver; beim Gesamtverbund zwingt es dagegen in tausende lokale Nested-Loops-Seeks. Und weicht die Collation der Kinddatenbank ab, verhindert eine implizite Konvertierung den Index Seek (Faktor ×119 mehr Reads).

Normalisierung & Datenqualität · 1 Mio. Zeilen

Klartext-Spalten vs. Nachschlagetabellen (3NF)

Stehen Werte wie Stadt und Zahlungsart als Klartext in der Bewegungstabelle statt als int-Fremdschlüssel auf eine Nachschlagetabelle, leidet nicht nur die Geschwindigkeit:

  • Normalisiert: 15 ms (432 Reads) · Widerspruchsprüfung: 2 ms (28 Reads)
  • Klartext: 160 ms (3.438 Reads) · Widerspruchsprüfung: 147 ms (4.655 Reads)

Auf der kleinen Nachschlagetabelle (5.000 Städte) kostet selbst ein LIKE '%berg' fast nichts, bevor über die schlanke StadtId gejoint wird. Gleichzeitig verhindert das Modell abweichende Schreibweisen derselben Stadt zur selben Postleitzahl.

Typische Ursachen

Woran Datenbanken meistens langsam werden

Nach Hunderten Optimierungen wiederholen sich die Muster. Diese sechs Bremsen finde ich in fast jeder gewachsenen Datenbank.

Fehlende Covering Indexes & Key Lookups

Ein Index pro Spalte hilft wenig. Entscheidend sind Spaltenreihenfolge (Gleichheit vor Bereich), abdeckende Spalten (INCLUDE gegen teure Key Lookups) und gefilterte Indexe – samt Beachtung von Parameter Sniffing und OPTION (RECOMPILE).

Implizite Konvertierung (CONVERT_IMPLICIT)

Passen Datentyp oder Collation von Spalte und Parameter nicht zusammen (z. B. varchar-Spalte gegen nvarchar aus Entity Framework), wandelt SQL Server jede Zeile beim Lesen um – und fällt vom Index Seek auf einen kompletten Scan zurück.

Ad-hoc-SQL, Plan-Cache-Bloat & Parameter Sniffing

Als Text verkettete Abfragen fluten den Plan-Cache mit Einmal-Plänen, kosten bei jedem Aufruf CPU-Kompilierzeit und verhindern stabile Ausführungspläne. Parametrisierte Prozeduren und Table-Valued Parameters (TVP) lösen das Problem dauerhaft.

Fehlende SARGability (Funktionen & führende Wildcards)

Bedingungen wie LIKE '%berg' oder Funktionen auf Indexspalten (YEAR(Datum) = …, ISNULL(Spalte, …)) machen vorhandene Indexe blind. Umgestellte Prädikate oder indizierte berechnete Spalten stellen den Index Seek wieder her.

Breite Clustered Keys & fehlende Normalisierung

Zusammengesetzte Text-Primärschlüssel vervielfachen sich in jedem Fremdschlüssel und jedem Nonclustered Index der Kindtabellen. Technische Schlüssel (int/bigint) halbieren oft den RAM-Bedarf im Buffer Pool.

Sperrketten (LCK_M_*), Lock Escalation & RCSI

Große Transaktionen und Cursorschleifen halten Sperren und lösen Kettenblockaden aus – besonders in Nachtläufen. Mengenorientiertes SQL, kleinere Transaktionsschritte und passende Isolationsstufen lösen den Stau auf.

Methodik & eigene Diagnose-Werkzeuge

Keine Blindflüge: Wie ich analysiere, ohne Ihren Betrieb zu gefährden

In gewachsenen Datenbanken hat jede Änderung Nebenwirkungen: Welche Prozedur, welche View, welcher SQL-Agent-Job oder welches SSIS-Paket bricht, wenn man eine Tabelle oder einen Schlüssel anfasst? Deshalb verlasse ich mich nicht auf Vermutungen, sondern auf deterministische Werkzeuge.

1 · Katalog & DMVs

Diagnose ohne Last auf Ihren Nutzdaten

Meine Analyse-Skripte werten Systemkataloge (sys.dm_db_partition_stats, sys.dm_exec_query_stats, sys.index_columns, Showplan-XML) aus, ohne große Tabellen zu scannen. So lässt sich schon vor einem Eingriff auf das Megabyte genau vorhersagen, was ein Schlüssel- oder Indexumbau bringt.

2 · COMPIDDA

Technische Abhängigkeiten vorab komplett sichtbar machen

Mit COMPIDDA habe ich ein eigenes Analyse-Werkzeug (mit über 4.000 automatisierten Tests) entwickelt, das T-SQL-Prozeduren, Views, Trigger, Dynamisches SQL, OPENQUERY, Linked Server, SSIS-Pakete, Jobs und C#-Zugriffe über den offiziellen SQL-Server-Syntaxbaum (ScriptDom) einliest und als Abhängigkeitsgraph mit exakter Fundstelle darstellt.

3 · KI-gestützt & testgetrieben

30 Jahre Erfahrung kombiniert mit moderner Werkzeug-Geschwindigkeit

Ich nutze moderne KI-Werkzeuge gezielt als Beschleuniger für Analyse-Skripte und Testfälle – gesteuert durch 30 Jahre Wissen über SQL-Server-Interna und abgesichert durch harte, deterministische Vorher-/Nachher-Messungen (Prüfsummen-Abgleich, Logical Reads, Ausführungsplan). Geraten wird an Ihrer Datenbank nichts.

Vorgehen

Minimalinvasiv – der laufende Betrieb bleibt sicher

Wenige Eingriffe an den wirksamsten Stellen

Wo keine ausreichenden Tests vorhanden sind, schreibe ich keinen Code im großen Stil um. Ich setze gezielt an den Stellen an, die am meisten bewirken – oft an Indexen, Statistiken und einzelnen Stored Procedures, ohne die Anwendung anzufassen. Wenn tiefere Umbauten nötig sind, sichern wir die abhängigen Pfade vorab mit automatisierten Tests und Prüfsummen ab.

Index vor Code vor Hardware

Eine Indexoptimierung wirkt oft innerhalb von Stunden. Änderungen an der Programmierung brauchen Tage bis Monate. Neue Hardware kostet Beschaffungszeit, eine Auszeit des Servers und Geld – und bringt meist nur einen Bruchteil dessen, was ein passender Index erreicht (im Labor schlägt ein auf 2 Kerne und 512 MB RAM gedrosselter SQL Server mit Covering Index einen ungedrosselten 8-Kern-Server ohne Index um das Hundertfache). Deshalb prüfe ich strikt in dieser Reihenfolge.

Ehrlich gesagt

Warum ich keine erfundenen Erfolgszahlen nenne

In Ausschreibungen werde ich oft gefragt, welche Kundenabfrage ich vor Jahren um welchen exakten Prozentwert beschleunigt habe. Die ehrliche Antwort: Einerseits erinnere ich nach über 30 Jahren Praxis nicht mehr jeden einzelnen Fall und die genauen Messzahlen dazu. Andererseits gebe ich aus beruflicher Diskretion niemals Kundendaten, interne Schemas oder vertrauliche Produktionsdaten weiter. Historische Protokolle aus fremden Kundensystemen nimmt ein seriöser Architekt nicht mit nach Hause – und Phantasiezahlen für den Lebenslauf erfinde ich nicht.

Daher habe ich typische erinnerte Fälle beweisbar mit echten Messungen in meinem eigenen SQL-Labor nachgestellt: vollautomatisiert, auf 1 bis 10 Millionen Zeilen, mit offengelegtem T-SQL, Showplan-XML und exakten Seitenzugriffen. Was ich Ihnen für Ihre Entscheidung liefere, ist greifbar und belastbar – und gerne trete ich den direkten Vorher-/Nachher-Beweis an Ihrer eigenen Datenbank an.

Der Beweis auf Ihren Daten

Geben Sie mir Ihre langsamste Abfrage oder Ihren kritischsten Nachtlauf

Ich analysiere Ausführungsplan, Lesevorgänge und Sperrverhalten, schlage die Maßnahme vor und messe vorher und nachher per Skript – unter denselben strengen Bedingungen wie in meinem SQL-Labor oben. So sehen Sie die exakte Wirkung an Ihrem eigenen System, bevor Sie über ein größeres Projekt entscheiden.

Projekt oder Erstanalyse anfragen

„Er arbeitet sehr strukturiert, analytisch und eigenverantwortlich …“

Auszug aus einer schriftlichen Referenz · STOCK SPIRITS

„Herr Hagen war in ganz besonders hohem Maße zuverlässig.“

Auszug aus einer schriftlichen Referenz · NXP Semiconductors

Weitere Referenzen auf der Startseite; vollständige Nachweise auf Anfrage.