在非常特殊的情况下,例如包含 ContactID 的表很大,和/或行很大(即很多大的 varchar),与将 ContactID 作为聚集索引相比,扫描聚集索引可能会占用大量 IO/内存索引 AND 作为非聚集索引。
如果查询需要按 ContactID 扫描表,并且 ContactID 上只有一个聚集索引,则从磁盘读取整行数据。但是,如果您在 ContactID 上还有一个非聚集索引,则只会从磁盘读取 ContactID。
这与集群与非集群在磁盘上的存储方式有关。聚集索引由 ContactID 存储,但所有行数据也与它一起存储。假设每行大到可以占用一页(8KB),那么扫描 100,000,000 行需要 800,000,000 kb 的磁盘 io。
非聚集索引只会将 ContactID 存储在其“行”中。假设 ContactID 为 8 字节 (bigint),则 1000 行非聚集索引可以放在一页 (8KB) 中。现在,通过 ContactID 扫描 100,000,000 行,只需要 (100,000,000 / 1000 * 8) = 800,000 KB 的磁盘 io。
如果正在分析的查询被相当频繁地调用,则 800,000 KB 与 800,000,000 KB 相比意义重大。
然而,正如 Evadman 所建议的,Tuning Advisor 只关注特定的工作负载。在大多数情况下,额外的非聚集索引只是插入/删除的额外工作量。
现实生活中的例子,我使用一个有很多 varchars 的表。聚集索引为 5521 MB。有几个查询,每秒调用多次,最终对聚集索引列进行部分扫描(我们称之为 P_ID)。 P_ID 上的非聚集索引为 211 MB(比聚集 Idx 小 26 倍)。这导致查询执行时间以及磁盘和内存负载显着减少。
奖励:查询聚集索引和非聚集索引的大小
DECLARE @TableName VARCHAR(200)
SET @TableName = 'NAME_OF_YOUR_TABLE'
SELECT
OBJECT_NAME(i.OBJECT_ID) AS TableName,
i.name AS IndexName,
i.index_id AS IndexID,
8 * SUM(a.used_pages) AS 'Indexsize(KB)',
(8 * SUM(a.used_pages)) / 1024 AS 'Indexsize(MB)'
FROM sys.indexes AS i
JOIN sys.partitions AS p
ON p.OBJECT_ID = i.OBJECT_ID AND p.index_id = i.index_id
JOIN sys.allocation_units AS a
ON a.container_id = p.partition_id
WHERE OBJECT_NAME(i.object_id) = @TableName
GROUP BY i.OBJECT_ID,i.index_id,i.name
ORDER BY OBJECT_NAME(i.OBJECT_ID),i.index_id