避免针对具有不同注释的相同查询的计划缓存膨胀
Posted
技术标签:
【中文标题】避免针对具有不同注释的相同查询的计划缓存膨胀【英文标题】:Avoid plan cache bloat for same query with different comments 【发布时间】:2020-06-06 17:11:54 【问题描述】:启用强制参数化的 SQL Server 2017。
我们的应用服务器通过一次登录连接到数据库,因此在 SQL Profiler 中我们看不到真实用户(GUI 用户)的名称。为了查看有关 SQL 服务器级别的信息,我在 NHibernate 中定义了一个拦截器,它在每个查询的开头放置带有用户名的注释。
这解决了问题,但又产生了另一个问题:SQL Server 认为来自不同用户的相同查询是不同的,即使它已正确参数化并且唯一的区别是注释。将每个计划乘以 150 会使计划缓存膨胀。
问题:我怎样才能让 SQL Server 认为这些注释查询在计划缓存的哈希方面是相同的?
强制参数化选项已经激活,因为一些旧版应用需要它。
将评论放在查询末尾也不起作用。 在我开始尝试其他解决方案(例如创建批处理并将用户信息和实际查询放在 2 个不同的语句中)之前,也许您有一些建议如何格式化注释以避免问题。如果分析器已连接但未在活动监视器或类似工具的“活动昂贵查询”中显示用户名,则批处理黑客将起作用。
以其他方式解决原始问题的 NHibernate 特定技巧也会很棒。
提前感谢您的任何提示!
编辑 2020-06-15:我看到的唯一方法是将批处理与 2 个语句一起使用。第一个将在将 sessionid 映射到当前用户名的小表中进行更新插入。
【问题讨论】:
【参考方案1】:来自documentation:
如果 Transact-SQL 语句在字面上匹配先前执行的 Transact-SQL 语句和缓存计划(每个字符一个字符),则该语句符合现有条件。
评论是查询的一部分。没有办法让只有 cmets 不同的查询使用相同的查询计划。
您可以通过其他方式传输您的附加信息来实现您的目标,例如将您的用户名放在连接Application Name
属性中,例如here。
可以在打开连接之前以编程方式设置应用程序名称,但这也会减少连接池中的连接重用。此应用程序名称属性在 SQL 事件探查器中可见。我已经用它来区分应用程序,这确实是它的预期用途。
据我所知,应用程序名称属性不是查询计划缓存键的一部分,因此具有不同的应用程序名称不应导致查询计划缓存未命中。但我还没有真正检查过。
【讨论】:
感谢您提供的“逐字符”文档参考。至少现在我知道 cmets 是一条死胡同。向连接属性应用程序名称添加后缀是个好主意。我会尝试将它放在 NHibernate 拦截器中,看看它是否可以在没有副作用的情况下工作。 使用应用程序名称将不起作用,因为 connectnio 搅拌是固定的,并且对所有用户都相同。它们共享一个连接池。我发现无法为每个选择添加不同的应用程序名称。会话上下文不能从外部访问,即另一个会话。因此,如果我在活动监视器中看到一个昂贵的查询并且拥有该会话 ID,我将无法通过另一个会话从 SSMS 读取它的上下文值。以上是关于避免针对具有不同注释的相同查询的计划缓存膨胀的主要内容,如果未能解决你的问题,请参考以下文章