SQL Server · Ausführungspläne · Indizes · Partitionierung · Wait Stats

SQL Performance Tuning – systematisch, messbar und nachhaltig

Langsame Abfragen, überlastete Datenbanken und unkontrolliert wachsende Laufzeiten kosten Geld und Nerven. Ich analysiere SQL-Server-Systeme systematisch, identifiziere die eigentlichen Engpässe und beseitige sie – durch fundierte Index-Strategien, optimierte Ausführungspläne, saubere Statistiken, Tabellen-Partitionierung und gezieltes Monitoring. Erfahrung seit 1994, MCSA SQL Server 2012.

Positionierung

Performance-Probleme in SQL-Server-Umgebungen sind selten zufällig. Sie entstehen, wenn Abfragen ohne Blick auf Ausführungspläne geschrieben werden, wenn Indizes fehlen oder veraltet sind, wenn Statistiken nicht mehr zur Datenlage passen oder wenn Tabellen wachsen, ohne dass die Datenbankarchitektur darauf ausgelegt ist. Das Ergebnis sind schleichend steigende Laufzeiten, Timeouts in Anwendungen und irgendwann ein System, das unter Last zusammenbricht. Der Schmerz ist real, die Ursache aber fast immer analysierbar und behebbar.

Genau hier liegt mein Schwerpunkt: Ich analysiere SQL-Server-Systeme von innen heraus, mit den Bordmitteln, die SQL Server selbst bereitstellt. Dynamic Management Views, Ausführungspläne, Wait Statistics und der Query Store liefern präzise Diagnosen, ohne dass teure Zusatzwerkzeuge nötig sind. Diese Diagnose führt dann zu konkreten Maßnahmen: ein fehlender Index, eine umformulierte Abfrage, eine Statistikaktualisierung, ein Plan-Guide gegen Parameter-Sniffing oder eine Partitionierung, die einen jahrelang wachsenden Bestand handhabbar macht.

Mein Hintergrund reicht bis 1994 zurück, und ich habe Performance-Probleme in sehr unterschiedlichen Kontexten gelöst: in Transaktionssystemen mit hohem OLTP-Aufkommen, in Data Warehouses mit großen Batch-Ladevorgängen, in SSIS-Strecken mit suboptimalem Datenfluss und in Reporting-Systemen, die unter Last inakzeptabel langsam wurden. MCSA SQL Server 2012 (MOC 70-461/462/463) und Projekterfahrung über alle SQL-Server-Versionen von 2000 bis 2025 sind dabei keine Zertifikatsnummern, sondern die Grundlage dafür, dass ich weiß, wie sich das Verhalten des Servers über Versionen verändert hat und welche Optimierungshebel in welchem Kontext wirken.

Auftraggeber holen mich, wenn ein System zu langsam geworden ist und niemand im Team weiß warum, wenn ein Release bevorsteht und kritische Abfragen noch nicht akzeptable Laufzeiten haben, oder wenn eine nachhaltige Performance-Strategie fehlt, die über das Ad-hoc-Setzen von Indizes hinausgeht. In all diesen Fällen geht es nicht um kosmetische Korrekturen, sondern um eine fundierte Diagnose und Maßnahmen, die dauerhaft wirken.

Kerngedanke: SQL-Performance-Tuning beginnt mit Messen, nicht mit Raten. Wer ohne Diagnose optimiert, behebt häufig das falsche Problem. Erst die genaue Kenntnis des Engpasses – ob fehlender Index, veraltete Statistik, schlechter Plan oder Ressourcenkonflikt – erlaubt gezielte, dauerhafte Verbesserungen.

Was Performance-Tuning umfasst

SQL Server Performance Tuning ist kein einzelner Handgriff, sondern ein Bündel von Disziplinen, die ineinandergreifen. Wer nur Indizes setzt, ohne die Abfrageformulierung zu prüfen, löst vielleicht ein Symptom, nicht die Ursache. Wer Statistiken pflegt, aber Wait Statistics ignoriert, übersieht möglicherweise einen I/O-Engpass, der alle Bemühungen zunichtemacht. Ein vollständiges Tuning-Bild umfasst mehrere Ebenen.

Abfrageebene

Die Abfrageebene ist der häufigste Ansatzpunkt. Hier entscheidet sich, welchen Plan der Optimizer wählt, ob Indizes genutzt werden können und wie viele Zeilen tatsächlich gelesen werden. Schlechte Abfrageformulierungen – implizite Konvertierungen, nicht-sargable Prädikate, übermäßige SELECT-*-Nutzung oder fehlende JOINs – können selbst einen perfekt indizierten Bestand ausbremsen. Das Lesen von Ausführungsplänen ist die zentrale Fertigkeit, um diese Probleme zu sehen.

Index-Ebene

Indizes sind das wirksamste Werkzeug für schnelle Leseoperationen, aber kein Allheilmittel. Zu viele Indizes bremsen Schreiboperationen und vergrößern den Speicherbedarf. Fehlende Indizes erzwingen Table Scans. Die Kunst liegt in der richtigen Balance: Welche Abfragen sind leseintensiv und häufig genug, um einen Index zu rechtfertigen? Welche Spalten sollten als INCLUDE hinzugefügt werden, damit der Index einen Lookup verhindert? Wo lohnt ein gefilterter Index, der nur einen Teil des Bestands abdeckt?

Statistik- und Plan-Ebene

SQL Servers Query Optimizer trifft seine Entscheidungen auf Basis von Statistiken. Veraltete oder irreführende Statistiken führen zu falschen Kardinalitätsschätzungen und damit zu suboptimalen Plänen. Parameter-Sniffing – ein Mechanismus, bei dem SQL Server einen Plan für den ersten Parameter-Wert kompiliert und danach für alle weiteren Werte beibehält – ist eine häufige Ursache für sporadische Performance-Einbrüche, die schwer zu reproduzieren sind. Hier helfen regelmäßige Statistikaktualisierungen, OPTION (RECOMPILE) und der Query Store.

Architektur- und Betriebsebene

Manche Performance-Probleme lassen sich auf Abfrage- oder Indexebene nicht vollständig lösen, weil sie struktureller Natur sind. Tabellen mit hunderten Millionen Zeilen ohne Partitionierung, Batch-Prozesse, die transaktionale Tabellen sperren, fehlende Ladefenster oder unkontrolliertes Wachstum von TempDB sind Beispiele dafür. Auf dieser Ebene kommen Partitionierung, Columnstore-Indizes, TempDB-Konfiguration und Architekturüberlegungen ins Spiel.

  • Abfrageformulierung und Ausführungsplan-Analyse
  • Index-Strategie: Covering-, Filtered- und Columnstore-Indizes
  • Statistikpflege und Umgang mit Parameter-Sniffing
  • Tabellen-Partitionierung für wachsende Datenbestände
  • Wait Statistics als systemweite Engpassanalyse
  • TempDB-Konfiguration und Ressourcensteuerung
  • SSIS-Datenfluss-Optimierung und Parallelisierung
  • Query Store als historisches Performance-Monitoring
Performance-Tuning hat immer Wechselwirkungen. Ein Index, der eine Abfrage beschleunigt, kann einen Batch-Ladevorgang verlangsamen. Eine Partitionierung, die Lesezugriffe optimiert, verändert das Locking-Verhalten. Wer diese Wechselwirkungen kennt, trifft bessere Entscheidungen.

Systematischer Tuning-Workflow

