2016-11-18 106 views
1

我在名爲Shopper的表上有兩個索引。儘管有聚簇索引,SQL Server仍在使用非聚簇索引

聚集索引:

CREATE CLUSTERED INDEX [CI_EMail_ShopperNumID] 
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC) 

非聚集索引

CREATE NONCLUSTERED INDEX [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115] 
ON [dbo].[Shopper] ([EMail] ASC) 
INCLUDE ([DateCreated], [FirstName], [LastLoginDate], [LastName], 
    [MaxEmailVolume], [ShopperNumID], [ShopperSourceCD], [ShopperSourceOther]) 

我運行一個非常簡單的SELECT

SELECT ShopperNumID 
FROM shopper 
WHERE Email = '[email protected]' 

在分析執行計劃,我注意到,非正在使用聚簇索引:

enter image description here

現在,我把非聚集索引:

DROP INDEX IF EXISTS [nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115] 
ON [dbo].[Shopper] 
GO 

並重新運行我的選擇注意到,聚簇索引(最終)使用

Clustered Index being used

有人可以解釋爲什麼(龐大的)非聚集索引被優化引擎使用,而不是(首選)聚集索引?

的Microsoft SQL Server 2016(RTM-GDR)(KB3194716) - 在Windows 10 13.0.1722.0(X64)
開發版(64位)專業版6.3(建設14393 :)

UPDATE: 基於收到的輸入,爲了進一步評估,我在表上創建了另一個非聚集索引,與已有的聚集索引非常相似。

CREATE NONCLUSTERED INDEX [NCI_EMail_ShopperNumID] 
ON [dbo].[Shopper] ([EMail] ASC, [ShopperNumID] ASC) 

目前,該表有3個指標,能夠支持我的SELECT

  1. 聚集索引[CI_EMail_ShopperNumID]
  2. 非聚集索引[nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115]
  3. 非聚集索引[NCI_EMail_ShopperNumID]

現在,當我運行s AME SELECT

SELECT ShopperNumID 
FROM shopper 
WHERE Email = '[email protected]' 

和分析執行計劃,我注意到新創建非聚集索引正在使用: enter image description here

好像優化是堅定的關於使用非聚集索引,不管怎樣!

+0

聚集索引不是真的索引,它是表本身。因此,它包含所有表格列,索引列僅定義數據存儲的排序順序。 –

+0

你的NONCLUSTERED覆蓋索引的用例是什麼?由於它只包含[email]列,並假定它存儲在同一個文件組中,所以在CLUSTERED索引上的性能沒有提高。 – uhleeka

+0

我很好奇 - 哪一個實際上更快? (清除緩存後)。 「SET STATISTICS IO」是否顯示任何特別不同的輸出? –

回答

2

正在使用非聚簇索引,因爲它已根據Email進行了優化以查找行。

您可能認爲它很笨重,但是它被鍵入Email的事實使它成爲您的查詢的理想選擇,即使它包含表中的每一列。

你可能沒有意識到的是,聚集索引同樣龐大,因爲它隱式包含了表中的每個字段。因此,在最壞的情況下(不要設計這樣的東西),你的索引都被鍵入Email,並且都包含每一列。優化器可以選擇使用,真的。

如果你使用這個腳本就可以告訴你實際使用的空間量由非聚簇和聚簇索引:排序聚簇索引和數據行存儲在基於表或視圖:

