为什么这个索引没有提高查询性能

Why this index doesn't improve query performance

平台:SQL服务器 2012

背景:我有两个相当大的日志表——每个表大约有 60 万条记录,它们正在使用 Pk/Fk 连接。为了便于讨论,我们称它们为 ReallyBigLog1 和 ReallyBigLog2。查询(如下)需要大约 3.5 秒才能到达 运行。 WHERE 子句包括三个不同的值。当被要求帮助改进这个查询时,我立即注意到 WHERE 子句中的项目没有被索引。我沾沾自喜地建议添加索引——假设性能的提高会让我看起来像个英雄。但是,附加指数没有可衡量的影响。

问题:给定下面的查询,为什么索引 StartTime、EndTime 和 DateStamp 对查询时间没有可测量的影响?

查询

SELECT 

    IrreleventField1,
    IrreleventField2,
    IrreleventField3....

    FROM  [dbo].[ReallyBigLog1] AS [T1]

    INNER JOIN [dbo].[ReallyBigLog2] AS [T2] ON [T1].[Id] = [T2].[Id]

    WHERE ([T1].[EndTime] IS NOT NULL) AND ([T1].[StartTime] IS NOT NULL) AND ([T2].[DateStamp] >= '2017-5-16 00:00:00')

索引

CREATE NONCLUSTERED INDEX [ix_RecommendedIndex]
ON [dbo].[ReallyBigLog1]
([StartTime] , [EndTime])

CREATE NONCLUSTERED INDEX [IX_DateStamp]
ON [dbo].[ReallyBigLog2]
([DateStamp])

执行计划

5 SELECT            
    4 Compute Scalar        
        3 Merge Join  / Inner Join Merge:([dbo].[ReallyBigLog1].[Id] [T2]=[dbo].[ReallyBigLog1].[Id] [T1]), Residual:([dbo].[ReallyBigLog2].[Id] as [T2].[Id]=[dbo].[ReallyBigLog1].[Id] as [T1].[Id])  
            1 Clustered Index Scan Predicate:([dbo].[ReallyBigLog1].[StartTime] as [T1].[StartTime] IS NOT NULL AND [dbo].[ReallyBigLog1].[EndTime] as [T1].[EndTime] IS NOT NULL), ORDERED FORWARD [dbo].[ReallyBigLog1].[PK_dbo.ReallyBigLog1] [T1]
            2 Clustered Index Scan Predicate:([dbo].[ReallyBigLog2].[DateStamp] as [T2].[DateStamp]>='2017-05-16 00:00:00.000'), ORDERED FORWARD [dbo].[ReallyBigLog2].[PK_dbo.ReallyBigLog2] [T2]

编辑(表格组成)

SELECT
  (SELECT COUNT(*) FROM ReallyBigLog1 WHERE StartTime IS NULL) as NullStartTime,
  (SELECT COUNT(*) FROM ReallyBigLog1 WHERE EndTime IS NULL) as NullEndTime,
  (SELECT COUNT(*) FROM ReallyBigLog1) as Log1Count,
  (SELECT COUNT(*) FROM ReallyBigLog2 WHERE DateStamp > '2017-5-16 00:00:00') AS DateStampUsage,
  (SELECT COUNT(*) FROM ReallyBigLog2) AS Log2Count

DateStampUsage  Log2Count   NullStartTime   NullEndTime  Log1Count
443038          651929      33748           34144        509545

ix_RecommendedIndex 的帮助非常小,除非你有很多空值。

这里,真正重要的指标是IdsIX_DateStamp。由于您似乎在 WHERE 子句中有很多匹配数据,因此优化器更喜欢集群 table 扫描(合并 Ids)。

一种使其更快的可能性是 IX_DateStamp 上的 CLUSTERED 索引,但它会对其他查询产生性能副作用,应首先在测试环境中强调。

如果您能为 EXPLAIN 提供统计信息,可能有助于更好地诊断。

编辑: 根据提供的统计数据,我看不出如何仅使用索引就能使它更快。有太多数据需要解析(超过两个 table 的一半)。您可能需要在另一个 table 中整合您的数据,或者在二进制级别优化数据(较小的记录大小以加快扫描速度)。

由于您要获取 table 中的大部分行,因此索引必须覆盖(=包含您查询中需要的每一列 table)以帮助您完全没有——而且这种改善可能不会太大。

索引没有真正帮助的原因是您正在读取大部分行,并且您的查询中有 IrreleventFields。由于索引仅包含索引键+聚集键,因此必须使用聚集索引键从table(=聚集索引)中获取其余字段。这称为键查找并且可能非常昂贵,因为必须对从索引中找到的与您的搜索条件匹配的每一行执行此操作。

对于被覆盖的索引,可以将"irrelevant"字段添加到索引的include部分,如果想尝试是否改善情况。

仅拥有日期和时间的索引不会有太大帮助。您还应该有一个涵盖连接条件的索引。例如 ID 列。由于您的查询主要是对 T2 别名的时间戳进行量化,因此我将提供以下索引

table           index
ReallyBigLog2   (DateStamp, ID )
ReallyBigLog1   (id, endTime, StartTime )

原因如下。您正在专门寻找 T2 > 给定日期的交易。所以真正的大日志 2 以此为基础开始。 Then ALSO include the "ID" column for the JOIN basis to log table 1. 这里索引的两个部分都被覆盖了,不需要去数据页进行比较来获取字段。

现在,T1 的列索引。以 ID 开始作为立即找到或未到 T2 table。将 endTime、StartTime 作为索引的一部分,同样,它不必转到原始数据页面来符合 WHERE / JOIN 标准。

完成所有操作后,它会获得记录集,转到这些记录的数据页并提取您需要的其余详细信息。

from
   [dbo].[ReallyBigLog2] AS [T2]
      JOIN [dbo].[ReallyBigLog1] AS [T1]
         ON [T1].[Id] = [T2].[Id]
         AND ([T1].[EndTime] IS NOT NULL) 
         AND ([T1].[StartTime] IS NOT NULL) 
where
   [T2].[DateStamp] >= '2017-5-16 00:00:00'