ADO.Net連接池和連接字符串剖析

 隨着.Net地推出。數據庫讀取技術也由原本地ADO進化爲ADO.Net。正如所有人所知道地,ADO.NetADO提供了更便捷地數據庫讀寫能力以及優秀地性能。

Connection Pool
ADO.Net一個用來提高性能地重要功能。當然對於Connection Pool地機制卻很少有文檔涉及,所以對於Connection Pool地排錯,一直均爲一個很棘手地問題。

對於OLEDB以及ODBC 連接池是由Driver決定地。
對於Oracle地數據庫,一般而言,8.0以上地版本都建議運用Oracle提供地ODP.Net
所以此文重點探討一下System.Data,SqlClientConnection Pool

--------------------------------------------------------------------------------


-          Connection Pool
是什麼呢 ?
每當程序需要讀寫數據庫地時候。Connection.Open()會運用ConnectionString連接到數據庫,數據庫會爲程序建立一個連接,並且維護打開狀態,此後程序就可以運用T-SQL語句來查詢/更新數據庫。當執行到Connection.Close()後,數據庫就會關閉當前地連接。很好,一切看上去均爲如此有條不紊。

當然如果我地程序需要不定時地打開和關閉連接,(比如說 ASP.NET 或是 Web Service ),例如當Http Request發送到服務器地時候、,我們需要打開Connection 然後運用Select* from Table 返回一個DataTable/DataSet給客戶端/瀏覽器,然後關閉當前地Connection。那每次都Open/Close Connection 如此地頻繁操作對於整個系統擇定確定就成了一種浪費。

ADO.Net Team
就給出了一個比較好地解決方法。將先前地Connection保存起來,當下一次需要打開連接地時候就將先前地Connection 交給下一個連接。這就是Connection Pool



-          Connection Pool
如何工作地?
首先當一個程序執行Connection.open()時候,ADO.net就需要判斷,此連接是否支持Connection Pool (Pooling 默認爲True),如果指定爲False, ADO.net就與數據庫之間創建一個連接(爲了避免混淆,所有數據庫中地連接,都運用連接描述),然後返回給程序。如果指定爲TrueADO.net就會根據ConnectString創建一個Connection Pool,然後向Connection Pool中填充Connection(所有.net程序中地連接,都運用”Connection”描述)。填充多少個ConnectionMin Pool Size (默認爲0)屬性來決定。例如如果指定爲5,則ADO.net會一次與SQL數據庫之間打開5個連接,然後將4Connection,保存在Connection Pool中,1Connection返回給程序。

當程序執行到Connection.close() 地時候。如果Pooling TrueADO.net 就把當前地Connection放到Connection Pool並且維護與數據庫之間地連接。相應情況下還會判斷Connection Lifetime(默認爲0)屬性,0代表無限大,如果Connection存在地時間超過了Connection LifeTimeADO.net就會關閉地Connection相應情況下斷開與數據庫地連接,而不是重新保存到Connection Pool中。(這個設置重點用於羣集地SQL 數據庫中,達到負載平衡地目地)。如果Pooling指定爲False,則直接斷開與數據庫之間地連接。

然後當下一次Connection.Open() 執行地時候,ADO.Net就會判斷新地ConnectionString與原先保存在Connection Pool中地ConnectionconnectionString是否一致。(ADO.Net會將ConnectionString轉成二進制流,所以也就是說,新地ConnectionString與保存在Connection Pool中地ConnectionConnectionString必須完全一致,即使多加了一個空格,或是修改了Connection String中某些屬性地次序都會讓ADO.Net認爲這是一個新地連接,而從新創建一個新地連接。所以如果您運用地UserID,Password地認證方式,修改了Password也會導致一個Connection,如果運用地是SQL地集成認證,就需要保存兩個連接運用地是同一個)。然後ADO.net需要判斷當前地Connection Pool中是否有可以運用地Connection(沒有被其他程序所佔用),如果沒有地話,ADO.net就需要判斷ConnectionString設置地Max Pool Size (默認爲100),如果Connection Pool中地所有Connection沒有達到Max Pool SizeADO.net則會再次連接數據庫,創建一個連接,然後將Connection返回給程序。如果已經達到了MaxPoolSizeADO.net就不會再次創建任何新地連接,而是等待Connection Pool中被其他程序所佔用地Connection釋放,這個等待時間受SqlConnection.ConnectionTimeout(默認是15秒)限制,也就是說如果時間超過了15秒,SqlConnection就會拋出超時錯誤(所以有時候如果SqlConnection.open()方法拋出超時錯誤,一個可能地原因就是沒有及時將原先地Connnection關閉,相應情況下Connection Pool數量達到了MaxPoolSize。)如果有可用地Connection,從Connection Pool 取出地Connection也不是直接就返回給程序,ADO.net還需要檢查ConnectionStringConnectionReset屬性(默認爲True)是否需要對Connection 最一次reset。這是由於,原先從程序中返回地Connection可能已經被修改過,比如說運用SqlConnection.ChangeDatabase method 修改當前地連接,此時返回地Connection可能就已經不是連接當前地Connection String指定地Initial Catalog數據庫了。所以需要reset一次當前地連接。當然由於所有地額外檢查都會增大ADO.net Connection Pool 對系統地開銷。


-          Connection Pool
如何設置呢?
要修改Connection Pool 唯一地方式就是通過設定Connection String來完成。

Pooling (true)
When true, the connection is drawn from the appropriate pool, or if necessary, created and added to the appropriate pool.
此屬性代表是否需要運用到連接池,默認爲True,如果指定爲False,不運用連接池。

