SQL_Kurs

SQL Server Index Fillfactor – Der richtige Kompromiss

Zwischen Lesegeschwindigkeit, Schreibperformance und Fragmentierung – Alles, was Sie wissen müssen

📌 Was ist der Fillfactor?

Der Fillfactor ist eine Index-Einstellung in SQL Server, die angibt, wie voll jede Seite eines Index-Baums (Blatt- und ggf. Zwischenebenen) beim Erstellen oder Reorganisieren/Neuerstellen befüllt werden soll. Er wird als Prozentwert (1–100) angegeben. Ein Fillfactor von 80 bedeutet, dass 80 % der Seite mit Daten gefüllt und 20 % als freier Raum für zukünftige Einfügungen reserviert werden. Der Standardwert (wenn nichts angegeben) ist 0, was praktisch 100 % Füllung bedeutet (kein freier Raum).
💡 Der Trade-off: Ein niedriger Fillfactor (z. B. 70) reduziert Seitensplits bei vielen Einfügungen, erhöht aber die Anzahl der benötigten Seiten und damit den Speicherplatz und die Lesezeit. Ein hoher Fillfactor (z. B. 95) spart Speicherplatz, führt aber bei häufigen Einfügungen zu vielen Seitensplits und Fragmentierung.

⚙️ Auswirkungen auf Performance & Fragmentierung

Zu niedriger Fillfactor (z. B. 60–70):
  • ➖ Mehr Speicherplatzverbrauch (mehr Seiten für gleiche Datenmenge).
  • ➖ Langsamere Lesezugriffe (mehr Seiten müssen gelesen werden).
  • ➕ Weniger Seitensplits bei vielen INSERT/UPDATE-Operationen.
  • ➕ Geringere Fragmentierungsrate zwischen Wartungszyklen.
Zu hoher Fillfactor (z. B. 90–100):
  • ➕ Spart Speicherplatz (bessere Packungsdichte).
  • ➕ Schnellere Lesezugriffe (weniger Seiten pro Abfrage).
  • ➖ Bei vielen Einfügungen häufige Seitensplits, die teuer sind und Fragmentierung verursachen.
  • ➖ Höhere Fragmentierung zwischen Wartungen.
-- Aktuellen Fillfactor eines Index anzeigen
SELECT 
  OBJECT_NAME(i.object_id) AS TableName,
  i.name AS IndexName,
  i.fill_factor
FROM sys.indexes i
WHERE i.fill_factor > 0;
⚠️ Beachte: Der Fillfactor wirkt sich nur bei Index-Erstellungs- oder Rebuild-Vorgängen aus. Bei REORGANIZE wird er nicht neu angewendet.

📈 Praxisempfehlungen – Richtwerte

Es gibt keine universelle Zahl – der optimale Fillfactor hängt vom Verhältnis der Schreib- zu Leseoperationen auf dem Index ab. Als Faustregel:
  • 90–100 % (Standard 0) – Für überwiegend lesende Tabellen (Data Warehouse, Reporting).
  • 80–90 % – Für gemischte Workloads mit moderaten Einfügungen.
  • 70–80 % – Für Tabellen mit sehr vielen INSERT/UPDATE-Aktivitäten (z. B. Logging, OLTP mit starken Schreiblasten).
  • Unter 70 % – Nur in extremen Fällen, z. B. wenn die Tabelle ständig in der Mitte der Schlüsselsequenz eingefügt wird (starke zufällige INSERTs).
-- Index mit Fillfactor erstellen
CREATE NONCLUSTERED INDEX IX_Example
  ON dbo.BigTable (LastModified)
  WITH (FILLFACTOR = 80);

-- Fillfactor eines bestehenden Index ändern (Rebuild erforderlich)
ALTER INDEX IX_Example ON dbo.BigTable
  REBUILD WITH (FILLFACTOR = 85);
💡 Wichtig: Der Fillfactor sollte auf Indexebene und nicht für die gesamte Datenbank festgelegt werden. Ein Index auf einer IDENTITY-Spalte (immer aufsteigend) benötigt einen hohen Fillfactor (z. B. 95–100), während ein Index auf einer stark zufälligen Spalte wie einem GUID niedriger sein sollte.

💥 Seitensplits – Der teure Mechanismus

