Sha*_*owe 4 sql-server parameter
我们的系统大约有 500 个“客户”,它们的记录数量存在极大差异。这是一个(非常简化的)示例查询,它可以根据传递的参数返回 0 - 100000 行。这个查询运行 find 但我相当肯定它会受到参数嗅探的影响,具体取决于哪个参数被缓存。
exec sp_executesql N'
SELECT *
FROM Widgets
WHERE CustomerId=@0
',N'@0 nvarchar(40)',@0=N'bda43162-2d98-4e79-8e81-7056f6df5e51'
Run Code Online (Sandbox Code Playgroud)
如果我修改查询以包含参数作为选择,它似乎会为每个客户缓存查询。
exec sp_executesql N'
SELECT ''bda43162-2d98-4e79-8e81-7056f6df5e51'', *
FROM Widgets
WHERE CustomerId=@0
',N'@0 nvarchar(40)',@0=N'bda43162-2d98-4e79-8e81-7056f6df5e51'
Run Code Online (Sandbox Code Playgroud)
由于每个客户都缓存了自己的查询版本,因此性能得到了极大的提高。这种方法有任何副作用吗?
假设:
编辑:我考虑过使用 OPTION (RECOMPILE) 但如果我可以通过这种方法获得编译查询的好处,我不想每次都重新编译。
Bre*_*zar 10
参数嗅探意味着一组参数产生的执行计划与另一组截然不同,如果缓存了错误的计划,则会对性能产生不利影响。
此答案基于您的简化查询 - 要为您的查询获得准确的建议,您需要发布查询以及由参数嗅探产生的两个不同计划。(我总是宁愿找到准确的根本原因,也不愿对简化的示例进行故障排除,但我必须使用您发布的代码,所以就这样了。)
您的查询中只有一个表(假设 Widgets 不是视图):
SELECT * FROM Widgets WHERE CustomerId=@0
Run Code Online (Sandbox Code Playgroud)
这意味着如果您在 CustomerID 上有一个非聚集索引,一些 CustomerID 值可能会生成一个计划,其中包含非聚集索引查找,然后是键查找,而其他参数将在 Widgets 表中执行聚集索引扫描。
有几种方法可以解决这种情况,我将以通常最安全到最危险的方式列出它们:
对查询使用 OPTION (RECOMPILE)。这确实需要更改代码以将行添加到查询中,但是此查询的每次执行都应获得最合适的计划。风险在于计划执行时 CPU 使用率更高(尽管在像这样的单表、单谓词查询中这通常无关紧要,因为计划生成起来非常简单)。
缓存各种计划。您注意到将查询作为字符串传入将使每个参数缓存其自己的单独计划。虽然这将在今天起作用,但它确实会使计划缓存膨胀(占用更多 SQL Server 的内存)。这里的风险是有人会打开强制参数化,这是一个数据库级别的选项,它将参数化您的所有查询,无论它们是否作为字符串发送,然后您突然又回到解决此问题的方法。
其余的都是有效的解决方案,但不适用于您的单表单谓词查询。我在这里列出它们只是为了后代和清晰:
使用 OPTIMIZE FOR UNKNOWN 查询提示,或者我们喜欢这样称呼它,优化平庸。需要更改查询,并为您提供一个通常足够好的计划。这将避免由于参数嗅探而随机更改查询计划,但风险在于它仍然不是性能最高的计划。
使用具有特定 CustomerID 的 OPTIMIZE FOR 查询提示。这还需要更改代码,并且您将针对您的大客户之一优化查询。这将得到一个非常适合大客户的查询计划,而不适合小客户。小客户性能会下降,但大客户不会削弱系统。风险在于您的客户分布会发生变化,这将不再是整个应用程序的正确计划。
使用查询计划指南。您可以准确获取所需的查询计划,然后将计划指南固定到内存中。这是有关计划指南的在线图书部分。我通常不太喜欢这个,因为如果您的索引发生变化,查询计划将不会利用新索引。如果您的查询发生更改,计划指南将不再有效。突然间,系统可能会表现得很糟糕,人们会忘记之前有计划指南在提供帮助。
使用带有手动逻辑的存储过程。拥有调用不同存储过程的分支,一个用于大客户,一个用于小客户。这仅用于更大、更复杂的查询,这些查询可能在几分钟和几小时之间有变化(或根本没有完成)。
归档时间: |
|
查看次数: |
2993 次 |
最近记录: |