【问题标题】:Difference in performance between clustered and non-clustered indexes聚集索引和非聚集索引之间的性能差异
【发布时间】:2016-09-05 07:41:12
【问题描述】:

我在 DB 中找到了一个表,在同一列上有两个单独的索引。列类型为int,并且该列上有一个聚集的主键。除此之外,同一列上还有一个唯一的非聚集索引。索引具有相同的选项(排序方向和其他),并且不包含任何包含的列。

此索引被其他一些表中的外键约束使用,因此如果不重新创建外键约束,我无法删除它。

这有什么合理的理由吗?

【问题讨论】:

  • 外键约束使用一个索引而不使用另一个索引是什么意思?如何为此目的定义索引。您应该能够删除非聚集索引。
  • 我的意思是,我收到以下消息:“索引 'dbo.Table.IX_Index' 上不允许使用显式 DROP INDEX。它被用于执行 FOREIGN KEY 约束”。
  • 在这个问题中也找到了相关的 Jeff Moden 的回答:stackoverflow.com/questions/18707037/… 但是在原始文档中没有找到任何证据。

标签: sql-server indexing sql-server-2012


【解决方案1】:

也许是为了效率。非聚集索引通常小于聚集索引,因为叶级别的聚集索引包含所有(非 LOB)字段。所以也许它更喜欢使用非聚集索引来强制执行外键约束。

更新:我使用 AdventureWorks 数据库做了一些进一步的测试,证实了这个理论。见下文。

我可以使用两个表 T1 和 T2 重现该问题。 T1 是父级,从 T2 到 T1 存在外键关系。

当 T1 具有聚簇主键约束和非聚簇唯一索引 Ix-T1 时,我可以更改表并删除聚簇主键约束,但我不能像您发现的那样删除 Ix-T1。

如果我用非聚簇主键约束和聚簇唯一索引 Ix_T1 创建 T1,那么情况正好相反:我可以删除 Ix-T1,但我不能删除主键约束。

CREATE TABLE T1
(
    id int NOT NULL CONSTRAINT PK_T1 PRIMARY KEY CLUSTERED
);

CREATE UNIQUE NONCLUSTERED INDEX Ix_T1
    ON T1(id);

CREATE TABLE T2
(
   id2 int NOT NULL PRIMARY KEY CLUSTERED,
   id1 int NOT NULL FOREIGN KEY REFERENCES dbo.T1(id)
);

INSERT INTO T1 (id)
    VALUES (1), (2), (3), (4);

INSERT INTO T2 (id2, id1)
    VALUES (11, 1), (12, 2), (13, 3);

尝试删除非聚集索引。这失败了。

DROP INDEX Ix_T1
    ON dbo.T1;

但是我可以删除集群主键约束。

ALTER TABLE dbo.T1
   DROP CONSTRAINT PK_T1;

使用具有非聚集主键和聚集唯一索引的 T1 重复测试。

CREATE TABLE T1
(
    id int NOT NULL CONSTRAINT PK_T1 PRIMARY KEY NONCLUSTERED
);

CREATE UNIQUE CLUSTERED INDEX Ix_T1
   ON T1(id);

这一次,我不能删除主键约束。

ALTER TABLE dbo.T1
    DROP CONSTRAINT PK_T1;

但是我可以删除聚集索引。

DROP INDEX Ix_T1
    ON dbo.T1;

因此,如果我的理论是正确的,那么如果您删除非聚集索引,性能可能会受到影响。您可能想做一些调查和测试。

是否有任何数据库架构文档解释索引存在的原因?或者你能问一下设计数据库的人吗?

我使用 AdventureWorks2014 做了一些进一步的测试,证实了我的理论。

USE AdventureWorks2014;
GO
CREATE SCHEMA test;
GO

-- Create two test tables
SELECT *
    INTO test.SalesOrderHeader
    FROM Sales.SalesOrderHeader;

SELECT *
    INTO test.SalesOrderDetail
    FROM Sales.SalesOrderDetail;

-- Test 1 - Clustered primary key and nonclustered index
ALTER TABLE test.SalesOrderHeader
    ADD CONSTRAINT PK_Test_SalesOrderHeader PRIMARY KEY CLUSTERED (SalesOrderID);

