亲爱的[您的名字在这里]!
哦,不,我很遗憾听到这个消息!让我们从一些基础知识开始,让您精打细算。
您遇到的事情称为参数嗅探
这是一个解决怪异问题的出路。这个名字从舌头上滚了下来。就像德语中的松鼠一词一样。
通常是你的朋友。
当查询命中您的服务器时,必须编译一个计划。为了稍后节省时间和资源,将根据该参数将导致您的代码处理并返回的估计行来缓存执行计划。
描绘这种情况的最简单方法是想象一个存储过程,该过程需要从两个不平衡的总体中计数。
例如:
穿着没有受伤的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;
如果查询当前未在执行,则可以使用sp_BlitzCache * 在计划缓存中进行查询。
如果您使用的是SQL Server 2016+,并且已打开查询存储,则可以使用sp_BlitzQueryStore *。
EXEC dbo.sp_BlitzCache @StoredProcName = 'Your Mom';
EXEC dbo.sp_BlitzQueryStore @StoredProcName = 'Your Mom';
这些将帮助您跟踪存储过程的缓存版本。如果只是参数化的代码,则搜索会有些困难。但是,这可能会有所帮助:
EXEC dbo.sp_BlitzCache @QueryFilter = 'statement';
您应该从任何这些中看到非常相似的输出。同样,邀请计划中的蓝色可疑蓝色查询列是您的朋友。
共享计划的最简单方法是使用粘贴计划 *,或将XML转储到pastebin中。要做到这一点,请单击那些邀请蓝色clicky列之一。您的查询计划应出现在新的SSMS选项卡中。
如果您对共享公司代码和查询不满意,可以使用Sentry One的免费Plan Explorer工具来匿名化您的计划。请记住,这会使获得帮助变得更加困难-匿名代码很难阅读和弄清楚。
我们讨论的所有这些工具都应返回查询文本。您无需在这里做任何其他事情。
获取参数要困难一些。如果您使用的是Plan Explorer,则在底部有一个选项卡可以为您列出所有选项。
如果使用的是sp_BlitzCache *,则有一个可单击的列,该列为您提供存储过程的执行语句。
获取表和索引定义
您可以轻松地右键单击SSMS来编写脚本。
如果您想一次获得所有内容,则直接将sp_BlitzIndex *指向表可以提供帮助。
EXEC dbo.sp_BlitzIndex @DatabaseName = 'StackOverflow2010',
@SchemaName = 'dbo',
@TableName = 'Users';
这将为您提供表定义(尽管不是create语句),并为所有索引创建语句。
收集这些信息并将其添加到您的问题中应该可以使人们获得足够的信息来帮助您,或者为您指明正确的方向。
我想自己做!
好吧,酷。我为你感到高兴。你这个疯子。
人们认为他们可以“修正”参数嗅探的方式有很多:
但是这些实际上只是以不同的方式禁用了参数嗅探。这并不是说他们无法解决问题,只是他们并没有真正找到根本原因。
这是因为找到根本原因通常很困难。您必须寻找那些讨厌的“计划质量问题”。
从快速计划与慢速计划开始,寻找类似的差异:
还要寻找使您的代码对参数嗅探敏感的其他运算符:
- 查询
- 排序
- 联接类型
- 内存授予(以及扩展,溢出)
- 线轴
不要被搜寻,扫描,索引碎片或人们四处乱逛的任何杂物所累。
通常,存在一个非常基本的索引问题。有时代码需要重新编写一些。
如果您想了解有关参数嗅探的更多信息:
如果您正在阅读本文,并且认为我错过了链接或有用的工具,请发表评论。我会尽力使它保持最新状态。