Wenn in einer vollständig gefüllten Indexseite eine neue Zeile eingefügt werden muss, teilt SQL Server die Seite in zwei Seiten auf (jeweils etwa 50 % gefüllt) und kopiert die Hälfte der Zeilen auf die neue Seite. Dieser Seitensplit ist sehr teuer (I/O, CPU, Blockaden). Ein niedriger Fillfactor reserviert Platz auf jeder Seite, sodass neue Zeilen häufiger ohne Split eingefügt werden können. Allerdings geht der freie Raum mit der Zeit durch Aktualisierungen und Einfügungen verloren. Regelmäßige Indexwartung (REORGANIZE/REBUILD) stellt den ursprünglichen Fillfactor wieder her.
-- Seitensplit-Tracking mit Extended Events
CREATE EVENT SESSION TrackPageSplits ON SERVER
ADD EVENT sqlserver.page_split (WHERE database_id = DB_ID())
ADD TARGET package0.event_file(SET filename = N'PageSplits.xel');
📌 Merke: Ein niedriger Fillfactor reduziert die Häufigkeit von Seitensplits, beseitigt sie aber nicht vollständig – insbesondere wenn die Einfügungen nicht genau in die reservierten Lücken passen.

📉 Fragmentierung – Ein natürlicher Feind

Ein niedriger Fillfactor führt nicht direkt zu Fragmentierung, sondern verhindert sie sogar, weil Seitensplits seltener sind. Allerdings: Wenn Sie einen sehr niedrigen Fillfactor wählen, kann der Index nach vielen Aktualisierungen dennoch fragmentieren. Die logische Fragmentierung (die Reihenfolge der Seiten) ist ein separater Wert. Die Indexwartung (REORGANIZE/REBUILD) korrigiert beide Probleme.
-- Fragmentierungsgrad und durchschnittliche Seitendichte prüfen
SELECT 
  OBJECT_NAME(ips.object_id) AS TableName,
  i.name AS IndexName,
  ips.avg_fragmentation_in_percent,
  ips.page_count,
  ips.avg_page_space_used_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') ips
JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 10
ORDER BY ips.avg_fragmentation_in_percent DESC;
💡 Hinweis: avg_page_space_used_in_percent zeigt, wie voll die Seiten im Durchschnitt sind. Ein Wert nahe des Fillfactors deutet auf eine gute Packungsdichte hin.

🎯 Sonderfälle: GUIDs, Identity, Sequenzen

Identity / aufsteigende Schlüssel (z. B. INT IDENTITY): Neue Werte sind immer größer als bestehende. Daher werden Einfügungen immer am Ende des Index-Baums vorgenommen. Seitensplits sind selten. Ein hoher Fillfactor (90–100) ist optimal.

GUIDs (z. B. uniqueidentifier, NEWID()): Zufällige Einfügungen über den gesamten Schlüsselraum verteilt. Seitensplits sind sehr häufig. Ein niedriger Fillfactor (z. B. 70–80) ist sinnvoll. Noch besser: Verwenden Sie NEWSEQUENTIALID() für sequentielle GUIDs oder verzichten Sie auf GUID-Clustered-Indizes (besser einen eigenen INT).

Primärschlüssel auf nicht-sequentiellen Spalten: Passen Sie den Fillfactor entsprechend der Einfügerate an.
-- Beispiel: Clustered Index auf sequentieller ID – Fillfactor 98
CREATE TABLE dbo.Sequentiell (
  Id INT IDENTITY(1,1) PRIMARY KEY WITH (FILLFACTOR = 98)
);

-- Beispiel: Nicht-sequentieller GUID – Fillfactor 75
CREATE TABLE dbo.Zufaellig (
  GuidId UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY WITH (FILLFACTOR = 75)
);
🚨 Warnung: Vermeiden Sie nach Möglichkeit clustered Indizes auf zufälligen GUIDs. Die Fragmentierung wird extrem hoch sein, selbst mit niedrigem Fillfactor. Besser ein INT als Clustering-Key und den GUID als Non‑Clustered Index.

🔄 Indexwartung und Fillfactor – Zusammenwirken

Indexwartung (REBUILD oder REORGANIZE) ist nötig, um den Fillfactor wiederherzustellen, nachdem er durch Schreibvorgänge abgenutzt wurde. Die Ola Hallengren Maintenance Solution z. B. erlaubt es, beim Rebuild den gewünschten Fillfactor zu definieren. Typische Intervalle:
  • Heavy‑Insert Tabellen mit niedrigem Fillfactor → wöchentlicher REBUILD oder REORGANIZE.
  • Overwiegend lesende Tabellen mit hohem Fillfactor → monatlicher REBUILD.
Wichtig: Ein REORGANIZE komprimiert die Seiten und entfernt freien Raum, setzt den Fillfactor aber nicht zurück. Nur REBUILD respektiert den ursprünglichen Fillfactor-Wert.
-- Beispiel: Index REBUILD mit neuem Fillfactor
ALTER INDEX IX_BigTable_OrderDate
  ON dbo.BigTable
  REBUILD WITH (FILLFACTOR = 85, ONLINE = ON);
💡 Pro-Tipp: Verwenden Sie eine automatisierte Indexwartung, die den Fragmentierungsgrad und die Seitendichte analysiert. Dann können Sie entscheiden, ob ein REBUILD mit angepasstem Fillfactor nötig ist.

