为什么我的查询突然比昨天慢?

Eri*_*ing 81 performance sql-server execution-plan parameter-sniffing query-performance

[敬礼]

(检查一个)

[ ] Well trained professional, [ ] Casual reader, [ ] Hapless wanderer,
Run Code Online (Sandbox Code Playgroud)

我有一个(检查所有适用的)

[ ] query [ ] stored procedure [ ] database thing maybe  
Run Code Online (Sandbox Code Playgroud)

运行良好(如果适用)

[ ] yesterday [ ] in recent memory [ ] at some point 
Run Code Online (Sandbox Code Playgroud)

但现在突然变慢了。

我已经检查过以确保它没有被阻止,并且它不是某些长时间运行的维护任务、报告或其他带外进程的受害者。

有什么问题,我应该怎么做,我可以提供哪些信息来获得帮助?

[*Insert appropriate closing remarks*]
Run Code Online (Sandbox Code Playgroud)

Eri*_*ing 92

亲爱的[你的名字]!

哦不,我很遗憾听到这个!让我们从一些基础知识开始,让您快速搞定。

你遇到的事情叫做参数嗅探

这是一个解决诡异问题的出路。这个名字从舌头上滚下来。就像德语中的松鼠一样。

而且通常是你的朋友。

当查询访问您的服务器时,必须编译一个计划。为了稍后节省时间和资源,执行计划会根据参数将导致您的代码处理和返回的估计行进行缓存。

描述这种情况的最简单方法是想象一个存储过程,它需要从两个不平衡的群体中计算事物。

例如:

  • 穿着 CrossFit 衬衫但没有受伤的人:零

  • 穿着 CrossFit 衬衫但畏缩时畏缩的人:全部

显然,该代码的一次执行必须比另一次执行更多的工作,并且您想要执行完全不同的工作量的查询计划看起来完全不同。

我反对什么?

这是一个真正难以找到、测试和修复的问题。

  • 很难找到,因为它不会一直发生
  • 很难测试,因为您需要知道哪些参数会导致不同的计划
  • 很难修复,因为有时它需要查询和索引调整
  • 很难修复,因为您可能无法更改查询或索引
  • 很难修复,因为即使您更改查询或索引,它也可能会再次出现

快速修复

有时,您所需要的只是一点清晰度。或者更确切地说,您的计划缓存确实如此。

如果是存储过程

尝试运行EXEC sys.sp_recompile @objname = N'schema.procname'。这将导致程序在下次运行时重新编译新计划。

这不会解决什么问题:

  • 当前运行它的进程。

这不能保证什么:

  • 重新编译后运行的下一个进程将使用一个为您提供良好计划的参数。

您也可以指向sp_recompile一个表或视图,但要预先警告所有涉及该表或视图的代码都将重新编译。这可能会使问题变得更加困难。

如果是参数化查询

你的工作有点困难。您需要跟踪 SQL 句柄。您不想释放整个计划缓存——就像sp_recompile对表或视图使用一样,您可能会触发(哈哈哈)一大堆意想不到的后果。

找出该命令的最简单方法是运行sp_BlitzWho *! 有一个名为“修复参数嗅探”的列,其中包含从缓存中删除单个计划的命令。但是,这与重新编译具有相同的缺点。

这不会解决什么问题:

  • 当前运行它的进程。

这不能保证什么:

  • 重新编译后运行的下一个进程将使用一个为您提供良好计划的参数。

我还需要帮助!

我们将需要以下东西:

  • 如果可能,好的查询计划
  • 糟糕的查询计划
  • 使用的参数
  • 有问题的查询
  • 表和索引定义

获取查询计划和查询

如果查询正在运行,您可以使用sp_BlitzWho * 或sp_WhoIsActive来捕获当前正在执行的查询。

EXEC sp_BlitzWho;

EXEC sp_WhoIsActive @get_plans = 1;
Run Code Online (Sandbox Code Playgroud)

坚果

如果查询当前没有执行,您可以使用sp_BlitzCache *在计划缓存中检查它。

如果您使用的是 SQL Server 2016+,并且打开了查询存储,则可以使用sp_BlitzQueryStore *。

EXEC dbo.sp_BlitzCache @StoredProcName = 'Your Mom';

EXEC dbo.sp_BlitzQueryStore @StoredProcName = 'Your Mom';
Run Code Online (Sandbox Code Playgroud)

这些将帮助您跟踪存储过程的缓存版本。如果它只是参数化代码,那么您的搜索会更困难一些。不过,这可能会有所帮助:

EXEC dbo.sp_BlitzCache @QueryFilter = 'statement';
Run Code Online (Sandbox Code Playgroud)

您应该从其中任何一个中看到非常相似的输出。同样,邀请酷蓝色 clicky 列的查询计划是您的朋友。

坚果

