聚集索引和非聚集索引之间的性能差异

gre*_*van 2 sql-server indexing sql-server-2012

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

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

可能有任何理智的理由吗?

Ric*_*dCL 5

也许是为了效率。非聚集索引通常小于聚集索引,因为叶子级的聚集索引包含所有(非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);
Run Code Online (Sandbox Code Playgroud)

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

DROP INDEX Ix_T1
    ON dbo.T1;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

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

ALTER TABLE dbo.T1
   DROP CONSTRAINT PK_T1;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

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

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

CREATE UNIQUE CLUSTERED INDEX Ix_T1
   ON T1(id);
Run Code Online (Sandbox Code Playgroud)

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

ALTER TABLE dbo.T1
    DROP CONSTRAINT PK_T1;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

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

DROP INDEX Ix_T1
    ON dbo.T1;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

因此,如果我的理论是正确的,那么删除非聚集索引可能会降低性能。您可能需要进行一些调查和测试。

数据库模式是否有任何文档解释索引为什么存在?还是可以问设计数据库的人?

我使用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;
Run Code Online (Sandbox Code Playgroud)

测试的实际执行计划1。

在此处输入图片说明

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

在此处输入图片说明

测试3的实际执行计划。索引搜索的估计子树成本超过测试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];
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

以下是一些说明聚簇索引和非聚簇索引的参考。

TechNet>表和索引数据结构体系结构:https : //technet.microsoft.com/zh-cn/library/ms180978(v=sql.105).aspx

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

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