Ein strukturiertes Vorgehen beim Performance-Tuning verhindert, dass man in die häufigste Falle tappt: das Optimieren ohne vorherige Diagnose. Ich folge einem mehrstufigen Workflow, der von der Messung über die Analyse bis zur Umsetzung und Verifikation führt. Jede Maßnahme wird vorher begründet und nachher gemessen.

Systematischer SQL-Performance-Tuning-Workflow von der Messung bis zur Verifikation

Der Tuning-Workflow beginnt mit der Messung von Baseline-Metriken, führt über die Engpassanalyse mit DMVs und Ausführungsplänen zur gezielten Maßnahme und schließt mit der Verifikation, ob die Verbesserung tatsächlich eingetreten ist.

Der erste Schritt ist immer die Baseline-Messung. Ohne eine Ausgangsmessung lässt sich nicht beurteilen, ob eine Maßnahme gewirkt hat. Dazu nutze ich SET STATISTICS IO ON und SET STATISTICS TIME ON für einzelne Abfragen sowie sys.dm_exec_query_stats für systemweite Aussagen darüber, welche Abfragen den größten Anteil an CPU-Zeit, logischen Lesezugriffen oder Gesamtlaufzeit haben. Diese Priorisierung ist entscheidend: Nicht jede langsame Abfrage ist ein lohnendes Optimierungsziel – es kommt auf die Kombination aus Laufzeit und Ausführungshäufigkeit an.

Analyse mit Dynamic Management Views

Dynamic Management Views sind das Diagnoseinstrument der ersten Wahl. sys.dm_exec_query_stats liefert kumulierte Laufzeitstatistiken für alle Abfragen im Cache. sys.dm_exec_requests zeigt aktuell laufende Abfragen einschließlich Wartetyp und Wartedauer. sys.dm_os_wait_stats aggregiert Wartestatistiken auf Systemebene und identifiziert, ob der Engpass bei I/O, Speicher, Locking oder CPU liegt. Diese drei DMVs zusammen geben in wenigen Minuten ein klares Bild der Systemlage.

Maßnahme und Verifikation

Erst nach der Diagnose folgt die Maßnahme. Das kann ein neuer Index sein, eine umformulierte Abfrage, eine Statistikaktualisierung, ein Plan-Guide oder – bei strukturellen Problemen – eine Architekturänderung wie Partitionierung oder ein Columnstore-Index. Nach der Umsetzung wird gemessen, ob die erwartete Verbesserung eingetreten ist, und das Ergebnis dokumentiert. Dieser Verifikationsschritt ist nicht optional: Er liefert den Nachweis, dass die Maßnahme wirkt, und schützt davor, Regressionen in anderen Abfragen zu übersehen.

T·SQL · Baseline-Messung: SET STATISTICS IO/TIME und Top-Abfragen
-- 1) Baseline fuer eine einzelne Abfrage messen
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO

-- Abfrage mit schlechtem Plan (kein Index auf OrderDate, implizite Konvertierung)
SELECT o.OrderID, o.CustomerID, o.TotalAmount
FROM   dbo.Orders AS o
WHERE  CONVERT(varchar, o.OrderDate, 104) = '01.06.2025';  -- verhindert Index-Nutzung!
GO

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
GO

-- 2) Top-10-Abfragen nach logischen Lesezugriffen systemweit
SELECT TOP 10
    qs.total_logical_reads / qs.execution_count  AS avg_logical_reads,
    qs.execution_count,
    qs.total_elapsed_time / qs.execution_count / 1000 AS avg_ms,
    SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
        ((CASE qs.statement_end_offset
            WHEN -1 THEN DATALENGTH(qt.text)
            ELSE qs.statement_end_offset
          END - qs.statement_start_offset)/2)+1)        AS query_text,
    qp.query_plan
FROM   sys.dm_exec_query_stats  AS qs
CROSS  APPLY sys.dm_exec_sql_text(qs.sql_handle)  AS qt
CROSS  APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
ORDER  BY avg_logical_reads DESC;

CONVERT um OrderDate verhindert die Index-Nutzung (nicht sargable). Die DMV-Abfrage priorisiert Optimierungsziele anhand durchschnittlicher logischer Lesezugriffe – aussagekräftiger als reine Gesamtlaufzeit, weil seltene lange Abfragen sonst überbewertet werden.

Der häufigste Fehler beim Performance-Tuning ist das Optimieren ohne Baseline. Ohne Vorher-Messung lässt sich weder die Wirksamkeit einer Maßnahme belegen noch eine Regression erkennen. Messen kommt immer zürst.

Wie SQL Server eine Abfrage verarbeitet

Um Performance-Probleme gezielt zu beheben, hilft es zu verstehen, was SQL Server intern tut, wenn eine Abfrage eintrifft. Dieser Lebenszyklus bestimmt, wo Optimierungspotenzial liegt und warum manche Maßnahmen wirken, andere aber nicht.

SQL-Server-Abfragelebenszyklus: vom Parse über Optimize bis zur Ausführung

Der Abfragelebenszyklus zeigt, wie SQL Server eine Abfrage parst, den Plan im Cache sucht, den Optimizer aufruft, den Plan kompiliert und speichert, und schließlich den Execution Engine ausführt – mit Rückgriff auf Buffer Pool und Storage I/O.

Wenn eine Abfrage eintrifft, prüft SQL Server zunächst, ob ein passender Plan bereits im Plan Cache liegt (Trivial Plan Cache / Full Plan Cache Lookup). Findet er einen, wird dieser wiederverwendet – der Optimizer muss nicht erneut rechnen. Dieses Wiederverwendungsprinzip ist effizient, birgt aber die Gefahr des Parameter-Sniffing: Der erste Plan, der für einen bestimmten Parameter-Wert erstellt wurde, ist möglicherweise für andere Werte suboptimal.

Parsing und Algebrisierung

SQL Server zerlegt die Abfrage zunächst syntaktisch (Parsing) und wandelt sie dann in einen logischen Operatorbaum um (Algebrisierung). In diesem Schritt werden auch Name-Auflösung und implizite Konvertierungen vorgenommen. Implizite Konvertierungen – etwa wenn ein varchar-Prädikat auf eine int-Spalte trifft – können dazu führen, dass SQL Server einen vorhandenen Index nicht nutzen kann, weil ein Scan des gesamten Bestands und eine zeilenweise Konvertierung günstiger erscheinen als ein Index-Seek mit Konvertierung.

Query Optimizer und Kardinalitätsschätzung

Der Query Optimizer ist kostenbasiert: Er bewertet verschiedene Ausführungsalternativen anhand von Schätzkosten und wählt den Plan mit den geringsten geschätzten Kosten. Die Grundlage dieser Schätzung sind Statistiken – Histogramme über die Werteverteilung in Spalten. Sind die Statistiken veraltet oder fehlen sie, schätzt der Optimizer falsch, und ein eigentlich guter Plan wird nicht gewählt. Der Optimizer kann zudem nicht alle Möglichkeiten durchprobieren; er hört nach einem internen Timeout auf und nimmt den besten bis dahin gefundenen Plan – bei sehr komplexen Abfragen ein wichtiger Hinweis, warum Vereinfachung helfen kann.

Execution Engine und Buffer Pool

