連接SQLServer時,因啟用連接池導致孤立事務的原因分析和解決辦法


 

本文出處:http://www.cnblogs.com/wy123/p/6110349.html 

 

之前遇到過這么一種情況:

  連接數據庫的部分Session會出現不定時的阻塞,這種阻塞時長時短,有時候持續較長時間,有時間持續時間較短,沒有什么規律。
   之后分析相關存儲過程和代碼寫法,發現是阻塞源頭的存儲過程中開啟了事務,而應用程序在調用存儲過程發生異常之后沒有進行特別的處理(提交或者回滾),
   那么在執行方法發生異常之后,連接關閉了,但是數據庫中遺留有活動事務(dbcc opentran對應的SessionId是sleeping狀態),於是就產生了阻塞。
   關鍵是活動事務會不定時自己消失,就有點詭異了,這是本文的重點。

 

這種機制跟連接池有關:

當應用程序連接數據庫的時候開啟了連接池,如果應用程序調用了一個開啟了事務操作的存儲過程,
當發生異常的時候,有可能會出現數據庫連接關閉,而存儲過程中的事務既沒有提交,也沒有回滾的情況。
這種情況下就會產生“孤立事務”,也就是說,因為打開事務的數據量連接斷掉了,而事務還處於活動狀態,
實際上開啟連接池的情況下,數據庫連接的關閉,並不是物理上的關閉,而是將數據庫連接返回到連接池。
此時如果沒有外界的干預,包括沒有對這個數據庫連接沒有被重用,或者這個連接沒有物理斷開,或者是沒有重啟應用程序,或者沒有數據庫服務器,這個事務將一直持續下去。
因為活動事務將阻塞其他Session對相關表的排他性訪問,所以就表現為阻塞。

 

 

 

如何判斷是否發生了連接池中的連接重用

首先,一個連接數據庫的過程中,有沒有重用連接池中的連接,在SQL Server中有哪些區別?
以ado.net為例,如果在連接字符串中加入pooling=false;則表示不啟用連接池.
如下,連續執行兩次數據庫訪問,兩次數據庫訪問均在連接字符串中加入了pooling=false;表示不啟用連接池

  

  如下是觀察到profile中的連接動作,注意這里第一次連接斷開之后,有一個logout,第二次訪問數據的時候,有一個login

  

  如果將上述兩個方法中連接字符串中的pooling=false;改為pooling=true;再次連續執行兩個方法,
  會發現第二次連接數據的之前,也即在第一個logout之后,第二次login之前,有一個exec sp_reset_connection的動作。
  exec sp_reset_connection的執行標志着連接從連接池中重用了連接,關於這個動作的作用下面再說

 

 

什么情況下會出現數據庫連接關閉,而事務保持活動狀態 

  首先,參考如下截圖,編寫一個事務性存儲過程,用waitfor delay '00:00:50'的方式延長其事務提交時間,造成連接超時(默認ado.net連接30秒)

 

 

在ado.net中調用這個存儲過程,連接超過30秒之后超時異常,當前執行方法的數據庫連接被關閉,此時並不關閉Visual Studio,模擬應用程序並沒有終止

 

   

  此時查詢數據中的活動事務,發現有一個活動事務,活動事務是上次執行“TimeoutFunction”造成的,
  但此時“TimeoutFunction”發生了異常,數據庫連接被正常關閉,  
  此時,執行這個方法造成的事務還是活動狀態的,如下截圖

 

而此時觀察SessionId = 57的狀態,他是sleeping啊,已經開始呼呼睡大覺了。 

 

如果此時對事務中的表執行查詢操作,會發現是被阻塞的,事實上t1這張表在上述方法執行之前一行數據都沒有

 

 

數據庫連接被重用,第一次連接遺留在數據庫中的事務被回滾

  上面在執行第一個方法之后,並沒有中斷VS的調試狀態,我們繼續執行第二個方法,此時第二個方法會重用第一個方法的數據庫連接,
  至於為什么說他就重用了第一個方法的數據庫連接,一開始就說了。
  當執行exec sp_reset_connection的時候,活動事務被回滾。查詢能夠正常執行。如下截圖

 

  查詢在exec sp_reset_connection之后正常完,因為事務是被回滾的,所以t1表沒有任何數據

 

  上述示例就模擬出來類似這么一種場景,當連接字符串中開啟了連接池之后
  一個方法執行超時連接被關閉之后,其調用的存儲過程中的事務並沒有顯式的提交或者回滾,造成連接關閉而事務繼續保持活動狀態的情況
  比如web程序,一個方法執行完成之后,連接超時但是正常關閉(歸還連接池),事務保持活動狀態,
  此時web服務器並沒有停止下來,也就是應用程序沒有直接關閉,也就是類似於Visual Studio繼續保持DEBUG狀態,
  此時事務一直保持活動狀態知道連接被重用(或者應用程序被關閉),那么其他Session發起對活動事務鎖定的對象,就會發生阻塞。
  問題就出在這里,主觀上無法保證連接池中的那個連接什么時候被重用,也就無法保證活動事務要持續多久,
  如果活動事務一直保持,那么阻塞就一直保持,這顯然是不可接受的

 

關於sp_reset_connection的作用,我就懶得打字了,參考《Microsoft SQL Server企業級平台實踐》第316頁

 

如何避免連接關閉而事務保持活動

  1,本質因為存儲過程執行時間超過了連接的時間導致連接關閉的,那么就可以從分析事務性操作超時的原因入手。

  2,可以在應用程序的代碼中catch的中,進行異常處理時候,保證連接關閉之前,活動事物最終提交或者回滾(作出明確的處理)

  3,關閉連接池,這種情況下,任何被物理關閉的數據庫連接,其發起的未提交事務都將被回滾,但連接池也是為了提高數據庫性能,可行性不大。

  4,從性能上以及連接池機制中分析,以上只能緩解這個問題,而逃不過這個問題,
    實際上,面對連接超時斷開而是事務繼續保持活動狀態這種情況,在存儲過程的事務性操作中加入try catch也是無濟於事的,
    那么就可以使用SET XACT_ABORT ON;命令,確保在任何異常情況下,對事務進行回滾。關於XACT_ABORT可參考聯機叢書。

    

 

總結:本文淺析了啟用數據庫連接池的條件下,在對數據庫訪問異常的情況下,造成孤立事物現象進行了原因進行了分析以及可行的解決方法嘗試。

   從中得到一個教訓,就是在對數據訪問異常處理的時候,應用程序中一定要確保連接與事物的同步釋放。同時,對事務處理的時候,存儲過程中一定要做到嚴謹的事務控制和異常處理機制。

   確保在異常情況下,事務能夠直接回滾,避免引起類似的阻塞。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM