【问题标题】:Explain optimal query for large table with clustered index in SQL Server 2008解释 SQL Server 2008 中具有聚集索引的大表的最佳查询
【发布时间】:2012-12-05 10:50:12
【问题描述】:

我正在处理一个非常大的表(每天大约添加 270 万行),它具有以下结构:

CREATE TABLE [dbo].[Result](
    [ResultDate] [date] NOT NULL,
    [Thing1Id] [int] NOT NULL,
    [Num] [int] NOT NULL,
    [Thing2Id] [int] NOT NULL,
CONSTRAINT [PK_Result] PRIMARY KEY CLUSTERED 
(
    [ResultDate] ASC,
    [Thing1Id] ASC,
    [Num] ASC
))

由于集群主键位于 ResultDate、Thing1Id 和 Num 上,我希望以下查询是最佳的:

SELECT Thing2.* 
FROM dbo.Result
INNER JOIN Thing2 ON Thing2.Id = result.Thing2Id
WHERE 
    ResultDate >= '2012-01-01'
    AND
    ResultDate <= '2012-01-30'
    AND Thing1Id = 23

如您所见,查询在 1 月 12 日查找特定 Thing1 的结果。

但是,执行计划表明通过添加以下索引可以获得巨大的性能提升:

CREATE NONCLUSTERED INDEX [IX_Missing]
ON [dbo].[Result] ([Thing1Id],[ResultDate])
INCLUDE ([Num],[Thing2Id]) 

当然,添加此索引确实会大大提高性能。

有人能解释一下原因吗?就我而言,应该使用聚集主键充分缩小结果范围,添加它会使索引大小变得更大并增加不必要的开销。

我可以对表进行不同的索引以获得更好的性能吗?

(请注意,实际上该表实际上是合并的 2 个表,数据每天从一个表转移到另一个表,数据按月分区)。

【问题讨论】:

  • 我敢打赌,即使只是在dbo.Result(Thing2Id) 上添加一个非聚集索引也会大大加快您的查询速度,因为Thing2Id 是一个外键并在您的INNER JOIN Thing2 ON Thing2.Id = result.Thing2Id 中使用声明...
  • 是的,你是对的。我已经在 Thing2Id 上添加了一个可以加快速度的索引,但我没有在帖子中包含它,因为我对聚集索引更感兴趣。谢谢。
  • 我不明白为什么 Thing2Id 上的索引会对此查询有所帮助。仅仅因为它有一个 FK 而添加一个索引,对这样的大表的伤害可能比它的帮助更大。
  • 很可能,Thing1id 更有选择性,或者 SQL Server 正在做某种索引交集。发布执行计划,我们就知道了。
  • 你能同时发布query execution plans吗?

标签: sql-server performance clustered-index sql-execution-plan


【解决方案1】:

索引基本上按“键”排列您的表格。在你的情况下'thing1ID','ResultDate'。对表进行排序后,访问行比遍历整个表(270 万)要快得多,因为您不知道行可能在哪里。

即2,7,3,8,1,您需要搜索整个表格才能找到数字 1。但如果您有 1、2、3、7、8。您只需检查第一个数字。

但是!对于包含许多涉及“键”的更新/插入的表,速度会变慢,因为您需要在每个条目后对表进行排序。因此,找出最适合您的数据库的方法。

【讨论】:

  • 这正是聚集索引在 ResultDate THEN Thing1Id 上的原因。恐怕您的评论无助于解释为什么需要额外的索引。
【解决方案2】:

PK 不是您的查询的最佳选择,因为您正在对 ResultDate 进行范围搜索。 通过您的查询,您可以将 Thing1Id 23 的搜索范围缩小到大约。 8100 万行仍然很多。

在您的查询中,对 Thing1Id 的搜索固定为 23,因此 Thing1Id 和 ResultDate 上的额外索引将最适合您的查询。

【讨论】:

  • 谢谢维姆。那么这是索引表的最佳方法,还是您能提出更好的方法?
  • 在不确切知道数据库内容和所有查询的情况下,不可能说它是否是索引表的最佳方式。但它是这个查询的一个很好的索引。我只是创建索引,运行一些性能测试并再次分析结果。
【解决方案3】:

query execution plan 会明确地告诉您这里发生了什么,这通常比推测要好得多,但是在这种情况下,我认为有足够的信息可以进行有根据的猜测。

首先,索引的INCLUDE ([Num],[Thing2Id]) 部分仅表示这两列的值在索引以及表本身中重复。它很有用,因为它可以防止 SQL Server 在该索引中执行查找后必须在表中查找这些详细信息(在这种情况下,索引是 covering index)但是通常这种查找非常快,因此不太可能直接负责“大幅”提高性能。我猜下面的索引是 99.9% 一样快。

CREATE NONCLUSTERED INDEX [IX_Missing]
ON [dbo].[Result]
(
    [Thing1Id],
    [ResultDate]
)

在我们继续之前,重要的是要了解 SQL Server 执行此查询有两种方式(为了解释的目的,已大大简化):

  1. 查找在提供的两个日期之间具有ResultDate 的所有行,然后在这些行中查找Thing1Id 为23 的行
  2. 查找所有 Thing1Id 为 23 的行,然后在这些行中查找在两个提供的日期之间具有 ResultDate 的行

根据表中存在的数据,其中一种方法可能明显快于另一种方法,例如,如果表中的大多数行的 Thing1Id 为 23 并且非常很少有人有匹配的ResultDate,那么使用第一种方法可能会更快,因为它会更快地消除更多行。

我们需要了解的另一个重要难题是,由于索引的工作方式,SQL 不能在第二种情况下使用您的聚集索引,因为Thing1Id 列在之后 ResultDate 列(这有点像要求某人使用书中的索引来查找第二个字母为“Q”的所有条目,然后然后要求他们通过并只挑出那些以“S”开头的单词)


因此,我猜测为什么这个索引会提高性能,只是 SQL Server 使用方法 2(首先按 Thing1Id 过滤)比方法 1 更有效。

您应该能够使用查询执行计划来确认这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-04
    • 1970-01-01
    • 2018-05-08
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    相关资源
    最近更新 更多