Der eigentliche Zugriff auf Daten läuft über den Buffer Pool. SQL Server liest Seiten möglichst aus dem Arbeitsspeicher (logische Lesezugriffe) und geht nur dann auf den Datenträger (physische Lesezugriffe), wenn eine Seite nicht gecacht ist. Die Kennzahl logical reads in SET STATISTICS IO zeigt deshalb, wie viele Seiten eine Abfrage im Buffer Pool benötigt – unabhängig davon, ob sie physisch gelesen werden mussten. Ein hoher Wert signalisiert, dass entweder sehr viele Daten gelesen werden oder dass ein Scan statt eines Seeks stattfindet.

  • Plan Cache: Wiederverwendung spart Compile-Zeit, birgt aber Parameter-Sniffing-Risiken
  • Implizite Konvertierungen verhindern Index-Seeks und müssen im Code vermieden werden
  • Kardinalitätsschätzung hängt an aktuellen Statistiken – veraltete Statistiken = schlechte Pläne
  • Logical Reads messen Buffer-Pool-Zugriffe und sind die wichtigste I/O-Kennzahl je Abfrage
  • Query Store ermöglicht historische Planverläufe und erzwungene Plan-Stabilisierung
Das Wissen um den Abfragelebenszyklus macht den Unterschied zwischen einem Tuning-Ingenieur und einem Rat-und-Hoffnung-Optimierer. Wer versteht, warum SQL Server einen bestimmten Plan wählt, kann gezielt eingreifen – statt blind Indizes zu probieren.

Ausführungspläne lesen und verstehen

Der Ausführungsplan ist das präziseste Diagnoseinstrument für eine einzelne Abfrage. Er zeigt, welche Operatoren SQL Server einsetzt, in welcher Reihenfolge er auf Tabellen zugreift, ob Indizes genutzt werden, wie viele Zeilen je Operator geschätzt und tatsächlich verarbeitet werden, und wo die höchsten Kosten anfallen. Wer Ausführungspläne lesen kann, sieht die Diagnose direkt – ohne auf externe Werkzeuge angewiesen zu sein.

Estimated vs. Actual Execution Plan

Der geschätzte Plan (Estimated Execution Plan) zeigt, was SQL Server tun will, ohne die Abfrage auszuführen. Er ist nützlich für die schnelle Vorab-Analyse. Der tatsächliche Plan (Actual Execution Plan) enthält zusätzlich die realen Zeilenzahlen und Laufzeitmessungen. Die wichtigste Vergleichsgröße ist die Differenz zwischen geschätzten und tatsächlichen Zeilen (Estimated Rows vs. Actual Rows): Eine große Abweichung zeigt eine ungenaue Kardinalitätsschätzung, die auf veraltete Statistiken oder fehlende Statistiken hinweist.

Schlüsseloperatoren und ihre Bedeutung

Table Scan bedeutet, dass SQL Server die gesamte Tabelle liest – fast immer ein Zeichen für einen fehlenden oder nicht nutzbaren Index. Clustered Index Scan ist ähnlich: der gesamte geclusterte Index wird durchsucht. Index Seek hingegen ist der erwünschte Zugriff: SQL Server navigiert direkt zu den relevanten Seiten. Key Lookup (Bookmark Lookup) taucht auf, wenn ein Non-Clustered Index genutzt wird, aber nicht alle benötigten Spalten enthält – SQL Server muss für jede Zeile zusätzlich in den geclusterten Index schauen. Viele Key Lookups bei gleichzeitig hoher Zeilenzahl sind ein klarer Hinweis darauf, dass INCLUDE-Spalten im Index fehlen.

Warnzeichen im Plan

Gelbe Ausrufezeichen im Plan sind explizite Warnungen: fehlende Statistiken, implizite Konvertierungen oder fehlende Indizes (Missing Index Hints). Diese Hinweise sollten immer untersucht werden. Parallelismus-Operatoren (Parallelism / Repartition Streams) zeigen, dass SQL Server mehrere Kerne nutzt – das ist nicht immer gewünscht und kann in OLTP-Systemen zu Locking-Problemen führen. Ein MAXDOP-Hint oder eine Ressourcen-Governor-Konfiguration kann das steuern.

T·SQL · Schlechter vs. guter Plan: implizite Konvertierung und Index Seek
-- Schlechter Plan: CONVERT verhindert Index-Nutzung -> Table Scan
SELECT OrderID, CustomerID, TotalAmount
FROM   dbo.Orders
WHERE  CONVERT(varchar(10), OrderDate, 104) = '01.06.2025';
-- Ausfuehrungsplan: Clustered Index Scan, hohe Logical Reads

-- Guter Plan: direktes Datums-Praedikat -> Index Seek
SELECT OrderID, CustomerID, TotalAmount
FROM   dbo.Orders
WHERE  OrderDate >= '2025-06-01'
  AND  OrderDate <  '2025-06-02';
-- Ausfuehrungsplan: Index Seek auf idx_Orders_OrderDate, wenige Logical Reads

-- Hinweis: den tatsaechlichen Plan mit Laufzeitdaten anfordern
SET STATISTICS IO ON;
SET STATISTICS TIME ON;

-- Abfrage hier einfuegen
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;

-- Alternativ: XML-Plan aus dem Cache lesen (fuer Produktionsauswertung)
SELECT  qp.query_plan,
        qs.total_logical_reads,
        qs.execution_count,
        SUBSTRING(qt.text, 1, 200) AS query_snippet
FROM    sys.dm_exec_query_stats  AS qs
CROSS   APPLY sys.dm_exec_sql_text(qs.sql_handle)   AS qt
CROSS   APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
WHERE   qt.text LIKE '%Orders%'
ORDER   BY qs.total_logical_reads DESC;

Der Vergleich zeigt, wie eine kleine Formulierungsänderung aus einem Table Scan einen Index Seek macht. Die CONVERT-Funktion auf der Spalte macht das Prädikat nicht-sargable: SQL Server kann den Index nicht nutzen, weil er nicht weiß, für welche Rohwerte die Konvertierung das gesuchte Ergebnis liefert.

Der Missing Index Hint im Ausführungsplan ist ein Vorschlag des Optimizers, kein Befehl. Indizes sollten immer mit Blick auf das gesamte Workload-Profil bewertet werden – nicht jeder vorgeschlagene Index ist sinnvoll, wenn er Schreiboperationen merklich verlangsamt oder bestehende Indizes überlappt.

Index-Strategie

Indizes sind das wichtigste Werkzeug für schnelle Lesezugriffe in SQL Server – aber nur, wenn sie richtig eingesetzt werden. Ein Index, der nicht genutzt wird, verbraucht Platz und verlangsamt INSERT/UPDATE/DELETE-Operationen unnötig. Ein fehlender Index führt zu vollen Scans über Millionen von Zeilen. Die Arbeit besteht darin, die richtige Balance zu finden: Welche Abfragen sind häufig und leseintensiv genug, um einen dedizierten Index zu rechtfertigen? Welche Indizes überlappen sich und können zusammengeführt werden?

Covering-Index und INCLUDE-Spalten

Ein Covering-Index enthält alle Spalten, die eine Abfrage benötigt – sowohl die Suchspalten (in der Schlüsselspalte) als auch die Ausgabespalten (als INCLUDE). Ein solcher Index ermöglicht einen Index-Only-Zugriff: SQL Server muss nicht mehr in den geclusterten Index zurückschauen (Key Lookup), weil alle benötigten Daten im Non-Clustered Index enthalten sind. Der Unterschied zwischen vielen Key Lookups und keinem Key Lookup ist bei häufig ausgeführten Abfragen erheblich.

Gefilterter Index

