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

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

文章內容來源於網絡

 

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

Connection PoolADO.Net一個用來提高性能的重要功能。但是對於Connection Pool的機制卻很少有文檔涉及,所以對於Connection Pool的排錯,一直都是一個很棘手的問題。

對於OLEDB以及ODBC 連接池是由Driver決定的。

對於Oracle的數據庫,一般而言,8.0以上的版本都建議使用Oracle提供的ODP.Net

所以此文主要探討一下System.Data,SqlClientConnection Pool

 

1.         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

 

2.         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 對系統的開銷。

 

3.         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)。

 

另外的一點備住:

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中被取出,如果需要將創建或添加到相應的池中。

發佈了13 篇原創文章 · 獲贊 1 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章