2014-10-10 85 views
7

我一直在研究Coldfusion/MS SQL多年,这是我见过的最奇怪的问题之一。问题本身已经得到解决,但我并不真正了解发生了什么;这个问题试图弄清楚可能的原因。慢速查询,CF 9&MSSQL 2008;腐败的执行计划?

的问题

在稳定的生产环境中,没有明显的原因,一个查询开始返回围绕1,000-1,500 MS(比平时慢约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倍,在7,000-16,000毫秒之间返回。这是如何检测到问题的;查询(埋在一个对象方法中)正在从循环体中调用,如果循环迭代超过5或6次请求超时。

对我来说,这表明了Coldfusion方面的一个问题,但重新启动服务,或者实际上机器什么都没做。

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

  • 改变所选择的字段(即select *
  • 卸下表别名(cc
  • 具有内嵌值
  • 卸下任何条件更换或者<cfqueryparam>

这些更改中的任何一个都“固定”了这个问题,但是在运行原始查询时,性能问题会返回。

解决方案

在这一点上我猜查询的执行计划已经损坏或什么的,做some Googling,跑DBCC FREEPROCCACHE对DB服务器。这立即修复了问题。太好了,问题就解决了....

问题

从那以后,虽然,我已经做了一点研究和共识似乎是,执行计划“没有遭到损坏”。有some talksimilar problems与存储过程和parameter sniffing发生,但我没有在这里使用任何sp。我们选择一个相当复杂的视图,但是(eLearning.v_courseCompletion)嵌套连接。这是问题吗?

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

..和地狱是连接循环在CF?!?

版本

  • ColdFusion的9.0.2.282541(64位)
  • 的SQL Server Express 10.50.4297(64位)
  • 两台服务器都赢Server 2008 R2的数据中心(64位)

回答

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上有一个很好的示范,如果一个特定的电力用户将装载他们的帐户页面,数以百万计的查询统计信息之前徽章和点数的建成,它会搞砸了所有其他的统计数据只有几百个点和少量徽章的用户,使他或她的页面变慢。

+0

啊太棒了,这很有道理。了解为什么从一个循环内运行它(甚至一次迭代)会如何影响性能?谢谢 – Molomby 2014-10-10 05:27:31

+0

不,循环听起来不像罪魁祸首。我会责怪循环提供的其他一些情况,例如索引变量或使循环看起来像是责怪的东西。另外,你几乎总是可以避免在循环中运行cfquery。实际上这很有意义。 – 2014-10-10 12:23:38

+0

谢谢你的跟进,但我向你保证,在循环中没有其他东西可以(应该)造成这种情况;上面提到的代码块是直接从我的测试用例中提取出来的(前后加上'getTickCount()')。循环的索引(或项目)变量根本不被查询引用,循环体中没有其他东西。据推测,在循环编译到Java期间,某些事情会发生故障?如果我能重现这个问题,我会深入一点。 – Molomby 2014-10-13 00:23:17