Ein gefilterter Index (Filtered Index) indiziert nur einen Teil des Bestands – nämlich genau die Zeilen, die einer WHERE-Bedingung entsprechen. Das ist besonders nützlich bei Spalten mit schiefer Verteilung: Wenn 95 Prozent der Bestellungen den Status 'abgeschlossen' haben und nur 5 Prozent 'offen' sind, aber fast alle Abfragen nach offenen Bestellungen filtern, ist ein gefilterter Index auf Status = 'offen' erheblich kleiner und damit schneller als ein vollständiger Index. Gleichzeitig besteht weniger Overhead bei Schreiboperationen auf die 95-Prozent-Mehrheit.

Columnstore-Index für analytische Abfragen

Für analytische Abfragen, die große Datenmengen aggregieren, bietet SQL Server den Columnstore-Index. Dabei werden Daten spaltenweise statt zeilenweise gespeichert, was für typische DWH-Abfragen (SELECT wenige Spalten, große Mengen, GROUP BY) eine erheblich bessere Kompression und deutlich geringere I/O-Last bedeutet. In kombinierten OLTP/OLAP-Szenarien kann ein Non-Clustered Columnstore-Index auf einer transaktionalen Tabelle analytische Abfragen um Größenordnungen beschleunigen, ohne den OLTP-Workload zu beeinträchtigen.

T·SQL · CREATE INDEX: Covering-Index, INCLUDE und gefilterter Index
-- 1) Covering-Index: Schluessel + INCLUDE-Spalten vermeiden Key Lookup
CREATE NONCLUSTERED INDEX idx_Orders_Date_Covering
ON dbo.Orders (OrderDate, CustomerID)
INCLUDE (TotalAmount, OrderStatus);
-- Abfragen, die OrderDate/CustomerID filtern und TotalAmount/OrderStatus ausgeben,
-- benoetigen keinen zusaetzlichen Lookup in den Clustered Index.

-- 2) Gefilterter Index: nur offene Bestellungen indizieren
CREATE NONCLUSTERED INDEX idx_Orders_Open
ON dbo.Orders (CustomerID, OrderDate)
WHERE OrderStatus = 'OPEN';
-- Kleiner, schneller Index fuer den haeufigsten Abfragefall.
-- Bei INSERT/UPDATE auf abgeschlossene Bestellungen entsteht kein Overhead.

-- 3) Non-Clustered Columnstore fuer analytische Summen-Abfragen
CREATE NONCLUSTERED COLUMNSTORE INDEX nccs_Orders_Analytics
ON dbo.Orders (OrderDate, CustomerID, TotalAmount, OrderStatus);
-- Aggregationen (SUM, COUNT, AVG) ueber grosse Datenmengen profitieren von
-- spaltenweiser Speicherung und Batch-Verarbeitung.

-- 4) Index-Nutzung pruefen: welche Indizes werden nicht genutzt?
SELECT  OBJECT_NAME(i.object_id)  AS table_name,
        i.name                    AS index_name,
        i.type_desc,
        ius.user_seeks,
        ius.user_scans,
        ius.user_lookups,
        ius.user_updates
FROM    sys.indexes                   AS i
LEFT JOIN sys.dm_db_index_usage_stats AS ius
       ON  ius.object_id  = i.object_id
       AND ius.index_id   = i.index_id
       AND ius.database_id = DB_ID()
WHERE   i.type > 0                    -- kein Heap
  AND   OBJECT_NAME(i.object_id) NOT LIKE 'sys%'
ORDER   BY ius.user_seeks + ius.user_scans + ius.user_lookups ASC;

sys.dm_db_index_usage_stats zeigt seit dem letzten Neustart, wie oft jeder Index für Seeks, Scans und Lookups genutzt wurde. Indizes mit user_seeks=0, user_scans=0, user_lookups=0, aber hohen user_updates sind Kandidaten für die Entfernung.

Eine Index-Strategie ist kein einmaliger Akt. Workloads ändern sich, neue Abfragen kommen hinzu, Datenverteilungen verschieben sich. sys.dm_db_index_usage_stats und der Query Store sollten deshalb regelmäßig ausgewertet werden, um veraltete Indizes zu entfernen und neue Lücken zu schließen.

Statistiken und Parameter-Sniffing

Zwei der häufigsten und gleichzeitig am schwersten zu diagnostizierenden Performance-Probleme in SQL Server sind veraltete Statistiken und Parameter-Sniffing. Beide führen dazu, dass der Query Optimizer einen Plan wählt, der für den tatsächlichen Datenzustand schlecht geeignet ist – und beide sind mit den richtigen Werkzeugen identifizierbar und behebbar.

Warum Statistiken kritisch sind

Statistiken beschreiben die Werteverteilung in Tabellenspalten in Form von Histogrammen. Der Query Optimizer nutzt diese Histogramme, um zu schätzen, wie viele Zeilen ein Prädikat zurückgibt. Ist ein Histogramm veraltet – weil seit der letzten Aktualisierung viele Zeilen eingefügt, aktualisiert oder gelöscht wurden –, sind diese Schätzungen ungenau. Der Optimizer wählt dann möglicherweise einen Hash Join statt eines Nested Loop Join, oder er reserviert zu wenig Speicher für eine Sortieroperation und schreibt auf TempDB. Beides ist deutlich langsamer als ein gut informierter Plan.

Parameter-Sniffing: Ursache und Symptome

SQL Server kompiliert einen Plan beim ersten Aufruf einer gespeicherten Prozedur und speichert ihn im Plan Cache. Dieser Plan ist für die Parameter-Werte des ersten Aufrufs optimiert. Wenn nachfolgende Aufrufe mit sehr unterschiedlichen Parameter-Werten arbeiten – etwa ein Kunde mit 10 Bestellungen vs. ein Kunde mit 10 Millionen Bestellungen –, kann der gecachte Plan für die neuen Werte extrem suboptimal sein. Das Symptom ist eine Stored Procedure, die mal schnell und mal qualvoll langsam ist, ohne dass sich am Code etwas geändert hat. Der Query Store ist das beste Werkzeug zur Diagnose: Er zeigt, ob dieselbe Procedure verschiedene Pläne hatte und welcher Plan zu welchem Zeitpunkt aktiv war.

T·SQL · UPDATE STATISTICS, OPTION (RECOMPILE) und Query-Store-Diagnose
-- 1) Statistiken aktualisieren (mit vollstaendigem Scan fuer kritische Tabellen)
UPDATE STATISTICS dbo.Orders WITH FULLSCAN;
-- FULLSCAN liest alle Zeilen und liefert genaue Histogramme.
-- Standard-Sample kann bei schiefer Verteilung ungenaue Schaetzungen liefern.

-- 2) Alle Statistiken einer Datenbank aktualisieren (Wartungsskript)
EXEC sp_updatestats;  -- verwendet adaptives Sampling, schnell aber weniger genau
-- Fuer kritische Tabellen: separater FULLSCAN-Aufruf empfohlen.

-- 3) OPTION (RECOMPILE) gegen Parameter-Sniffing
-- Zwingt SQL Server, fuer jeden Aufruf einen neuen Plan zu kompilieren.
-- Geeignet fuer Abfragen, die selten laufen und stark variierende Parameter haben.
CREATE OR ALTER PROCEDURE dbo.GetOrdersByCustomer
    @CustomerID INT
