聚簇索引 - 多部分与单部分索引以及插入/删除的影响

时间:2010-05-27 20:25:12

标签: sql sql-server indexing clustered-index

这个问题是关于插入完成后重组聚簇索引中的数据会发生什么。我假设在具有聚簇索引的表上执行插入应该更昂贵,因为重组聚簇索引中的数据涉及更改磁盘上数据的物理布局。我不知道如何通过我在工作中遇到的一个例子来表达我的问题。

假设有一个表(垃圾),并且在表上有两个查询,第一个查询按名称搜索,第二个查询按名称搜索。当我正在处理数据库时,我发现该表已经创建了两个索引,一个用于支持每个查询,如下所示:

--drop table Junk1
CREATE TABLE Junk1
(
    Name char(5),  
    Something char(5),
    WhoCares int
)

CREATE CLUSTERED INDEX IX_Name ON Junk1
(
    Name
)

CREATE NONCLUSTERED INDEX IX_Name_Something ON Junk1
(
    Name, Something
)

现在,当我查看这两个索引时,似乎IX_Name是多余的,因为任何希望按名称搜索的查询都可以使用IX_Name_Something。所以我将消除IX_Name并使IX_Name_Something成为聚集索引:

--drop table Junk2
CREATE TABLE Junk2
(
    Name char(5),  
    Something char(5),
    WhoCares int
)

CREATE CLUSTERED INDEX IX_Name_Something ON Junk2
(
    Name, Something
)

有人建议应该保留第一个索引方案,因为它会导致更高效的插入/删除(假设不需要担心Name和Something的更新)。那会有意义吗?我认为第二种索引方法会更好,因为它意味着需要维护一个较少的索引。

我希望能够深入了解这个具体的例子,或者向我介绍有关维护聚簇索引的更多信息。

3 个答案:

答案 0 :(得分:9)

是的,当您的聚簇索引不是最佳时,插入现有表(或其页面)的中间可能会很昂贵。最糟糕的情况是页面拆分:页面上的一半行必须移动到其他地方,并且需要更新索引(包括该表上的非聚集索引)。

您可以使用正确的聚簇索引来缓解该问题 - 理想情况是:

  • 狭窄(只有一个字段,尽可能小)
  • 静态(永不改变)
  • unique(以便SQL Server不需要向行添加4字节的唯一符)
  • 不断增加(如INT IDENTITY)

您需要一个窄键(理想情况下是一个INT),因为每个非聚集索引中的每个条目都将包含聚类键 - 您不希望在聚类中放置大量列键,你也不想把VARCHAR(200)放在那里!

随着聚簇索引的不断增加,您将永远不会看到页面拆分的情况。您可能遇到的唯一碎片来自删除(“瑞士奶酪”问题)。

查看Kimberly Tripp关于索引的excellet博客文章 - 最值得注意的是:

  

假设有一个表(垃圾)和   有两个查询已完成   该表,第一个查询搜索   名称和第二个查询搜索   名字和东西。正如我正在努力的那样   数据库我发现了   表已创建两个   索引,一个支持每个查询,   像这样:

这绝对没有必要 - 如果你在(Name, Something)上有一个索引,那么如果你只搜索和限制WHERE Name = abc那个索引也可以使用 - 只有一个单独的索引完全不需要Name列,只会浪费空间(并且需要花费时间来保持最新)。

所以基本上,你只需要(Name, Something)上的一个索引,我同意你的看法 - 如果你在这个表上没有其他索引,那么你应该能够将它作为聚簇密钥。由于这个密钥不会不断增加并且可能也会改变(对吧?),这可能不是一个好主意。

另一种选择是引入代理ID INT IDENTITY和群集 - 有两个好处:

  • 这应该是一个很好的集群密钥,包括不断增加的 - >对于INSERT操作的页面拆分和性能,你永远不会有任何问题
  • 您仍然可以获得拥有群集密钥的所有好处(请参阅Kim Tripps的博客文章 - 群集表几乎总是优于堆)

答案 1 :(得分:0)

  

有人建议应该保留第一个索引方案,因为它会导致更有效的插入/删除

这是一个虚假的主张。有序数据是有序数据,将执行相同的IO。

SET STATISTICS IO ON
-- your insert statement here

答案 2 :(得分:0)

您只能在一列上创建聚簇索引,而不是两列或更多列,因此请选择您的应用主要查询的列,例如客户全名的通配符查询等(请参阅discussion