【问题标题】:Why do I have Sort operation with Key Lookup in query plan (SQL Server)为什么我在查询计划(SQL Server)中使用 Key Lookup 进行排序操作
【发布时间】:2020-10-14 10:42:11
【问题描述】:

我们有一个应用程序执行一些我们无法更改的查询,如下所示(我使用 StackOverflow2013 数据库来演示它):

SELECT *
FROM dbo.Posts p
WHERE CHARINDEX(N'Aptana', p.Title) > 0

我们的数据库具有类似的结构 - 行很宽,由许多不同的列组成,包括 nvarchar(smth) 和 nvarchar(max) 数据类型。

这个查询有这个查询计划(就像我们的,聚集索引扫描),很明显:

  |--Clustered Index Scan(OBJECT:([StackOverflow2013].[dbo].[Posts].[PK_Posts_Id] AS [p]), WHERE:(charindex(N'Aptana',[StackOverflow2013].[dbo].[Posts].[Title] as [p].[Title])>(0))) 

actual execution plan with clustered index scan

我们在这个专栏上有一个索引,我在 dbo.Posts (Title) 上创建了一个:

CREATE INDEX myPleasureSort ON dbo.Posts (Title);

我无法更改查询,但我可以创建索引并使用计划指南添加 INDEX HINT。 我不得不说,我们的用户总是使用这种查询来查找几行,可能是 5000 万行中的 100 行,因此非聚集索引扫描应该更快且资源消耗更少。

所以当我尝试这个时:

SELECT *
FROM dbo.Posts p
WHERE CHARINDEX(N'Aptana', p.Title) > 0
OPTION (MAXDOP 1, TABLE HINT(p, INDEX (myPleasureSort)))

结果如下:

|--Nested Loops(Inner Join, OUTER REFERENCES:([p].[Id], [Expr1002]) WITH UNORDERED PREFETCH)
   |--Sort(ORDER BY:([p].[Id] ASC))
   |    |--Index Scan(OBJECT:([StackOverflow2013].[dbo].[Posts].[myPleasureSort] AS [p]),  WHERE:(charindex(N'Aptana',[StackOverflow2013].[dbo].[Posts].[Title] as [p].[Title])>(0)))
   |--Clustered Index Seek(OBJECT:([StackOverflow2013].[dbo].[Posts].[PK_Posts_Id] AS [p]), SEEK:([p].[Id]=[StackOverflow2013].[dbo].[Posts].[Id] as [p].[Id]) LOOKUP ORDERED FORWARD)

actual execution plan with key lookup and sort

这是我的问题。为什么我在 Key Lookup 之前有这个排序操作?我想正因为如此,我获得了巨额的内存补助,但我不希望它在生产中使用。

查询内存授权检测到“ExcessiveGrant”,这可能会影响 可靠性。授权大小:初始 566496 KB,最终 566496 KB,使用 216 知识库。

我找到了这个索引的解决方法:

CREATE INDEX myPleasure ON dbo.Posts (Id, Title);

对于这个查询,我有下一个查询计划:

SELECT *
FROM dbo.Posts p
WHERE CHARINDEX(N'Aptana', p.Title) > 0
OPTION (MAXDOP 1, TABLE HINT(p, INDEX (myPleasure)))

  |--Nested Loops(Inner Join, OUTER REFERENCES:([p].[Id], [Expr1002]) WITH UNORDERED PREFETCH)
       |--Index Scan(OBJECT:([StackOverflow2013].[dbo].[Posts].[myPleasure] AS [p]),  WHERE:(charindex(N'Aptana',[StackOverflow2013].[dbo].[Posts].[Title] as [p].[Title])>(0)) ORDERED FORWARD)
       |--Clustered Index Seek(OBJECT:([StackOverflow2013].[dbo].[Posts].[PK_Posts_Id] AS [p]), SEEK:([p].[Id]=[StackOverflow2013].[dbo].[Posts].[Id] as [p].[Id]) LOOKUP ORDERED FORWARD)

actual execution plan with key lookup without sort