AS
BEGIN
    SELECT OrderID, OrderDate, TotalAmount
    FROM   dbo.Orders
    WHERE  CustomerID = @CustomerID
    OPTION (RECOMPILE);  -- kein Plan-Caching, immer frischer Plan
END;

-- 4) Query Store: regressive Plaene identifizieren
SELECT  qsq.query_id,
        qsrs.avg_duration         / 1000 AS avg_ms,
        qsrs.count_executions,
        qsp.plan_id,
        qsp.is_forced_plan,
        TRY_CAST(qsp.query_plan AS XML)   AS plan_xml
FROM    sys.query_store_query           AS qsq
JOIN    sys.query_store_query_text      AS qsqt ON qsqt.query_text_id = qsq.query_text_id
JOIN    sys.query_store_plan            AS qsp  ON qsp.query_id        = qsq.query_id
JOIN    sys.query_store_runtime_stats   AS qsrs ON qsrs.plan_id        = qsp.plan_id
WHERE   qsrs.avg_duration > 500000  -- Abfragen mit mehr als 500 ms Durchschnitt
ORDER   BY qsrs.avg_duration DESC;

OPTION (RECOMPILE) ist kein Allheilmittel – sie erhöht CPU-Last durch häufiges Kompilieren. Für hochfrequente Abfragen mit stabilen Parametern ist Plan Forcing über den Query Store die bessere Alternative: Der bekannte gute Plan wird erzwungen, ohne jedes Mal neu zu kompilieren.

Eine nachhaltige Statistikstrategie umfasst mehrere Elemente: automatische Aktualisierung ist eingeschaltet (AUTO_UPDATE_STATISTICS ON), für kritische Tabellen mit schnell wachsenden Beständen wird mit einem Schwellwert-Trace-Flag (TF 2371 oder die neue adaptive Threshold-Logik ab SQL Server 2016) gearbeitet, und bestimmte Tabellen erhalten regelmäßige FULLSCAN-Aktualisierungen im Wartungsfenster. Der Query Store ist aktiviert und so konfiguriert, dass eine ausreichende Historie für Planverläufe erhalten bleibt.

Parameter-Sniffing ist kein Bug, sondern ein Feature mit Nebenwirkungen. SQL Server optimiert für den ersten gesehenen Wert – das ist in den meisten Fällen vernünftig. Das Problem entsteht, wenn die Werteverteilung stark schief ist und der erste Wert nicht repräsentativ für den typischen Workload ist.

Tabellen-Partitionierung

Wenn Tabellen in den Bereich von zig bis hunderten Millionen Zeilen wachsen, stoßen herkömmliche Indexstrategien an Grenzen. Wartungsoperationen wie Index Rebuild oder STATISTICS UPDATE dauern Stunden, Ladeoperationen sperren die gesamte Tabelle, und Archivierungen erfordern aufwendige DELETE-Batches. Tabellen-Partitionierung löst diese Probleme strukturell, indem sie eine Tabelle intern in physisch getrennte Segmente aufteilt – für Abfragen und Anwendungen unsichtbar, für Wartung und Performance aber erheblich.

Eine Partitionierung erfolgt anhand einer Partitionsschlüsselspalte, typischerweise ein Datum oder ein Zeitstempel. SQL Server verteilt die Zeilen anhand einer Partitionsfunktion auf Partitionsschemas und physische Dateigruppen. Abfragen, die einen Datumsbereich filtern, profitieren von Partition Elimination: SQL Server liest nur die Partitionen, die den gesuchten Bereich abdecken, und überspringt alle anderen. Das reduziert I/O und logische Lesezugriffe erheblich.

Switch-basierte Archivierung und Beladung

Der größte operative Vorteil der Partitionierung liegt im Partition Switch. Eine neue Partition kann als separate Tabelle vorbefüllt, indiziert und dann per ALTER TABLE … SWITCH in Sekundenbruchteilen (metadata-only) in die Haupttabelle eingefügt werden. Umgekehrt lässt sich eine alte Partition ebenso schnell ausschalten und in eine Archivtabelle verschoben werden. Diese Switch-Operationen erfordern keine Datenbewegung und keine langen Sperren – ein kritischer Vorteil in hochverfügbaren Systemen.

Automatische Partitionserweiterung

In Projekten mit kontinuierlichem Datenzufluss ist eine automatische Partitionserweiterung sinnvoll: Ein SQL-Agent-Job prüft regelmäßig, ob die nächste Zeitperiode bereits als Partition existiert, und legt sie bei Bedarf an. So wächst die Tabelle kontrolliert, ohne dass manuelle Eingriffe nötig sind. Dieses Muster habe ich in einem E-Commerce-Projekt mit SCD2-Historisierung umgesetzt: Die Partitionierung nach Monat ermöglichte sowohl schnelle Beladung (Switch-In) als auch schnelle Archivierung älterer Historienbestände (Switch-Out).

T·SQL · Partitionsfunktion, Partitionsschema und Partition-Switching
-- 1) Partitionsfunktion: teilt Daten nach Monat auf
CREATE PARTITION FUNCTION pf_Orders_Monthly (DATE)
AS RANGE RIGHT FOR VALUES (
    '2024-01-01','2024-02-01','2024-03-01','2024-04-01',
    '2024-05-01','2024-06-01','2024-07-01','2024-08-01',
    '2024-09-01','2024-10-01','2024-11-01','2024-12-01',
    '2025-01-01','2025-02-01','2025-03-01','2025-04-01',
    '2025-05-01','2025-06-01'
);

-- 2) Partitionsschema: jede Partition einer Dateigruppe zuordnen
CREATE PARTITION SCHEME ps_Orders_Monthly
AS PARTITION pf_Orders_Monthly
ALL TO ([PRIMARY]);    -- vereinfacht: alle auf PRIMARY; produktiv separate FGs nutzen

-- 3) Partitionierte Tabelle anlegen
CREATE TABLE dbo.Orders_Partitioned (
    OrderID     BIGINT        NOT NULL,
    CustomerID  INT           NOT NULL,
    OrderDate   DATE          NOT NULL,
    TotalAmount DECIMAL(18,2) NOT NULL,
    CONSTRAINT pk_Orders_Part PRIMARY KEY CLUSTERED (OrderDate, OrderID)
) ON ps_Orders_Monthly (OrderDate);

-- 4) Neue Partition fuer naechsten Monat hinzufuegen (Erweiterung)
ALTER PARTITION SCHEME  ps_Orders_Monthly NEXT USED [PRIMARY];
ALTER PARTITION FUNCTION pf_Orders_Monthly() SPLIT RANGE ('2025-07-01');

-- 5) Schnelles Switch-In einer neuen Partition (kein Datentransfer, nur Metadaten)
-- Staging-Tabelle (gleiche Struktur, gleiche Dateigruppe) muss vorab beladenwerden:
ALTER TABLE dbo.Orders_Staging
    SWITCH TO dbo.Orders_Partitioned PARTITION 19;
-- Dauert Millisekunden, keine langen Sperren auf der Haupttabelle.

-- 6) Partition Elimination pruefen
SELECT  partition_number,
        row_count,
        reserved_page_count * 8 / 1024 AS reserved_MB
FROM    sys.dm_db_partition_stats
WHERE   object_id = OBJECT_ID('dbo.Orders_Partitioned')
ORDER   BY partition_number;

Der SPLIT RANGE am Ende fügt eine neue Partition hinzu, ohne die Tabelle zu sperren. SWITCH IN/OUT ist der entscheidende Vorteil der Partitionierung: Statt stundenlanger DELETE-Batches eine metadaten-only-Operation in Millisekunden.

