Während der Erstellung eines Artikels für SIMPLE-TALK kam ein sehr interessanter Aspekt in Bezug auf HEAPS in Verbindung mit non-clustered Indexen in den Fokus: „Wird beim Neuaufbau eines non-clustered Index die Position des Forwarded Record im non-clustered Index gespeichert oder die ursprüngliche Adresse?. Was ein Forwarded Record ist und welchen Einfluss ein Forwarded Record auf die Performance haben kann, beschreibt der Artikel “Forwarded Records Intern”. Der nachfolgende Artikel geht auf die Verwaltung von non-clustered Indexen in einem Heap ein.
Testumgebung
Für den Artikel wird eine Tabelle [dbo].[Customer] erstellt, die mit 1.000 Datensätzen* gefüllt wird. Die Tabelle hat folgenden – einfachen – Aufbau:
CREATE TABLE dbo.Company ( Id INT NOT NULL IDENTITY (1, 1), Name VARCHAR(200) NOT NULL, Street VARCHAR(100) NOT NULL, ZIP VARCHAR(10) NOT NULL, City VARCHAR(100) NOT NULL ); GO CREATE NONCLUSTERED INDEX ix_Company_ZIP ON dbo.Company (ZIP); GO
Non-Clustered Index in einem Heap
Ein Non-Clustered Index in einem Heap folgt einer anderen Vorgehensweise als in einem Clustered Index. Während ein non-clustered Index in einem Clustered Index nur den Clusterschlüssel speichert, muss der non-clustered Index für einen Heap immer die absolute Position des Datensatzes speichern, da eine Suche über einen Schlüssel nicht möglich ist. Die nachfolgende Abbildung zeigt die unterschiedliche Indexstruktur für einen Heap und für einen Clustered Index.
Non-Clustered Index und Forwarded Records
Im Zusammenhang mit Forwarded Records kam die Frage auf, ob bei dem Neuaufbau eines non-clustered Index Microsoft SQL Server die “neue” Adresse des Datensatzes im non-clustered Index speichert. Diese Frage kann man ganz klar mit NEIN beantworten. Mit dem nachfolgenden Code wird zunächst die Datenseite ermittelt, auf dem sich der Datensatz befindet.
-- Auf welcher Datenseite liegt der Datensatz mit ZIP = 00142 SELECT FPLC.*, C.* FROM dbo.Company AS C CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%) AS FPLC ORDER BY ZIP; GO
Ein Blick auf die Datenseite 145 zeigt, dass kein weiterer Platz mehr zur Verfügung steht.
DBCC TRACEON (3604); DBCC PAGE (demo_db, 1, 145, 3);
Wird der Datensatz mit der ID = 626 erweitert, so muss es unweigerlich zu einem Forwarded Record kommen. Im Beispiel Fall wird auf der Seite 145 ein FORWARDED_STUB generiert, der auf die neue Datenseite verweist, auf der sich der Datensatz als Forwarded Record befindet.
—Aktualisierung des Datensatzes mit der ID 626 UPDATE dbo.Company SET Street = 'Das ist eine Strasse mit einem gaaaaaaanz langen Namen' WHERE ID = 626;
Wie die Abbildung zeigt, wurde der Datensatz mit der ID = 626 auf die Datenseite 337 in Datendatei 1 in Slot 31 verschoben. Diese Position entspricht – in Binärcode umgewandelt – der RID-Position 0x51 01 00 00 01 00 1f 00. Im nächsten Schritt wird der non-clustered Index neu aufgebaut und anschließend die Indexseiten überprüft.
-- Bestehenden Index neu aufbauen ALTER INDEX ix_Company_ZIP ON dbo.Company REBUILD; GO -- Auf welcher Datenseite ist der Datensatz mit ID = 00142 gespeichert? SELECT * FROM dbo.Company WITH (INDEX(2)) CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%) AS FPLC -- Ein Blick auf die Datenseite... DBCC TRACEON (3604); DBCC PAGE (demo_db, 1, 1920, 3); GO
Die Abbildung zeigt, dass trotz Neuaufbau des Index die Position des Datensatzes im Heap nicht geändert wurde. Dieses Ergebnis ist verständlich, wenn man versteht, wie ein Forwarded Record funktioniert.
Ein Forwarded Record gibt den allokierten Speicherplatz nicht auf sondern hinterlässt lediglich einen Verweis auf die neue Position. Tatsächlich wird der Datensatz selbst immer noch auf der Originalseite verwaltet. Ein non-clustered Index in einem Heap speichert – wie oben beschrieben – die exakte Position des Datensatzes und nicht, wie in einem Clustered Index, einen Schlüsselwert. Somit versteht sich das Ergebnis als Konsequenz daraus, dass bei einem Rebuild des Index die Position des Datensatzes im Heap zu keiner Zeit neu positioniert wurde.
Herzlichen Dank fürs Lesen!
*Achtung: Werbung
Für das Befüllen von Testtabellen verwende ich sehr gern den SQL Data Generator von RedGate. Hiermit lassen sich in wenigen Sekunden komplexe Datenmodelle mit Testdaten füllen.