🧪 Praxistest – So finden Sie den optimalen Wert

Führen Sie reproduzierbare Lasttests mit typischen INSERT- und SELECT-Operationen durch. Gehen Sie wie folgt vor:
  1. Kopieren Sie die Tabelle (mit allen Indizes) in eine Testumgebung.
  2. Messen Sie die durchschnittliche INSERT-Dauer und SELECT-Dauer mit Fillfactor 100 (Standard).
  3. Wiederholen Sie den Test mit Fillfactor 90, 80, 70.
  4. Ermitteln Sie den Punkt, an dem INSERTs akzeptabel bleiben, ohne dass SELECTs zu stark leiden.
  5. Berücksichtigen Sie auch den Speicherplatzverbrauch – mehr freier Raum bedeutet mehr Speicherbedarf, was Pufferpool und Backup vergrößert.
-- Beispiel: Test mit unterschiedlichen Fillfactors (Pseudocode)
-- 1. Index neu erstellen mit Fillfactor X
ALTER INDEX IX_Test ON dbo.TestTable REBUILD WITH (FILLFACTOR = 80);
-- 2. 100.000 INSERTs durchführen (Zeit messen)
-- 3. SELECT COUNT(*) und durchschnittliche Seek-Zeit messen
📌 Wichtig: Ein einmal gemessener Wert gilt nur für Ihr spezifisches Schema und Ihre Workload. Überprüfen Sie regelmäßig, ob die Annahmen noch gültig sind.

⚠️ Typische Irrtümer über den Fillfactor

  • ❌ „Ein niedriger Fillfactor (z. B. 50) reduziert Fragmentierung.“ → Richtig, weil Seitensplits seltener werden. Aber falsch, weil er die Fragmentierung nicht beseitigt – sie verschiebt sich nur. Zudem kostet er viel Platz.
  • ❌ „Fillfactor 100 ist immer gut.“ → Nein, bei vielen Einfügungen führt er zu massiven Seitensplits.
  • ❌ „Man sollte den Fillfactor auf Datenbankebene festlegen.“ → Das ist möglich (ALTER DATABASE ... SET FILL_FACTOR), aber selten sinnvoll. Jeder Index hat andere Anforderungen.
  • ❌ „Der Fillfactor wird durch REORGANIZE wiederhergestellt.“ → Falsch. REORGANIZE defragmentiert nur, aber füllt keine leeren Seiten auf. Dafür ist REBUILD nötig.
  • ❌ „Je niedriger der Fillfactor, desto weniger Fragmentierung.“ → Nicht ganz. Sehr niedrige Werte bedeuten viele Seiten mit viel freiem Platz, was logisch keine Fragmentierung ist, aber die Scan‑Leistung beeinträchtigt.
🚀 Fazit: Der Fillfactor ist ein feines Abstimmungswerkzeug – für die meisten Indizes ist der Standard (0 = 100) völlig ausreichend. Ändern Sie ihn nur, wenn Sie nachweislich Seitensplit-Probleme (hohe avg_fragmentation_in_percent trotz regelmäßiger Wartung) oder extrem viele Schreiboperationen haben.

✅ Best Practices – So gehen Sie richtig vor

  • Standardwert belassen: Für die meisten Tabellen ist 0 (100) ausreichend.
  • Nur auf Indizes mit vielen Änderungen anpassen: Überwachen Sie Seitensplits über Extended Events.
  • Für sequentielle Schlüssel (IDENTITY) hohen Fillfactor verwenden (90–100).
  • Für zufällige Schlüssel (GUID) niedrigeren Fillfactor (70–85). Besser sequentielle GUIDs.
  • Nicht‑clustered Indizes auf häufig geänderte Spalten separat betrachten.
  • Regelmäßige Indexwartung: REBUILD mit angepasstem Fillfactor, falls nötig.
  • Nach Anpassung: Fragmentierung und Seitendichte beobachten.
  • Dokumentieren Sie Ihre Fillfactor‑Entscheidungen.
💡 Letzter Tipp: Verwechseln Sie nicht Fillfactor mit PAD_INDEX. PAD_INDEX wendet den Fillfactor auch auf die Zwischenebenen des B‑Baums an (sonst nur auf Blattebene). Im Normalfall ist PAD_INDEX nicht nötig.
Nächster SQL Server – QUOTED_IDENTIFIER & ANSI_NULLS
Cookies user preferences
We use cookies to ensure you to get the best experience on our website. If you decline the use of cookies, this website may not function as expected.
Accept all
Decline all
Analytics
Tools used to analyze the data to measure the effectiveness of a website and to understand how it works.
Google Analytics
Advertisement
If you accept, the ads on the page will be adapted to your preferences.
Google Ad
Save