Partitionierung ist keine universelle Beschleunigung. Abfragen, die die Partitionsschlüsselspalte nicht im Prädikat haben, können trotz Partitionierung einen vollständigen Scan aller Partitionen durchführen. Die Partitionsschlüsselwahl muss den dominanten Zugriffsmuster der Anwendung widerspiegeln.

SSIS-Performance

SQL Server Integration Services ist das ETL-Werkzeug für viele Ladeprozesse in SQL-Server-Umgebungen. Performance-Probleme in SSIS entstehen häufig nicht durch den Datenfluss selbst, sondern durch schlecht konfigurierte Quell- und Zielkomponenten, fehlende Parallelisierung, unnötige Sortierungen und den Einsatz von Blocking-Transformationen dort, wo Streaming-Transformationen ausreichen würden.

Blocking vs. Streaming Transformationen

In SSIS unterscheidet man zwischen Fully Blocking-, Partially Blocking- und Row-by-Row-Transformationen. Fully Blocking-Transformationen wie Sort oder Aggregate puffern alle eingehenden Zeilen, bevor sie die erste Ausgabezeile erzeugen. Das bedeutet hohen Speicherverbrauch und einen vollständigen Stau im Datenfluss. Wer eine Sortierung im Datenfluss vermeiden kann – etwa indem ORDER BY in der SQL-Quellabfrage oder im Merge Join durch einen vorsortiert gelieferten Bestand ersetzt wird –, gewinnt erheblich an Durchsatz und verringert den Speicherbedarf.

OLE DB Destination und Batch-Größe

Die OLE DB Destination schreibt Daten ins Ziel. Die entscheidenden Einstellungen sind der Data Access Mode (Fast Load ist fast immer richtig), die Batch-Größe (rows per batch) und die Anzahl der Zeilen pro Commit. Zu kleine Batches erzeugen hohen Overhead durch viele kleine Transaktionen, zu große Batches können den Transaktionslog belasten und Sperren verlängern. Ein guter Ausgangswert ist 10.000 bis 50.000 Zeilen pro Batch, je nach Zeilengröße und Log-Konfiguration. MaxInsertCommitSize steuert, wie viele Zeilen pro Commit übergeben werden – 0 bedeutet, dass der gesamte Batch in einer Transaktion landet.

Parallelisierung und Datenfluss-Partitionierung

SSIS nutzt mehrere Threads innerhalb eines Datenflusses automatisch. Mit dem Property EngineThreads lässt sich die Anzahl der Threads steuern. Darüber hinaus können mehrere Data Flow Tasks parallelisiert werden, indem sie als Kinder eines Sequence Containers mit aktiviertem MaxConcurrentExecutables ausgeführt werden. In Projekten habe ich SSIS-Strecken grundlegend redesignt und dabei Parallelisierung eingeführt, die die Gesamtlaufzeit signifikant reduziert hat.

T·SQL / SSIS · Wait-Stats-relevante Monitoring-Abfrage für SSIS-Läufe
-- Waehrend eines SSIS-Laufs: aktuelle Sperren und Wartetypen auf dem Zielserver pruefen
SELECT
    r.session_id,
    r.status,
    r.wait_type,
    r.wait_time        AS wait_ms,
    r.logical_reads,
    r.writes,
    SUBSTRING(t.text, 1, 200) AS query_snippet,
    DB_NAME(r.database_id)    AS db_name
FROM   sys.dm_exec_requests AS r
CROSS  APPLY sys.dm_exec_sql_text(r.sql_handle) AS t
WHERE  r.session_id <> @@SPID
  AND  r.status IN ('running','suspended')
ORDER  BY r.wait_time DESC;

-- Typische SSIS-Wartetypen und ihre Bedeutung:
-- WRITELOG         : Transaktionslog-I/O (Log-Datei zu langsam oder Batch zu gross)
-- PAGEIOLATCH_EX   : Datenseiten-I/O beim Schreiben (Storage-Engpass)
-- LCK_M_*          : Sperrkonflikte (SSIS-Ziel vs. parallele Lesezugriffe)
-- ASYNC_IO_COMPLETION: asynchrone I/O-Vorgaenge (normal bei Fast-Load)

-- Empfehlung: SSIS-Ladevorgang in Ladefenster legen und Tabellensperre
-- (TABLOCK-Hint in OLE DB Destination) nur dann aktivieren, wenn
-- keine parallelen Lesezugriffe auf die Zieltabelle stattfinden.

WRITELOG als dominanter Wartetyp während eines SSIS-Laufs signalisiert, dass die Commit-Frequenz zu hoch oder das Transaktionslog zu langsam ist. Vergrößerung von MaxInsertCommitSize und Umstellung auf Fast Load lösen dieses Problem in den meisten Fällen.

SSIS-Performance-Tuning beginnt auf der Datenbankseite, nicht in SSIS selbst. Ein schlecht indiziertes Ziel, ein zu häufig committendes Ziel oder eine Quelltabelle ohne passenden Index für die Filterkriterien des SSIS-Laufs sind häufiger der Engpass als der Datenfluss selbst.

Wait Stats und Monitoring

Wait Statistics sind die systemweiteste und gleichzeitig präziseste Methode, um Engpässe in SQL Server zu identifizieren. SQL Server protokolliert für jeden Thread, der auf etwas warten muss, den Wartetyp und die Wartezeit. Die Aggregation dieser Daten in sys.dm_os_wait_stats zeigt, worauf das System insgesamt am längsten wartet – und damit, wo der tatsächliche Engpass liegt, bevor man sich überhaupt in einzelne Abfragen vertieft.

Die wichtigsten Wartetypen

PAGEIOLATCH_SH und PAGEIOLATCH_EX signalisieren I/O-Engpässe: SQL Server wartet darauf, dass Datenseiten vom Speicher gelesen werden. Ist dieser Wartetyp dominant, hilft mehr RAM (größerer Buffer Pool) oder schnellerer Storage (SSD/NVMe). CXPACKET und CXCONSUMER stehen für parallele Abfrageausführung: Threads warten aufeinander. Moderate CXPACKET-Werte sind normal; hohe Werte deuten auf schief-verteilte Daten oder ungeeignetes MAXDOP hin. LCK_M_S, LCK_M_U und LCK_M_X stehen für Sperrkonflikte: Lesende und schreibende Transaktionen blockieren sich gegenseitig. WRITELOG signalisiert, dass der Transaktionslog-Schreibvorgang der Engpass ist.

Query Store als historisches Monitoring

Der Query Store, eingeführt in SQL Server 2016 und Standard ab SQL Server 2019, speichert Abfragen, Pläne und Laufzeitstatistiken dauerhaft in der Datenbank. Das ermöglicht historische Analysen: Welche Abfrage hatte letzte Woche eine Planregression? Welcher Plan war vor dem letzten Index-Rebuild aktiv? Mit Plan Forcing lässt sich ein bekannt guter Plan dauerhaft erzwingen – ohne Eingriff in den Anwendungscode. Das ist besonders wertvoll bei fremden Anwendungen, deren Abfragecode nicht zugänglich oder nicht änderbar ist.

Regelmäßiges Wartungsregime