CREATE UNIQUE NONCLUSTERED INDEX Ix_Test_SalesOrderHeader
    ON test.SalesOrderHeader(SalesOrderID);

-- Test 2 - Nonclustered primary key and clustered index
CREATE UNIQUE CLUSTERED INDEX Ix_Test_SalesOrderHeader
    ON test.SalesOrderHeader(SalesOrderID);

ALTER TABLE test.SalesOrderHeader
    ADD CONSTRAINT PK_Test_SalesOrderHeader PRIMARY KEY NONCLUSTERED (SalesOrderID);

-- Test 3 - Clustered primary key only
ALTER TABLE test.SalesOrderHeader
    ADD CONSTRAINT PK_Test_SalesOrderHeader PRIMARY KEY CLUSTERED (SalesOrderID);

-- Same for all tests
ALTER TABLE test.SalesOrderDetail
    ADD CONSTRAINT PK_Test_SalesOrderDetail PRIMARY KEY CLUSTERED (SalesOrderDetailID);

ALTER TABLE test.SalesOrderDetail
    ADD CONSTRAINT FK_Test_SalesOrderDetail_SalesOrderHeader FOREIGN KEY (SalesOrderID) REFERENCES test.SalesOrderHeader(SalesOrderID);

-- Update 100 records in SalesOrderDetail
UPDATE test.SalesOrderDetail
    SET SalesOrderID = SalesOrderID + 1
    WHERE SalesOrderDetailID BETWEEN 57800 AND 57899;

测试 1 的实际执行计划。

测试 2 的实际执行计划。Index Seek 算子的估计子树成本与测试 1 几乎相同。

测试 3 的实际执行计划。Index Seek 的估计子树成本是测试 1 或测试 2 的两倍以上。

这是一个测量索引大小的查询。 (测试 1 配置。)您可以清楚地看到聚集索引要大得多。

-- Measure sizes of indexes
SELECT I.object_id, I.name, I.index_id, I.[type], I.[type_desc], SUM(s.used_page_count) * 8 AS 'IndexSizeKB'
    FROM sys.indexes AS I
        INNER JOIN sys.dm_db_partition_stats AS S
            ON S.[object_id] = I.[object_id] AND S.index_id = I.index_id
    WHERE I.[object_id] = OBJECT_ID('test.SalesOrderHeader')
    GROUP BY I.object_id, I.name, I.index_id, I.[type], I.[type_desc];

这里有一些解释聚集索引和非聚集索引的参考资料。

TechNet > 表和索引数据结构架构:https://technet.microsoft.com/en-us/library/ms180978(v=sql.105).aspx

培训工具包 70-462 管理 Microsoft SQL Server 2012 数据库 > 第 10 章:索引和并发 > 第 1 课:实现和维护索引

Microsoft SQL Server 2012 Internals by Kalen Delaney > 第 7 章:索引:内部和管理

【讨论】:

  • 你能提供任何证明 NCI 比 CI 更有效的证据吗?关于大小:本文说反之亦然 NCI 需要比 CI 更多的空间:sqlserverlogexplorer.com/… 无论如何,磁盘上的大小不能成为效率的一个因素,因为它在搜索时不会被完全读取。
  • 你指向的文章很简短。他对非聚集索引的描述令人困惑。他没有区分堆上的非聚集索引和聚集索引。他关于非聚集索引比聚集索引需要更多空间的说法是不正确的。叶级别的聚集索引使用更多空间,因为叶节点包含所有非 lob 字段,而非聚集索引包含指向聚集索引或堆的指针。
  • 我在答案中添加了三个引用来解释表和索引。
  • 我使用 AdventureWorks 数据库添加了进一步的测试,这证实了我的理论。我还添加了一个显示聚集索引和非聚集索引大小的查询;聚集索引要大得多。
  • 很好的研究。现在我相信它确实是出于性能原因而完成的。谢谢。
猜你喜欢
  • 2011-10-12
  • 2013-08-07
  • 2011-07-01
  • 2021-01-14
  • 2023-03-23
  • 2016-01-05
  • 2011-07-02
相关资源
最近更新 更多