慢速运行查询,CF 9和MSSQL 2008; 腐败的执行计划?

Mol*_*mby 7 coldfusion sql-server-2008 coldfusion-9

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

问题

在稳定的生产环境中,没有明显的原因,一个查询开始返回大约1,000-1,500毫秒(比通常慢大约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>
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)

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

对我而言,这表明Coldfusion方面存在问题,但重新启动服务,或者确实是机器,什么也没做.

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

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

这些更改中的任何一个"修复"了问题,但在运行原始查询时,性能问题将会返回.

解决方案

在这一点上,我猜测查询的执行计划已经损坏或者其他东西,做了一些谷歌搜索,然后DBCC FREEPROCCACHE针对数据库服务器运行.这立即解决了问题.很好,问题解决了......

问题

从那时起,我已经做了一些研究,并且共识似乎是执行计划"不会被破坏".有一些关于存储过程和参数嗅探发生类似问题的讨论,但我没有在这里使用任何sp.我们正在从一个相当复杂的视图中选择()嵌套连接.这是问题吗?eLearning.v_courseCompletion

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

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

版本

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

J.T*_*.T. 6

使用cfqueryparam时,您正在使用存储过程.如果不使用cfqueryparam,则只需将查询作为"自由文本"批处理查询发送.当您使用cfqueryparam时,您将使用sp_executeSQL()发送您的查询,sp_executeSQL()本身将您的查询体作为参数发送.这允许查询计划缓存.如果它看到相同的查询,它将使用它为该特定计划保存的统计信息.这意味着,如果它运行了一些非常奇怪的数据并且执行查询的想法不好,那么下一次迭代将使用相同的计划,这对于该查询的99%的用例来说是一个"糟糕的计划",但对于那个奇怪的实例来说也许是一个很好的计划.

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

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

StackOverflow很好地证明了这一点,如果一个特定的超级用户在构建查询统计数据之前加载他们的帐户页面上有数百万个徽章和点数,那么对于每个只有几百个左右的用户来说,它会搞乱统计数据积分和一些徽章,使他或她的页面变慢.