Neben der reaktiven Diagnose gehört ein proaktives Wartungsregime zu einer gesunden SQL-Server-Umgebung. Das umfasst regelmäßige Index-Reorganisierungen und -Rebuilds nach Fragmentierungsgrad (nicht nach starrem Zeitplan), Statistikaktualisierungen mit FULLSCAN für kritische Tabellen, Überprüfung des Query Stores auf Regressionen und ein wöchentliches oder monatliches Review der Top-Wait-Stats-Kategorien. Erst diese Regelmäßigkeit verhindert, dass sich Performance-Probleme schleichend aufbauen, bis sie akut werden.

T·SQL · Wait-Stats-Analyse: sys.dm_os_wait_stats
-- Top-20-Wartetypen seit letztem Neustart (normalisiert auf Prozent)
WITH waits AS (
    SELECT
        wait_type,
        waiting_tasks_count,
        wait_time_ms,
        max_wait_time_ms,
        signal_wait_time_ms,
        wait_time_ms - signal_wait_time_ms AS resource_wait_ms
    FROM   sys.dm_os_wait_stats
    WHERE  wait_type NOT IN (           -- Leerlauf-Wartetypen herausfiltern
        'SLEEP_TASK','BROKER_TO_FLUSH','BROKER_TASK_STOP','CLR_AUTO_EVENT',
        'DISPATCHER_QUEUE_SEMAPHORE','FT_IFTS_SCHEDULER_IDLE_WAIT',
        'HADR_FILESTREAM_IOMGR_IOCOMPLETION','HADR_WORK_QUEUE',
        'LAZYWRITER_SLEEP','LOGMGR_QUEUE','ONDEMAND_TASK_QUEUE',
        'REQUEST_FOR_DEADLOCK_SEARCH','RESOURCE_QUEUE','SERVER_IDLE_CHECK',
        'SLEEP_DBSTARTUP','SLEEP_DCOMSTARTUP','SLEEP_MASTERDBREADY',
        'SLEEP_MASTERMDREADY','SLEEP_MASTERUPGRADED','SLEEP_MSDBSTARTUP',
        'SLEEP_TEMPDBSTARTUP','SNI_HTTP_ACCEPT','SP_SERVER_DIAGNOSTICS_SLEEP',
        'SQLTRACE_BUFFER_FLUSH','SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
        'WAIT_XTP_OFFLINE_CKPT_NEW_LOG','WAITFOR','XE_DISPATCHER_WAIT',
        'XE_TIMER_EVENT','BROKER_EVENTHANDLER','CHECKPOINT_QUEUE',
        'DBMIRROR_EVENTS_QUEUE','SQLTRACE_WAIT_ENTRIES'
    )
)
SELECT TOP 20
    wait_type,
    waiting_tasks_count,
    wait_time_ms,
    max_wait_time_ms,
    CAST(100.0 * wait_time_ms
         / SUM(wait_time_ms) OVER ()    AS DECIMAL(5,2)) AS pct_total,
    resource_wait_ms,
    signal_wait_time_ms                                   AS cpu_queue_ms
FROM   waits
ORDER  BY wait_time_ms DESC;

Die Idle-Wartetypen werden ausgefiltert, weil SQL Server in Ruhephasen auf sie wartet und sie das Bild verfälschen würden. Die verbleibenden Wartetypen zeigen den echten Engpass: I/O, Locking, Parallelismus oder CPU-Queue.

Wait Statistics sind kein Punktwert, sondern ein Trend. Ein einmaliger Snapshot zeigt den aktuellen Zustand, aber ob sich das System über Wochen verschlechtert hat, ist nur durch regelmäßige Baseline-Snapshots erkennbar. Ich empfehle, täglich einen Wait-Stats-Snapshot in eine History-Tabelle zu schreiben und so Veränderungen sichtbar zu machen.

Vorgehen in der Zusammenarbeit

Performance-Tuning ist keine isolierte Aktivität, sondern eine Zusammenarbeit mit dem Team, das die Systeme kennt und betreibt. Ich bringe die analytischen Werkzeuge und die Diagnosekompetenz mit; das Projektteam bringt das fachliche Wissen über die Abläufe, die Zeitfenster und die Kritikalität der betroffenen Systeme. Aus dieser Kombination entstehen Maßnahmen, die technisch fundiert und im Betrieb umsetzbar sind.

Typischer Projekteinstieg

In der Regel beginne ich mit einem Assessment: Ich verschaffe mir einen Überblick über die SQL-Server-Umgebung, analysiere Wait Statistics, die teuersten Abfragen aus sys.dm_exec_query_stats und den Query Store, prüfe den Index-Bestand und die Statistikaktualität. Das Ergebnis ist eine priorisierte Liste von Engpässen und Maßnahmen. Dieser erste Schritt dauert typischerweise ein bis drei Tage – je nach Umgebungsgröße – und liefert ein klares Bild, bevor aufwendigere Maßnahmen in Angriff genommen werden.

Iterative Umsetzung

Die Umsetzung erfolgt iterativ und messbar. Jede Maßnahme wird vorher begründet, nach der Umsetzung gemessen und dokumentiert. So entsteht ein nachvollziehbares Performance-Log, das zeigt, welche Maßnahme welchen Effekt hatte. Das ist nicht nur für das aktuelle Projekt wertvoll, sondern auch für das langfristige Verständnis des Systems: Warum hat man damals diesen Index angelegt? Welches Problem sollte er lösen?

  • Assessment: Wait Stats, Top-Abfragen, Index-Bestand, Statistikaktualität
  • Priorisierung: Maßnahmen nach Aufwand/Wirkung-Verhältnis sortieren
  • Umsetzung: Iterativ, jede Maßnahme wird gemessen und dokumentiert
  • Verifikation: Vorher-/Nachher-Vergleich mit denselben Werkzeugen
  • Wissenstransfer: Team mit Diagnosewerkzeugen und Mustern vertraut machen

Ich arbeite remote, hybrid und vor Ort. Gerade bei Performance-Projekten ist ein enger Austausch mit dem Betriebsteam wichtig, weil viele Maßnahmen im Wartungsfenster durchgeführt werden müssen und weil das Team anschließend in der Lage sein soll, denselben diagnostischen Blick selbst anzuwenden. Wissenstransfer ist für mich kein Zusatzangebot, sondern Teil der Lieferung.

Nachhaltigkeit ist das Ziel. Eine Performance-Optimierung, die nach sechs Monaten wieder verfallen ist, weil niemand das Monitoring fortgeführt oder neue Abfragen auf Planerstellung geprüft hat, ist eine verpasste Chance. Ich lege deshalb Wert darauf, dass das Team nach dem Projekt selbst in der Lage ist, Engpässe zu erkennen und zu beheben.

Typische Leistungen rund um SQL Performance Tuning

Je nach Ausgangslage und Projektziel übernehme ich unterschiedliche Aufgaben – von der einmaligen Notfallanalyse bis zum mehrstufigen Performance-Programm, das eine Umgebung dauerhaft auf ein neues Niveau hebt.

  • Performance-Assessment: Wait Stats, Top-Abfragen, Index- und Statistikanalyse
  • Ausführungsplan-Analyse und Abfrageoptimierung für kritische Workloads
  • Index-Strategie: Entwurf, Umsetzung und Bereinigung redundanter Indizes
  • Statistikstrategie: Wartungsplan, FULLSCAN-Zeitpläne, Query-Store-Konfiguration
  • Parameter-Sniffing-Diagnose und Behebung (Recompile, Plan Forcing, Query Store)
  • Tabellen-Partitionierung: Design, Umsetzung und automatische Erweiterung
  • Columnstore-Index-Einführung für analytische Workloads
  • SSIS-Performance-Optimierung: Datenfluss-Redesign, Parallelisierung, Batch-Größen
  • TempDB-Konfiguration und Spill-Diagnose
  • Wait-Stats-Monitoring und historisches Performance-Tracking
  • Query-Store-Konfiguration und Planregression-Management
  • Wissenstransfer und Coaching für Entwicklungsteams

