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

Ans*_*sss 7 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
)
Run Code Online (Sandbox Code Playgroud)

现在,当我查看这两个索引时,似乎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
)
Run Code Online (Sandbox Code Playgroud)

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

我将非常感谢您对此特定示例的任何见解或指导我了解有关维护聚簇索引的更多信息.

mar*_*c_s 10

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

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

  • 狭窄(只有一个字段,尽可能小)
  • 静态(永不改变)
  • 唯一的(以便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的博客文章 - 集群表几乎总是优于堆)