SQL Server 2008的非聚集索引是否包含聚集索引字段?

5

好的,我需要再详细说明一遍。我已经在线阅读了相关文章,但仍然没有找到明确的答案。

在 SQL Server 2008 中,我有一个“核心”表格,大约有 50,000 条记录,并且有很多读取活动,在所有查询中都以相同的方式使用。这些数据每月更新一次,并且每秒钟会被读取数百次。

这些数据在常用情况下具有聚集索引。假设聚集索引是:

聚集索引

Field1 int
Field2 int
Field3 int
Field4 int
Field5 int

现在,除此之外没有更多的数据了,所以把额外的几列放到“包含的列”中是有道理的,但是SQL Server不允许在聚集索引上使用包含的列。因此,我们需要一个第二个索引,其基本上与聚集索引相同,具有其他列作为“包含的列”。然而,根据我所读的内容,我认为这可能是多余的?非聚集覆盖索引。
Field1 int
Field2 int
Field3 int
Field4 int
Field5 int

包含的列

Field6 varchar(96)
Field7 varchar(96)

非聚集索引是否已经定义了聚集索引中的列?
如果是这样,如何创建一个没有任何列(除了聚集索引中已有的列)的第二个索引?换句话说,“这个索引与聚集索引完全相同…只是包含了一些包括的列”。
或者,将所有列都放入聚集索引中(包括不识别记录的两列)是否更好?varchar列更新的频率更高(每天几次而不是每月一次),所以我本来想将它们排除在聚集索引之外,但我认为它们足够深,不会对索引树产生影响,从而在发生更改时导致任何重新平衡。
因此,是否有一种有效的方法来设置这些索引,使得这个表的所有列都可以通过索引访问,而不需要返回到表格?
4个回答

5
是的 - 非聚集索引通过聚集键(当表具有聚集键时)或行 ID(当表没有聚集键时)访问表中的数据,因此它会自动包括聚集索引字段。这也是为什么更改聚集索引会强制重建所有非聚集索引的原因之一。
如果该索引满足大量查询,则具有2个包含字段的附加 NC 索引可能是有效的,但我不确定它是否解决了正确的问题。
将另外 2 个字段包含在聚集键中并不理想,现在在 NC 索引中已确认,您可以看到该表上的每个索引都在其中批量包含聚集键。
这是您希望聚集键尽可能窄的主要原因,如果有什么问题,您应该检查聚集键并询问为什么选择了一个 5 字段聚集键,这是否会导致碎片化?
您可能最好使用人工值(Identity)作为聚集键,并使用唯一的 NC 索引来强制执行您使用 5 字段聚集键时的唯一性。

很好的回应。在这个例子中,表确实有一个“ID”列,但它基本上是没有意义和未使用的。所有查询都通过这些索引字段从该表请求数据。 - Flipster
假设您可以接受任何碎片化,那么您可以将其用作聚集索引。具有2个附加字段的NC索引可能会提供性能提升(取决于聚集表的宽度与索引的宽度),但是您还必须了解选择/插入等比率。索引和调整没有一种适合所有情况的方法。 - Andrew
我之前提到过:选择操作 = 每秒100次。插入操作 = 每月1次。对于大多数实际目的而言,这是只读数据。 - Flipster
每秒100次...每8小时2.88百万次访问,每24小时8.64百万次访问(假设每秒只有100次)- 确保数据库具有足够的内存,以便所有50k条记录都将位于缓存中。 - Andrew
在我们的第三方数据库中,他们创建了这些巨大的复合聚集索引 - 这导致所有其他索引都很大,因为它们必须包括所有引用的聚集索引字段。对于那些不相信我的人,请将聚集索引中的一个字段包含到新索引中并重新构建。您会发现索引的大小没有改变。 - MikeTeeVee
显示剩余4条评论

4
聚集索引不需要包含。包含是指在索引树的最低级别存储额外的数据。这就是聚集索引中的数据。因此,您不需要重叠索引。
但是,如果内存占用是您关注的问题,则需要缩小表格。对于50k行,我会考虑从-32768开始的smallint代理键。然后,您可以删除每个NC索引中C键的开销。这意味着您可以像您问题中提到的那样拥有一个覆盖索引。
请注意,一旦您的执行计划被缓存并且数据在缓存中,那么您的查询将来自内存。您的使用方式意味着它会在缓存中停留一段时间。缺乏更新意味着您不会获得基于统计信息的重新编译。
但是,如果您的数据几乎是静态的,那么如果性能是一个问题,为什么要调用SQL Server呢?将其缓存。根据我的缓存评论,消除可能是您最大开销的网络往返。
我们将一些查找和缓存外包给客户端以减少服务器负载(在高峰期我们有约20秒钟的50k次写入)。

虽然这里所有的答案都很相似,但我认为你将问题提炼到了其本质,并为我们提供了“可操作”的信息,而没有过多地涉及关系型数据库理论。谢谢! - Flipster

1
“将额外的几列放入“包含的列”中是有意义的,但SQL Server不允许在聚集索引上使用包含的列。”
因为聚集索引已经包含了所有列,所以无法添加额外的列。这也是为什么它被称为聚集索引。
“因此,我们有第二个索引,其基本上与聚集索引具有相同的字段,其他列作为“包含的列”。但是,根据我所读到的,我认为这可能是多余的?”
是的,这可能是多余的。只有在聚集索引无法适应内存的极少数情况下才会出现这种情况。
“非聚集索引是否已经定义了聚集索引中的列?”
很可能:非聚集索引包含指向聚集索引的指针。如果聚集索引是唯一的,则该指针包含所有聚集索引字段。(在大多数情况下,这些字段与主键对应。)
那么,有没有一种有效的方法来设置这些索引,以便通过索引可用所有表列而无需返回表?
在您发布的示例中,聚集索引似乎已足够,您不需要任何其他索引来避免表查找。您可以通过运行查询并查找“键查找”或“rid查找”操作来验证此操作。

把所有列都放入聚集索引中,以给SQL Server一个“提示”,让它知道如何组织数据,这样做有意义吗?数据有一定的层次结构,我希望SQL Server根据这个层次结构进行组织,因为这是访问数据的方式。 - Flipster

1

我认为你需要更好地理解聚集索引和非聚集索引。聚集索引是一种平衡树(B-tree),每个节点包含索引的键列。通常,也是最佳选择,一个列是索引的键列。每行的所有数据都存储在聚集索引的叶级别(即底层)。这就是为什么你不能在聚集索引中包含列;所有列都被定义为包含。

非聚集索引也是一种B-tree结构。每个节点包含索引的键列。非聚集索引的叶级别包含任何包含的列。关键列和包含列之间的区别在于关键列值出现在索引的每个级别上,而包含列仅出现在叶级别上。叶级别还包含聚集索引的键列,用于将索引链接到表数据。

在任何索引中包含的列越多,索引就越大。这可能会降低性能。

因此,对于聚集索引,你不需要在索引中包含所有列,甚至不需要包含许多列作为键。数据已经是索引的一部分。


那么,针对我们的特定情况,您是说数据已经通过聚集索引可用,而NC索引是无关紧要的,应该删除吗? - Flipster
1
如果非聚集索引与聚集索引几乎相同,则NC索引是无关紧要的。然后,NC索引将永远不会被使用,因为聚集索引将是更好的选择,因为它具有所有数据。 - bobs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接