如果索引中包含VARCHAR(MAX)列,则整个值是否始终存储在索引页中?


12

我是出于好奇而问这个问题,受到这个问题的启发。

我们知道VARCHAR(MAX)长于8000字节的值不会存储在行中,而是存储在单独的LOB页中。随后使用该值检索一行需要两个或多个逻辑IO操作(从本质上讲,比理论上需要多一个)。

如链接的问题所示VARCHAR(MAX),我们可以将列INCLUDEd 添加到唯一索引。如果此列的值长度超过8000个字节,这些值是否仍将“内联”存储在索引叶页中,还是将它们移到LOB页中?

Answers:


16

超过8000个字节的值不能“内联”存储。它们存储在LOB页面上。您可以通过sys.dm_db_index_physical_stats看到它。从一个简单的表开始:

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);

现在插入一些行,该行的值占该VARCHAR(MAX)列的8000个字节,并检出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'); 

索引中没有LOB页:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 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 
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

但是,如果我添加的值占8001字节的行:

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'); 

现在,我刚插入的每一行的索引中都有1个LOB页面:

╔═════════════╦════════════════════╦══════════════════════╦════════════╦══════════════╗
 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 
╚═════════════╩════════════════════╩══════════════════════╩════════════╩══════════════╝

您还可以通过SET STATISTICS IO ON;和正确的查询查看此内容。考虑以下查询,该查询仅查看具有8000字节的行:

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

执行结果:

扫描计数1,逻辑读2560,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

如果我改为查询8001字节的行:

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

现在我看到lob读取:

扫描计数1,逻辑读20,物理读0,预读0,lob逻辑读5080,lob物理读0,lob预读0。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.