Viele Performance-Probleme sind nicht isoliert: Eine langsame Abfrage ist oft Symptom einer fehlenden Index-Strategie, die ihrerseits aus einer fehlenden Monitoring-Praxis folgt. Ich schaue deshalb nicht nur auf die akut brennende Stelle, sondern nehme die gesamte Umgebung in den Blick – und trenne, was sofort behoben werden muss, von dem, was mittel- und langfristig zu einer gesunden Performance-Basis beiträgt.

Die Bandbreite meiner SQL-Server-Erfahrung – von SQL Server 2000 bis 2025, von OLTP über DWH bis SSIS, von kleinen Einzelsystemen bis zu Umgebungen mit rund 80 virtualisierten Servern – erlaubt es mir, schnell zu beurteilen, welche Maßnahmen in welchem Kontext realistisch und wirksam sind. Nicht jede Partitionierung lohnt sich, nicht jeder Columnstore-Index ist ein Gewinn – der Kontext entscheidet, und dieser Kontext ist immer die konkrete Workload-Charakteristik des jeweiligen Systems.

Ausgewählte anonymisierte Referenzprojekte

Sparkasse / Finanzdienstleister

SSIS-Redesign · Query-Optimierung · PowerShell · SSDT-Deployment

Ablösung bestehender Java-basierter Ladeprozesse durch neu konzipierte SSIS-Strecken, einschließlich Textdatei-Loads und SSDT-Deployment über PowerShell. Gleichzeitig systematische Performanceoptimierung der neuen SSIS-Strecken: Redesign der Datenflüsse, Parallelisierung, Batch-Größen-Konfiguration und Reduzierung von Blocking-Transformationen. Ergebnis: signifikant kürzere Ladefenster und stabile Betriebsprozesse.

Loyalty / Handel / Clearing

Performanceanalyse · SQL Server · SSIS · Power BI · DSGVO

Umfassende Performanceanalyse einer Clearing-Plattform mit hohem Transaktionsvolumen: Wait-Stats-Analyse, Ausführungsplan-Review für kritische Clearing-Abfragen, Index-Optimierung und Statistikstrategie. Ergänzend SSIS-Performance-Tuning für Batch-Ladevorgänge sowie Aufbau von Power-BI-Datenmodellen mit Row-Level-Security.

Handel / E-Commerce

Tabellen-Partitionierung · SCD2-Historisierung · SSIS

Entwurf und Umsetzung einer monatsbasierten Tabellen-Partitionierung für eine wachsende Bestellhistorie mit SCD2-Historisierung. Implementierung automatischer Partitionserweiterung über einen SQL-Agent-Job. Switch-basierte Beladung und Archivierung reduzierten die Ladezeiten und beseitigten lange Tabellensperren im Produktionsbetrieb.

Textil- / Servicedienstleister

Azure Synapse · SSIS · Power BI · Kostenreduktion

Aufbau und Optimierung von ETL-Strecken in einer hybriden Umgebung aus SQL Server und Azure Synapse. Performance-Analyse bestehender SSIS-Pakete, gezielte Überarbeitung von Datenflüssen und Reduzierung redundanter Ladeschritte. Anbindung an Power BI mit optimierten Datenmodellen sowie Maßnahmen zur Kostenreduktion in der Azure-Umgebung.

Häufige Fragen zum SQL Performance Tuning

Wie läuft eine Performance-Analyse ab?

Ich beginne mit einer Baseline-Messung: Wait Statistics auf Systemebene, Top-Abfragen aus sys.dm_exec_query_stats und einem Blick in den Query Store. Daraus ergibt sich eine priorisierte Liste von Engpässen. Dann folgen Ausführungsplan-Analyse für die teuersten Abfragen, Index-Review und Statistikprüfung. Jede Maßnahme wird vor und nach der Umsetzung gemessen.

Welche SQL-Server-Versionen decken Sie ab?

SQL Server 2000 bis 2025. Die Diagnose- und Optimierungswerkzeuge sind über Versionen hinweg weitgehend konsistent; neuere Versionen bieten zusätzliche Möglichkeiten wie den Query Store (ab 2016) oder Intelligent Query Processing (ab 2019), die ich gezielt einsetze, wo sie verfügbar sind.

Was ist Parameter-Sniffing, und wie beheben Sie es?

SQL Server speichert beim ersten Aufruf einer Stored Procedure einen Plan, der für den ersten Parameter-Wert optimiert ist. Dieser Plan kann für andere Werte suboptimal sein. Diagnose erfolgt über den Query Store. Lösungsansätze: OPTION (RECOMPILE) für selten ausgeführte Prozeduren mit stark variierenden Parametern, Plan Forcing über den Query Store für bekannt gute Pläne, oder Überarbeitung der Abfrage.

Wann lohnt sich Tabellen-Partitionierung?

Partitionierung lohnt sich, wenn Tabellen in den Bereich ab ca. 50–100 Millionen Zeilen wachsen, wenn Wartungsoperationen (Index Rebuild, Statistik-Update) inakzeptabel lang dauern, wenn Switch-basierte Beladung und Archivierung benötigt werden oder wenn Abfragen typischerweise einen Zeitraumfilter haben, der Partition Elimination ermöglicht.

Können Sie auch SSIS-Performance-Probleme lösen?

Ja. SSIS-Performance-Tuning umfasst Datenfluss-Redesign, Parallelisierung, Batch-Größen-Konfiguration, Vermeidung von Blocking-Transformationen und Optimierung der Ziel-Konfiguration (OLE DB Destination Fast Load, MaxInsertCommitSize). Oft liegt der eigentliche Engpass aber auf der Datenbankseite – schlechte Indizes auf dem Ziel oder fehlende Indizes auf der Quelle.

Wie nachhaltig sind die Optimierungsmaßnahmen?

Nachhaltigkeit entsteht durch Monitoring. Ich richte regelmäßige Wait-Stats-Snapshots ein, konfiguriere den Query Store und übergebe dem Team Diagnoseabfragen, mit denen es selbst erkennen kann, wenn sich Probleme aufbauen. Das Ziel ist, dass das Team nach dem Projekt eigenständig in der Lage ist, Performance-Engpässe zu diagnostizieren.

Arbeiten Sie auch remote?

Ja, vollständig remote ist möglich. Performance-Analysen und Index-Optimierungen lassen sich effektiv über Remote-Datenbankzugang und Screensharing durchführen. Für strukturelle Maßnahmen wie Partitionierungen, die im Wartungsfenster stattfinden, ist gelegentliche Abstimmung vor Ort hilfreich, aber nicht zwingend.

In welchen Sprachen können wir zusammenarbeiten?

Auf Deutsch, Englisch und Portugiesisch – jeweils fließend, auch in technischen Diskussionen über Ausführungspläne, DMV-Ausgaben und Architekturentscheidungen.

Kontakt

Projektanfrage

Benötigen Sie Unterstützung bei ETL, Data Vault, BI-Architektur, SQL Server oder Azure?

Remote · Hybrid · Deutschland · EU · Brasilien · Teilzeit · Vollzeit