运行缓慢的查询,CF 9 和 MSSQL 2008;执行计划损坏?



我多年来一直在研究Coldfusion/MS SQL,这是我见过的最奇怪的问题之一。问题本身已经解决了,但我真的不明白发生了什么;这个问题试图弄清楚可能的原因。

问题

在稳定的生产环境中,没有明显的原因,一个查询在1000-1500毫秒左右开始返回(大约比平时慢10倍)。我能够将其隔离为:

<cfquery datasource="#ds#" name="query">
   select 1
   from eLearning.v_courseCompletion cc
   where 
      cc.memberIncId = <cfqueryparam value="3" cfsqltype="cf_sql_integer"> and
      cc.courseId = <cfqueryparam value="25" cfsqltype="cf_sql_integer"> and 
      cc.currentCourseCompleted = 1
</cfquery>

奇怪的是,当在循环中时,这种行为会加剧,即使只有一次迭代,就像本例中一样:

<cfloop from="1" to="1" index="i">
   <cfquery datasource="#ds#" name="query">
      select 1
      from eLearning.v_courseCompletion cc
      where 
         cc.memberIncId = <cfqueryparam value="3" cfsqltype="cf_sql_integer"> and
         cc.courseId = <cfqueryparam value="25" cfsqltype="cf_sql_integer"> and 
         cc.currentCourseCompleted = 1
   </cfquery>
</cfloop>

这应该和上面完全一样,对吧?循环应该没有效果,但相反,该测试运行速度慢了大约10倍,在7000-16000毫秒之间返回。这就是检测问题的方式;查询(隐藏在对象方法中)是从循环的主体调用的,如果循环迭代超过5或6次,则请求将超时。

对我来说,这表明Coldfusion方面有问题,但重新启动服务或机器没有任何作用。

同时,在隔离后,我注意到对查询本身进行任何更改都会导致性能恢复到预期水平,大约为150-190毫秒。例如:

  • 更改所选字段(即select *
  • 删除表别名(cc
  • 用内联值替换<cfqueryparam>
  • 删除任何条件

这些更改中的任何一个都"修复"了问题,但在运行原始查询时,性能问题会再次出现。

解决方案

在这一点上,我猜测查询的执行计划已经损坏或其他什么,在谷歌上进行了一些搜索,并在DB服务器上运行了DBCC FREEPROCCACHE。这立即解决了问题。太好了,问题解决了。。。。

问题

不过,从那以后,我做了更多的研究,人们的共识似乎是执行计划"不会被破坏"。有一些关于存储过程和参数嗅探出现类似问题的讨论,但我在这里没有使用任何sp。我们从一个相当复杂的视图中进行选择,不过(eLearning.v_courseCompletion)带有嵌套联接。这就是问题所在吗?

基本上,这里到底发生了什么?我该如何阻止它再次发生?

CF中的循环连接到底是什么?!?

版本

  • Coldfusion 9.0.2.282541(64位)
  • SQL Server Express 10.50.4297(64位)
  • 两台服务器都是Win Server 2008 R2数据中心(64位)

使用cfqueryparam时,您在后台使用存储过程。当您不使用cfqueryparam时,您的查询只是作为"自由文本"批处理查询发送。使用cfqueryparam时,将使用sp_executeSQL()发送要执行的查询,sp_executeSL()本身将把查询正文作为参数发送进来。这允许查询计划进行缓存。如果它看到相同的查询,它将使用为该特定计划保存的统计信息。这意味着,如果它使用一些非常奇怪的数据运行,并且对执行查询有一个坏主意,那么接下来的迭代都将使用相同的计划,这对于该查询99%的用例来说是一个"坏计划",但对于那个奇怪的实例来说可能是一个好计划。

使用sp_execute SQL执行的每个查询还返回一个数字句柄,JDBC驱动程序可以使用该句柄来简单地告诉SQL它可以使用哪个计划,基本上是一个快捷方式。这在CFadmin中的DSN设置中被称为"最大池语句"。将其设置为0或1000不会影响您将利用sp_executeSQL的计划缓存这一事实。

http://blogs.msdn.com/b/turgays/archive/2013/09/18/exec-vs-sp-executesql.aspx

StackOverflow很好地证明了这一点,如果一个特定的超级用户在建立查询统计数据之前加载他们的帐户页面,其中包含数百万个徽章和积分,那么其他每个只有几百个左右积分和少量徽章的用户的统计数据都会混乱不堪,从而使页面速度变慢。

最新更新