【问题标题】:How to improve performance on a clustered index seek如何提高聚集索引查找的性能
【发布时间】:2010-12-30 13:45:37
【问题描述】:

我正在尝试提高运行速度非常慢的查询的性能。经过实际执行计划后;我发现 Clustered Index Seek 占用了 82%。我有什么方法可以提高 Index Seek 的性能吗?

索引:

/****** Object:  Index [IX_Stu]    Script Date: 12/28/2009 11:11:43 ******/
CREATE CLUSTERED INDEX [IX_Stu] ON [dbo].[stu] 
(
 [StuKey] ASC
)WITH (PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [PRIMARY]

表格(为简洁起见省略了一些列):

CREATE TABLE [dbo].[stu](
 [StuCertKey] [int] IDENTITY(1,1) NOT NULL,
 [StuKey] [int] NULL
 CONSTRAINT [PK_Stu] PRIMARY KEY NONCLUSTERED 
(
 [StuCertKey] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

【问题讨论】:

  • 将聚簇索引放在主键以外的东西上对我来说是个坏主意吗?该查询从不使用主键,所以我认为最好在连接最多的列上创建聚集索引(StuKey)
  • 您可以发布查询吗?此外,表中有很多行,查询返回大约多少行?
  • 聚集索引不需要在主键上;但是,这通常表明 PK 本身是多余的。如果您在 PK 上有从未使用过的二级索引,则会损害整体性能。
  • 该表有大约 800 万行。该表中有大约 600 万个不同的 StuKey 值。该查询返回大约 50 行,比我在这里展示的要复杂得多。
  • 如果它不是唯一的,你通常不应该把聚集索引放在它上面。使用常规索引并包含您需要它覆盖的任何列。

标签: sql sql-server query-performance sql-execution-plan


【解决方案1】:

我在这里概括,但是...

在大多数情况下,聚集索引查找是最佳情况。我能想到的提高性能的唯一方法是:

  • 如果可能,更新查询以返回更少的行/列;
  • 对索引进行碎片整理或重建;
  • 跨多个磁盘/服务器对索引进行分区。

如果它只返回 138 行,而且速度很慢……也许它被其他进程阻塞了?您是单独测试,还是其他用户/进程同时在线?或者甚至可能是硬件问题,例如磁盘故障。

【讨论】:

  • 可能有几个其他用户,但不是很多。这也是一个 DW 查询。如果数据库中没有更新,那么不应该有任何锁会阻止我阅读,对吧?
  • 其他一些进程可能正在做一个没有过滤器的SELECT * FROM stu,检索所有800万行;由于 I/O 瓶颈,这肯定会减慢其他查询。不太可能,但有可能。
【解决方案2】:

在使用非聚集索引时会发生聚集索引查找,但不一定是坏的。

考虑以下查询:

SELECT s.StuKey, s.Name, s.Address, s.City, s.State FROM stu s WHERE State='TX'

如果StuKey上只有一个聚集索引,那么Sql Server只有1个选项,它必须扫描整个表寻找State="TX'的行并返回这些行。

如果在 State 上添加非聚集索引

CREATE INDEX IX_Stu_State on Stu (State)

现在 Sql server 有一个新选项。它可以选择使用非聚集索引进行查找,这将产生 State='TX' 的行。但是,为了在 SELECT 中返回剩余的列,它必须通过对每一行执行聚集索引查找来查找这些列。

如果您想减少聚集索引搜索,那么您可以通过在其中包含额外的列来使您的索引“覆盖”。

 CREATE INDEX IX_Stu_State2 on Stu (State) INCLUDE (name, address, city )

该索引现在包含回答上述查询所需的所有列。该查询将执行索引搜索以仅返回 State='TX' 的行,并且可以将其他列从非聚集索引中拉出,因此聚集索引搜索消失。

【讨论】:

    【解决方案3】:

    返回 138 行的聚集索引范围查找不是您的问题。

    从技术上讲,您可以通过缩小聚集索引来提高查找性能:

    两者都可以对范围寻道时间产生相当大的影响,因为它们减少了 IO 和命中物理读取的需要。当然,通常情况下,结果会因大量其他因素而异,例如您投影哪些列(将投影列驱逐到 BLOB 分配单元实际上可能对某些查询产生不利影响)。附带说明一下,通常碎片对这种短距离扫描的影响很小。同样,这取决于。

    但正如我所说,我非常怀疑这是你真正的问题。您只发布了计划的选定部分和您自己的分析结果。真正的根本原因可能完全在别处。

    【讨论】:

      【解决方案4】:

      想法...

      • 为什么 IX_Stu 是集群的?在内部,SQL Server 为非唯一聚集索引添加了一个 4 字节的“唯一标识符”。理由是什么?这也会让你的PK膨胀

      • 您正在运行的实际查询是什么?

      • 最后,为什么 FILLFACTOR 80%?

      编辑:

      • “正常”FILLFACTOR 为 90%,但这只是经验法则

      • 11 连接查询?这很可能是你的问题。您的 JOIN、WHERE 子句等是什么?全文方案是什么?

      【讨论】:

      • 关于您的第一个问题,我将 IX_Stu 设为集群,因为它将用于最多连接。我认为这会提高性能,我在这里混淆了吗?第二个问题,如果可以避免的话,我宁愿不要在互联网上发布查询。如果这对您有帮助的话,它非常大,有 11 个连接... 最后一个问题:我通过管理工作室而不是通过查询创建了索引。显然管理工作室添加了 FILLFACTOR 80%。这可能会导致问题吗?
      【解决方案5】:

      一些一般性建议:当我必须进行查询优化时,我首先会写出我认为的执行计划应该是什么。

      一旦我决定了我认为的执行计划应该是什么,我就会尝试让实际的查询符合这个计划。执行此操作的技术对于每个 DBMS 都不同,并且不一定在一个 DBMS 之间转移,有时甚至在不同版本的 DBMS 之间转移。

      要记住的是,DBMS 一次只能执行一个连接:它从两个初始表开始,连接它们,然后获取该操作的结果并将其连接到下一个表。每一步的目标是尽量减少中间结果集中的行数(更准确地说,是尽量减少产生中间结果必须读取的块数,但这通常意味着最少的行数)。

      【讨论】:

      • 有趣。假设我知道某个连接将我的集合从 600 万行减少到大约 500,000 行。让那个 Join 出现在其他 Join 之前是否会提高性能,或者可能创建一个 CTE 来保存该 Join 的内容,然后继续使用它?
      【解决方案6】:

      如果您对 WHERE 条件进行硬编码,会发生什么情况,如下所示:

      SELECT StuCertKey, StuKey FROM stu 
      WHERE stuKey in (/* list 50 values of StuKey here */)
      

      如果它仍然很慢,则说明您存在某种内部问题。如果它更快,那么索引不是您的瓶颈,它是您正在执行创建 WHERE 过滤器的 JOIN

      请注意,如果有很多大列,尤其是有 BLOB 时,SELECT * 可能会非常慢。

      【讨论】:

        【解决方案7】:

        您是否尝试过对此索引进行一些维护?喜欢碎片整理吗?花费这么多(120.381)似乎真的很奇怪。索引查找是最快的索引操作,不应该花那么长时间。你可以发布查询吗?

        【讨论】:

        • 关于多少碎片整理是太多碎片整理的任何建议?
        【解决方案8】:

        检查索引统计数据。

        重新计算聚集索引统计信息将解决问题。

        就我而言,我正在寻找 30 条 40M 记录的记录。 执行计划说它正在通过聚集索引,但它花了大约 200 毫秒。并且索引没有进行碎片整理。重新计算它的统计数据后,它在 10 毫秒内完成!

        【讨论】:

          【解决方案9】:

          重建索引,并计算统计数据?

          我认为加快速度的唯一其他方法是对表进行分区,这可能会也可能不会。

          【讨论】:

          • 尝试重建索引和统计信息。不过没有运气。接下来我可能正在研究对表进行分区。对于慢速搜索,这是一个很好的解决方案吗?
          猜你喜欢
          • 2012-04-30
          • 2023-03-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-08-27
          • 1970-01-01
          • 2011-11-08
          • 2017-03-06
          相关资源
          最近更新 更多