SELECT o.NAME AS TableOrViewName, 
     i.name As IndexName, 
     i.type_desc As IndexType, 
     i.index_id As IndexOrdinal, 
     s.Name AS SchemaName, 
     p.rows AS RowCounts, 
     p.data_compression_desc As CompressionType, 
     SUM(a.total_pages) * 8/1024.0 AS ObjectSpaceMB, 
     SUM(a.used_pages) * 8/1024.0 AS UsedSpaceMB 
     FROM sys.objects As o 
     LEFT JOIN sys.indexes i ON o.OBJECT_ID = i.object_id 
     JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id 
     JOIN sys.allocation_units a ON p.partition_id = a.container_id 
     LEFT JOIN sys.schemas s ON o.schema_id = s.schema_id 
     WHERE o.NAME NOT LIKE 'dt%' 
     AND o.is_ms_shipped = 0 
     AND i.OBJECT_ID > 255 
     GROUP BY o.Name, 
     i.name, 
     i.type_desc, 
     i.index_id, 
     s.Name, 
     p.data_compression_desc, 
     p.Rows; 
0

MSDN: Clustered and Nonclustered Indexes Described關鍵的價值。這些是包含在索引定義中的列。每個表只能有一個聚簇索引,因爲數據行本身只能按一個順序排序。

非聚簇索引覆蓋(包括)附加的指定列,以便在引用任何包含的列時不需要返回到表。請參閱MSDN:Create Indexes with Included Columns。有效地,非聚集索引就像創建一個包含列的新表,按索引列排序。

對於您的查詢,聚簇索引和非聚簇索引非常接近,唯一的區別是聚簇索引額外按[ShopperNumID]排序。也許查詢優化器正在挑選非聚集索引,因爲它名義上更合適。在這種情況下,更好的配合並不一定意味着更好的性能。

假設羣集索引和非聚簇索引都位於同一存儲介質上,則非聚簇索引佔用空間,但不提供添加的性能值。

1

基本上,它是六個一個或另一個的另一半。

您的聚簇索引和非聚簇索引都具有電子郵件地址的b-tree結構。所以,要麼能夠非常快地找到匹配的電子郵件地址。

那麼,優化程序如何選擇要提取?那麼,在這兩種情況下,如果有一條記錄,那麼一個頁面(數據頁面或索引葉頁)被提取。也許任意選擇非聚集索引。

但是,優化器不知道電子郵件地址匹配的記錄數。因此,它必須根據電子郵件匹配的數量做出決定。如果非聚集索引只有兩列,那麼這將是一件容易的事。索引頁面將包含更多記錄(因爲「記錄」只有兩列),所以與電子郵件匹配的記錄將在較少的頁面上。

儘管如此,非聚集索引是一個包含所有列的覆蓋索引。也許更多的這些適合索引頁面而不是數據頁面(在數據頁面上有一些開銷,它可能比索引頁面上的開銷更多)。

那麼,我們在哪裏得到了?基本操作是通過b-tree(對於兩種索引類型都是相同的)進行搜索,然後讀取匹配的記錄。在大多數情況下,這兩種索引結構在這些操作中將非常相當。對於非聚集索引,SQL Server可能會有輕微的偏好,因爲索引頁上的數據比數據頁上的記錄數多(這是一種猜測)。

+0

感謝您的輸入。請參閱我在帖子中所做的修改。 – AeyJey

0

首先,讚賞查看查詢計劃以查看正在使用哪個索引。查詢優化器會盡量減少IO,但它可以做一些有趣的事情。一般來說,非聚集索引小於聚集索引。如果優化器可以看到非聚集索引可以使用更少的讀取來回答查詢,這是您的問題的答案。如果非聚集索引包含表中的所有列,則會發生異常。我懷疑這可能是你問題的關鍵。

儘管在聚簇索引中使用字符串的確有用處,但請記住聚簇索引始終包含在每個非聚簇索引中。你希望你的聚集索引很小,並且如果不是唯一的話就是有選擇性的,它看起來像ShopperNumbId會符合這個標準,但是我們沒有你的全表。考慮從你的聚集索引中刪除電子郵件地址。

如果您的應用程序需要根據電子郵件地址查找記錄,從而創建列的最小完整覆蓋索引,您需要爲您提供最佳性能,這就是nci_wi_Shopper_D8E9A1BB0660D0838F923BB8587C7115的樣子。

相關問題