2014-10-10 84 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