Wenn eine VARCHAR (MAX) -Spalte in einem Index enthalten ist, wird der gesamte Wert immer auf den Indexseiten gespeichert?

12

Ich frage dies aus Neugier, inspiriert von dieser Frage .

Wir wissen, dass VARCHAR(MAX)Werte, die länger als 8000 Bytes sind, nicht in Zeilen, sondern auf separaten LOB-Seiten gespeichert werden. Das anschließende Abrufen einer Zeile mit einem solchen Wert erfordert zwei oder mehr logische E / A-Operationen (im Wesentlichen wäre eine mehr als sonst theoretisch erforderlich).

Wir können eine VARCHAR(MAX)Spalte als INCLUDEd zu einem eindeutigen Index hinzufügen , wie in der verknüpften Frage gezeigt. Wenn diese Spalte Werte mit einer Länge von mehr als 8000 Byte enthält, werden diese Werte dann weiterhin "inline" auf den Indexblattseiten gespeichert oder werden sie auch auf LOB-Seiten verschoben?

mustaccio
quelle

Antworten:

16

Werte, die 8000 Bytes überschreiten, können nicht "inline" gespeichert werden. Sie werden auf LOB-Seiten gespeichert. Sie können dies mit sys.dm_db_index_physical_stats sehen . Beginnen Sie mit einer einfachen Tabelle:

USE tempdb;

DROP TABLE IF EXISTS #LOB_FOR_ME;

CREATE TABLE #LOB_FOR_ME (
ID BIGINT,
MAX_VERNON_WAS_HERE VARCHAR(MAX) 
);

CREATE INDEX IX ON #LOB_FOR_ME (ID) INCLUDE (MAX_VERNON_WAS_HERE);

Fügen Sie nun einige Zeilen mit Werten ein, die 8000 Byte für die VARCHAR(MAX)Spalte benötigen, und überprüfen Sie die DMF:

USE tempdb;

INSERT INTO #LOB_FOR_ME
SELECT 1, REPLICATE('Z', 8000)
FROM master..spt_values;

SELECT index_level, index_type_desc, alloc_unit_type_desc, page_count, record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('#LOB_FOR_ME'), 2, NULL , 'DETAILED'); 

Der Index enthält keine LOB-Seiten:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 index_level   index_type_desc    alloc_unit_type_desc  page_count  record_count 
╠═════════════╬════════════════════╬══════════════════════╬════════════╬══════════════╣
           0  NONCLUSTERED INDEX  IN_ROW_DATA                 2540          2540 
           1  NONCLUSTERED INDEX  IN_ROW_DATA                   18          2540 
           2  NONCLUSTERED INDEX  IN_ROW_DATA                    1            18 
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

Aber wenn ich Zeilen mit Werten hinzufüge, die 8001 Bytes benötigen:

USE tempdb;

INSERT INTO #LOB_FOR_ME
SELECT 2, REPLICATE(CAST('Z' AS VARCHAR(MAX)), 8001)
FROM master..spt_values;

SELECT index_level, index_type_desc, alloc_unit_type_desc, page_count, record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('#LOB_FOR_ME'), 2, NULL , 'DETAILED'); 

Jetzt habe ich 1 LOB-Seite im Index für jede Zeile, die ich gerade eingefügt habe:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 index_level   index_type_desc    alloc_unit_type_desc  page_count  record_count 
╠═════════════╬════════════════════╬══════════════════════╬════════════╬══════════════╣
           0  NONCLUSTERED INDEX  IN_ROW_DATA                 2556          5080 
           1  NONCLUSTERED INDEX  IN_ROW_DATA                   18          2556 
           2  NONCLUSTERED INDEX  IN_ROW_DATA                    1            18 
           0  NONCLUSTERED INDEX  LOB_DATA                    2540          2540 
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

Sie können dies auch mit SET STATISTICS IO ON;und der richtigen Abfrage sehen. Betrachten Sie die folgende Abfrage, die nur Zeilen mit 8000 Bytes betrachtet:

SELECT SUM(LEN(MAX_VERNON_WAS_HERE))
FROM #LOB_FOR_ME
WHERE ID = 1;

Ergebnisse bei der Ausführung:

Scananzahl 1, logische Lesevorgänge 2560, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

Wenn ich stattdessen die Zeilen mit 8001 Bytes abfrage:

SELECT SUM(LEN(MAX_VERNON_WAS_HERE))
FROM #LOB_FOR_ME
WHERE ID = 2;

Jetzt sehe ich Lob liest:

Scananzahl 1, logische Lesevorgänge 20, physische Lesevorgänge 0, Vorlesevorgänge 0, logische Vorlesevorgänge 5080, physikalische Vorlesevorgänge 0, Vorlesevorgänge 0.

Joe Obbish
quelle