共享计划的最简单方法是使用Paste The Plan *,或将 XML 转储到 pastebin 中。要做到这一点,请单击其中一个邀请的蓝色可点击列。您的查询计划应出现在新的 SSMS 选项卡中。

坚果

如果您对共享公司的代码和查询很敏感,您可以使用Sentry One 的免费计划资源管理器工具来匿名化您的计划。请记住,这使得获得帮助变得更加困难——匿名代码更难以阅读和理解。

我们谈到的所有这些工具都应该返回查询文本。您无需在此处执行任何其他操作。

获取参数有点困难。如果您使用的是Plan Explorer,则底部有一个选项卡为您列出所有这些。

坚果

如果您使用sp_BlitzCache *,则有一个可点击的列,它为您提供存储过程的执行语句。

坚果

获取表和索引定义

您可以轻松地右键单击 SSMS 来编写脚本。

坚果

如果您想一次性获得所有内容,sp_BlitzIndex * 可以帮助您将其直接指向一张桌子。

EXEC dbo.sp_BlitzIndex @DatabaseName = 'StackOverflow2010',
                       @SchemaName = 'dbo',
                       @TableName = 'Users';
Run Code Online (Sandbox Code Playgroud)

这将为您提供表定义(尽管不是创建语句),并为所有索引创建语句。

收集这些信息并将其添加到您的问题中应该能让人们获得足够的信息来提供帮助,或者为您指明正确的方向。

我要自己做!

嗯,很酷。我为你感到高兴。你这个疯子。

人们认为他们“修复”参数嗅探的方式有很多:

但这些实际上只是以不同的方式禁用参数嗅探。这并不是说他们不能解决问题,他们只是没有真正找到根本原因。

那是因为找到根本原因通常有点困难。你必须寻找那些讨厌的“计划质量问题”。

从快速与慢速计划开始,寻找以下差异:

  • 使用的索引
  • 加入顺序
  • 串行与并行

还要寻找使您的代码对参数嗅探敏感的不同运算符:

  • 查找
  • 排序
  • 加盟类型
  • 内存授予(以及扩展,溢出)
  • 线轴

不要太沉迷于搜索与扫描、索引碎片化或任何人们对货物狂热的东西。

通常,有一个非常基本的索引问题。有时代码需要一些重写。

如果您想了解有关参数嗅探的更多信息:

如果您正在阅读本文,并且认为我错过了一个链接或有用的工具,请发表评论。我会尽力保持最新状态。



SQL*_*tor 30

参数嗅探并不是查询性能变化的唯一可能原因。以下任何常见原因都可能显示相同的症状:

  1. 数据分布/数量发生变化,跨越优化器搜索树决策临界点
  2. 索引/文件碎片化
  3. 统计数据已更新/添加/删除或由于数据更改而变得陈旧和误导
  4. Windows 内存利用率已更改
  5. 事务日志已满未截断,导致物理文件重复扩容
  6. 架构更改 - 索引/索引视图/列/约束添加、修改或删除、数据类型更改等。
  7. 跟踪标志设置已更改
  8. 已应用 Windows 更新
  9. 数据库或服务器设置已更改
  10. 服务器 CU 级别已更改
  11. 客户端应用程序会话设置已更改

此列表中的第 6 - 11 项只能在采取了一些明确的行动后才能发生。我猜您打算排除这些,但很多时候遇到挑战的人并不知道其他人进行了更改,在您开始清除计划缓存条目的路径之前,这值得检查。

  • 感谢保罗的编辑。@sp_BlitzErik - 我无意提供有关特定主题的建议,只是为了提高人们对它们存在的认识,并且可能值得一试。这绝不意味着你的伟大职位会被削弱。您深入、专业且幽默地处理了参数嗅探。我很喜欢读它。我只想确保,如果有人访问这篇文章,按照吸引人的标题,他/她会意识到其他潜在原因。恕我直言,它会为您的帖子增加价值,但如果您仍然希望我删除它,请告诉我。 (2认同)

小智 9

只是为了添加到现有答案中以防它们没有帮助,当您的查询在第二天“突然”出现不同时,请检查:

  • 自上次以来,所用表的方案是否发生了变化?对于 SSMS,您可以在对象资源管理器中右键单击服务器并选择Reports ? Standard Reports ? Schema Changes History
  • 项目数量是否急剧增加?当使用的表中有大量数据时,您的查询可能会慢得多。
  • 其他人是否与您同时使用该数据库?也许选择不干扰彼此工作的时间段。
  • 系统统计数据看起来如何?也许服务器运行很热并且正在限制 CPU 或硬盘驱动器空间不足或交换。也许还有另一个硬件问题,例如服务器机房发生火灾或洪水。


pac*_*ely 9

另一种可能性是,您的基础架构团队正在使用 VMware 上的 vMotion 等工具,并且支持您的 SQL 实例的虚拟机正在从主机无缝移动到主机,而 DBA 不知道。

当您的基础设施外包时,这是一个真正的问题......我真的做噩梦了。