【问题标题】:SQL Server query speed is difference when filtering by different columns按不同列过滤时SQL Server查询速度不同
【发布时间】:2013-08-17 21:10:53
【问题描述】:

我有一个数据库,其中只有一个表Logs,其中包含列:

  • Id (PK Clustered, int, not null),
  • ServiceName (nvarchar(255), not null) 和其他一些列,如
  • TaskVariant (nvarchar(1024)),
  • Source (nvarchar(1024))。

我在ServiceName 列上创建了一个索引INDEX_SERVICENAME(非唯一,非集群),其中包括除Id, ServiceName 之外的所有列。

  • 数据库大小约为 4 GB。
  • 表包含大约 3 500 000 行。
  • 表包含大约 1400000 行,Source = N'IpJob'。
  • 表包含大约 240 万行,TaskVariant = N'Ip'。
  • 表包含大约 600 000 行,ServiceName = '1' 和 TaskVariant = N'Ip'。
  • 表包含大约 350 000 行,ServiceName = '1' 和 Source = N'IpJob'。

问题:

我想通过ServiceNameTaskVariantSource 分页从表过滤中选择所有列。我最初的查询是选择由Source 过滤的最后 100 个项目:

SELECT TOP (100) 
[Filter1].[Id] AS [Id], 
[Filter1].[Date] AS [Date], 
[Filter1].[Data] AS [Data], 
[Filter1].[ServiceName] AS [ServiceName], 
[Filter1].[LogLevel] AS [LogLevel], 
[Filter1].[StackTrace] AS [StackTrace], 
[Filter1].[TaskVariant] AS [TaskVariant], 
[Filter1].[Source] AS [Source], 
[Filter1].[Message] AS [Message]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[Date] AS [Date], [Extent1].[Data] AS [Data], [Extent1].[ServiceName] AS [ServiceName], [Extent1].[LogLevel] AS [LogLevel], [Extent1].[StackTrace] AS [StackTrace], [Extent1].[TaskVariant] AS [TaskVariant], [Extent1].[Source] AS [Source], [Extent1].[Message] AS [Message], row_number() OVER (ORDER BY [Extent1].[Id] DESC) AS [row_number]
    FROM [dbo].[Logs] AS [Extent1]
    WHERE (@serviceName = [Extent1].[ServiceName]) AND (@source = [Extent1].[Source])
)  AS [Filter1]
WHERE [Filter1].[row_number] > 0
ORDER BY [Filter1].[Id] DESC

这个查询的运行速度非常快~ 00:00:00 时间。

但是当我尝试按 TaskVariant 过滤时,查询需要大约 00:02:18 分钟(下一个查询)。

SELECT TOP (100) 
[Filter1].[Id] AS [Id], 
[Filter1].[Date] AS [Date], 
[Filter1].[Data] AS [Data], 
[Filter1].[ServiceName] AS [ServiceName], 
[Filter1].[LogLevel] AS [LogLevel], 
[Filter1].[StackTrace] AS [StackTrace], 
[Filter1].[TaskVariant] AS [TaskVariant], 
[Filter1].[Source] AS [Source], 
[Filter1].[Message] AS [Message]
FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[Date] AS [Date], [Extent1].[Data] AS [Data], [Extent1].[ServiceName] AS [ServiceName], [Extent1].[LogLevel] AS [LogLevel], [Extent1].[StackTrace] AS [StackTrace], [Extent1].[TaskVariant] AS [TaskVariant], [Extent1].[Source] AS [Source], [Extent1].[Message] AS [Message], row_number() OVER (ORDER BY [Extent1].[Id] DESC) AS [row_number]
    FROM [dbo].[Logs] AS [Extent1]
    WHERE (@serviceName = [Extent1].[ServiceName]) AND (@taskVariant = [Extent1].[TaskVariant])
)  AS [Filter1]
WHERE [Filter1].[row_number] > 0
ORDER BY [Filter1].[Id] DESC

问题:为什么第二个查询执行这么慢,如何解决这个问题?

非常感谢您的建议。

执行计划1

【问题讨论】:

  • 你的表真的没有聚集索引吗?真的真的很糟糕......它应该有!最好在Id 上。许多操作在聚簇表上比在堆上快得多。请参阅 Kim Tripp 的优秀博文 The Clustered Index Debate Continues...,其中详细解释了这些事情
  • 你能显示两个查询的执行计划吗?
  • 抱歉,列 ID 是主聚集键。我将更新问题以添加此信息。

标签: sql sql-server sql-server-2008


【解决方案1】:

索引的工作方式类似于层次结构/树,其级别对应于其中的列。

因此,如果您的索引位于 ServiceName, TaskVariant,您可以快速过滤到特定的 ServiceNames,因为这是树中的顶层。

但是如果您尝试按TaskVariant 过滤,您现在必须通读整个索引:您不能只跳转到特定的TaskVariant,因为相同的TaskVariant 将在不同的ServiceNames 下.

如果你想过滤TaskVariant,你需要另一个TaskVariant开头的索引。注意:不要只在每一列上创建完整的索引:每个索引都会占用额外的空间,并且需要在 UPDATEs 和 INSERTs 上做更多的工作

【讨论】:

  • 我想过滤 TaskVariant 或 Source 列。对源查询进行过滤时执行速度很快,但在对 TaskVariant 进行过滤时执行速度很慢。问题为什么超过 2 分钟的差异如此之大?
【解决方案2】:

您看到的执行时间差异主要是由于第一个有索引而第二个没有。至于为什么会有这么大的差异,可能是因为有索引,就意味着值是排序的。

由于对值进行了排序,因此您可以使用非常高效的字符串搜索算法,该算法可以使过滤数量级时的操作次数更小。

此外,还有很多其他特征会影响这一点。有可能整个索引都在内存中,而表数据不在,所以第一个查询中的过滤可以全部在内存上完成,从不接触磁盘,而另一个可能不会。

【讨论】:

  • 我添加了第二个查询。仅在第二列中的两个查询之间的差异(在第一种情况下是 Source,在第二种情况下 - TaskVariant)。两个查询都使用索引列 ServiceName。
  • 即使您使用索引也同样适用。第一个可以快速获取行的范围,然后只流式传输结果。第二个它可以快速抓取键的范围,但必须遍历每一行并进行过滤。
  • 这个怎么办?
  • 现在,如果我是对的,当不过滤服务名称但仍查询任务变体时,该列上的查询应该非常慢。如果不是,那么很可能是由于统计。为了使它们都快速运行,您可以在索引中包含 TaskVariant 列(您必须将其设为包含列,因为它 > 900 字节)
  • 是的,您是对的,如果不过滤服务名称,对该列的查询也很慢。但是所有列都已添加为包含...
猜你喜欢
  • 2016-08-28
  • 1970-01-01
  • 2022-01-22
  • 2016-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-10
相关资源
最近更新 更多