但我更喜欢仅在 nvarchar 列上使用索引,以便有可能将其与 LIKE 'str%' 之类的内容一起使用。

提前谢谢你,请执行我糟糕的英语。

更新:选择@@VERSION:

Microsoft SQL Server 2017 (RTM-CU20) (KB4541283) - 14.0.3294.2 (X64)

更新 2:感谢@MattM,它看起来像我的情况:Why is Sort operation before Nested Loops (Inner Join)?

【问题讨论】:

  • WHERE CHARINDEX(N'Aptana', p.Title) > 0 不是 SARGable,因此索引无济于事。因此,SQL Server 感觉最简单的方法是扫描整个聚集索引(这可能是最快的,因为它将成为覆盖索引)/
  • 谢谢,我明白了。但我希望 SQL Server 扫描该列上的非聚集索引,因为它比聚集索引小得多,而且我确信它只会返回几行
  • 但是您使用的是SELECT *,因此包含每一列的列将(可能)效率更高。否则实例将需要扫描所述非聚集索引,然后执行键查找;这正是你强制索引时它正在做的事情。让数据引擎决定如何获取数据,它知道自己在做什么。
  • 我无法更改查询并且它包含 SELECT *,但是带有键查找的非聚集索引扫描仍然比聚集索引扫描快得多。
  • 可能知道只有几行,但 SQL Server 不知道。如果您希望它能够使用适当的索引,请修复查询;使其成为 SARGable。

标签: sql-server sql-execution-plan


【解决方案1】:

总的来说,SQL Server 确实将您的最大利益放在心上。如果它正在执行聚集索引扫描或使用排序运算符,这可能是因为查询优化器在它必须搜索的时候找到了最好的计划。

内存授予是 SQL Server 确定查询需要完成的内容 - 这包括读取所有数据所需的内存。过多的内存授予意味着 SQL Server 使用的内存远少于优化器认为查询所需的内存。

查询优化器根据要返回的数据的潜在大小来估计此内存量。

(优化器会在您的查询计划中考虑排序运算符。但我怀疑这不是这里的问题。)

我的猜测是您的 SELECT * 语句中的许多列是大型对象—— VARCHAR 和/或 NVARCHAR 数据类型的数据长度为 100+ 或 MAX —— 它们不使用它们的整个长度。

例如,假设[Title] 列是NVARHCAR(255),并且该列中的大多数值的长度都小于 50 个字符。

查询优化器看不到此列中的数据。只是数据类型。它必须假设该列中的任何或所有数据,对于它期望返回的所有行,都可以是完整的 255 个字符。

因此,它将请求足够的内存来容纳此列。 (它可能不会得到它所要求的一切,因为 SQL Server 不是一个完全的白痴。SQL Server 将分配一个修改后的内存授权,但它仍然是相当大的。)

[Title] 列中的大多数数据长度小于 50 个字符时,SQL Server 会抱怨它必须为这个愚蠢的查询清除所有内存,但最终没有无论如何都要使用它!

将任何这些大对象列的数据类型调整为更窄的长度将意味着查询优化器需要更少的内存。

但是,在您的情况下,SELECT * 和非 SARGable WHERE 子句可能仍会使查询运行欠佳。

确实,提高查询性能的唯一方法是重写它,使其成为 SARGable。

例如

SELECT  *
FROM    dbo.Posts AS p
WHERE   p.Title LIKE '%Aptana%' ;

这甚至可以在不需要查询提示的情况下使用您的索引。

【讨论】:

  • 感谢您的详细回答,@MattM。我知道我的 WHERE 子句不是 SARGable 并且 SQL Server 认为它将返回 33% 的行并且更喜欢扫描聚集索引。我也知道它使用估计的行数和行大小来保留内存。但是我不明白为什么当我使用索引提示和 SQL Server 执行键查找时它使用 SORT 运算符。
  • 不确定。此答案表明 SQL Server 按聚集键列排序以优化键查找和嵌套循环连接:stackoverflow.com/questions/45360126/…
猜你喜欢
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多