【问题标题】:inconsistent SQL Server Execution Plan Key Lookups不一致的 SQL Server 执行计划键查找
【发布时间】:2011-06-12 18:52:29
【问题描述】:

我在 SQL Server 2005 中有一个大表,我必须通过非聚集键从中选择记录,并且我正在尽我所能优化这个过程。

该表有很多列,我在三个不同的列上添加了非聚集索引。

SELECT * FROM table WHERE Field1 = 10;
SELECT * FROM table WHERE Field2 = 40;
SELECT * FROM table WHERE Field3 = 'A';

Field1 和 Field2 是整数字段,Field3 是 varchar。

当我从 SQL Server 请求这三个查询的估计查询执行计划时,我得到的每个查询基本相同:

SELECT -> Nested Loop -> Index Seek
                      -> Key Lookup

但我发现执行时间非常不一致。具体来说,第二个查询占用了总查询成本的 98%。它的执行计划与其他执行计划相同,只是 Key Lookup 步骤与 Index Seek 相比成本为 100%。在其他两个查询中,它接近 50%。

我知道键查找是不可取的,可以通过向索引添加列来避免查找额外的列。但是,在这种情况下,我希望将表中的所有列都返回给我,因此将它们全部添加到索引中是没有意义的。但是一个索引怎么会导致一个 Key Lookup 操作比另一个 Key Lookup 花费更长的时间呢?

【问题讨论】:

    标签: sql sql-server performance sql-server-2005 indexing


    【解决方案1】:

    成本不一定与时间相关,而是与资源消耗更密切相关。如果不查看查询、了解表结构以及列中包含的数据的构成,很难说出您的情况到底发生了什么。但是与您的条件匹配的记录数可能会在不同的查询计划中产生不同的成本。

    关于表的索引,如果您可以在索引中包含尽可能多的条件列,那么您的情况可能会更好。根据您的数据,您可以通过首先对最具选择性的列(最有可能消除最多数据的列)进行索引来获得更好的提升。 (但不一定是这种情况,特别是如果您与其他表连接、对结果进行排序等)。您可以根据需要或有益的情况添加其他列。您可以在创建索引时使用“include”子句向索引添加选择性较低的“where”列,以减少所需的键查找次数。

    【讨论】:

      【解决方案2】:

      但是一个索引怎么会导致一个 Key 查找操作要花这么多 比另一个 Key Lookup 更长?

      这完全取决于预期密钥长度的当前统计数据。

      查询优化器(QO) 通过查看索引的统计信息来工作。 fieldb 上的索引可能具有 100 的平均基数,而其他字段上的索引的平均基数为 10000(更具体的 100 倍)。因此,它会根据averaged 计划为您提供相对指示。

      要查看详细信息,请始终打开统计信息*,但这实际上只会为您提供特定值的 actual execution time。在某些方面,如果访问变量是随机的,则 QO 在长期内会更准确。

      考虑这两个查询的情况

      SELECT * FROM table WHERE Field2 = 40;
      SELECT * FROM table WHERE Field2 = 42;
      

      假设 42 是一个特殊代码,用于 80% 的所有记录。 40 是仅用于 1 条记录的唯一代码。您不能期望 QO 为每个显示不同的估计行吗?但是,如果您运行查询,除非涉及参数化/计划缓存,否则第二个查询可能会使用 clustered index 来扫描表,而不是执行 80%(昂贵)的书签查找。

      * 打开统计报告

      set statistics io on
      set statistics time on
      

      【讨论】:

        【解决方案3】:

        我见过几个“查询成本(相对于批次)”百分比至少可以说具有误导性的案例。

        最好结合 io 和时间统计来查看实际执行计划,以了解实际发生的情况。

        set statistics io on
        set statistics time on
        
        SELECT * FROM table WHERE Field1 = 10;
        SELECT * FROM table WHERE Field2 = 40;
        SELECT * FROM table WHERE Field3 = 'A';
        

        然后查看每个查询返回的逻辑读取、CPU 时间和运行时间。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-04-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-12
          相关资源
          最近更新 更多