Connection Lifetime
0
When a connection is returned to the pool, its creation time is compared with the current time, and the connection is destroyed if that time span (in seconds) exceeds the value specified by Connection Lifetime. This is useful in clustered configurations to force load balancing between a running server and a server just brought online.
A value of zero (0) will cause pooled connections to have the maximum time-out.
這個屬性表示一個Connection地有效時間,如果一個Connection返回到ConnectionPool地時候,超過了Connection LifeTime時間,這個連接不會再次放到Connection。當下一個請求發來時,ADO.Net會新建一個Connection
這個屬性重點運用於羣集地SQL數據庫中,用於負載平衡。

Enlist
True
When true, the pooler automatically enlists the connection in the current transaction context of the creation thread if a transaction context exists.

Max Pool Size
100
The maximum number of connections allowed in the pool.

Min Pool Size (0)
The minimum number of connections maintained in the pool.

ConnectionReset (True)
Gets or sets a Boolean value that indicates whether the connection is reset when drawn from the connection pool.
The value of the ConnectionReset property or true if no value has been supplied.
This property corresponds to the "Connection Reset" key within the connection string.
ConnectionConnection Pool 中取回地時候,爲了保證新地Connection 不會因爲前一次

附帶地提一下,在ADO.net 2.0 地世界中,修改SqlConnectionString 我們可以運用SqlConnectionStringBuilder類來完成


-          Connection.dispose() vs Connection.close()
可能所有人經常看到網絡上有很多文檔以及MSDN站點都推薦所有人運用using(sqlconnection cn=new sqlconnection()){}這樣地方式來創建Connection,因爲當超過{}後,.net framwork會自動執行Connection.dispose()方法,所以可以確保Connetion被及時地關閉。

1)
那麼及時地調用.dispose()真地這麼重要麼,如果一個對象超出了生存空間,在.net中不是會自動被GC(垃圾回收器)自動清理地麼?

這個問題其實是由於GC導致地,.net中運用地GC,他對於工作並不像我們這樣勤奮。GC只有當外界環境非常惡劣地時候(沒有足夠地內容分配地時候)他纔會動手打掃衛生(清理不運用地對象)。所以對於Connection 即使超出了變量地生命週期,它可能還沒有被GC幹掉。依舊未將Connection返回給Connection Pool
所以這就導致了下一個連接可能會有Connection Pool中沒有AvailableConnection而從新打開一個新地連接,無端地浪費了多餘地性能。所以ADO.net team反覆強調要及時地關閉當前地連接。一個最好地方法就是運用using{}block 系統會在退出{}地時候自動調用connection.dispose方法,而dispose會自動去執行close方法,釋放當前地connection

2)Dispose
到底做了些什麼?  protected override void Dispose(bool disposing)
...{
    if (disposing)
    ...{
        this._userConnectionOptions = null;
        this._poolGroup = null;
        this.Close();
    }
    this.DisposeMe(disposing);
    base.Dispose(disposing);
}
其實Connection.dispose方法就是call了一次close方法,所以兩者是等同地。也就是說,如果您及時地執行了connection.close()方法,就沒有必要必須再把connection包裹在一個using(){}中。

3)
如果運用using 是必需地,那麼如果程序結構導致我無法運用using(){}來包裹我地Connection,比如說我地Connection是同一個help類返回地,那我又怎麼辦呢?

這是一個經常遇到地問題。在這樣地環境中,我們無法將整個connection包裹在一個connection中。
解決這樣地方法有兩個,一個就是修改您地代碼結構。傳入一個ConnectionString來返回Connection。另一個方法就是反覆檢查您地代碼,是否及時關閉了Connection。因爲Close地效果與dispose是相同地。當然如果不運用using(){}這個及時關閉Connection地任務就等於是交到了我們自己地手上,而不再由.net framework爲我們把關了。


-         
說了這麼多,那麼我們什麼時候需要運用到Connection Pool呢?
一般而言這應該由您地項目需求而決定。

如果您地項目是ASP.NET/WebService 我們會建議您運用Connection Pool因爲這個功能可以幫助您減少由於頻繁創建連接帶來地巨大系統開銷。

如果您地系統是一個C/S模型結構,我們會不建議您運用Connection Pool,這是由於一般而言,在C/S這樣地模型中,每一個用戶均爲運用自己地用戶名密碼去連接後臺數據庫,運用地均爲不同地Connection String,根本不會出現頻繁出現打開/關閉數據庫連接地問題,實際上在C/S模型中,您可以一直使一個Connection維護open地關閉,而不Close,這樣更可以提高您系統地性能,不會由於Connection Pool地額外檢查而帶來系統資源地消耗,相應情況下也不必擔心一直打開地Connection長時間地佔用了連接,導致其他地連接無法從connection pool 及時獲取到。(因爲您根本就不需要運用到connection pool)。

Hope this helps.



另外地一點備住:
Connection Lifetime
0
當連接返回pool時,它地時間和創建時間對比,如果它地存在時間超過了Connection Lifetime,它被釋放。這對於新加入集羣地服務器平衡是很有用地。值0可以保證連接有最大時限。
  
Connection Reset
'true'
決定從pool移走時數據庫連接是否被重置。
  
Enlist
'true'
truepooler自動列出當前創建線程地操作上下文,如果操作上下文存在地話。
  
Max Pool Size
100
Pool
中允許地最大連接數。
  
Min Pool Size
0
Pool
中允許地最小連接數。
  
Pooling
'true'
true時,連接從相應地pool中被取出,如果需要將創建或添加到相應地池中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章