Ado.net中SQLServer資料庫連線池(轉)

iSQlServer發表於2010-08-27

關於資料庫連線池,MSDN有如下文字:

連線到資料庫伺服器通常由幾個需要很長時間的步驟組成。 必須建立物理通道(例如套接字或命名管道),必須與伺服器進行初次握手,必須分析連線字串資訊,必須由伺服器對連線進行身份驗證,必須執行檢查以便在當前事務中登記,等等。

實際上,大多數應用程式僅使用一個或幾個不同的連線配置。 這意味著在執行應用程式期間,許多相同的連線將反覆地開啟和關閉。 為了使開啟的連線成本最低,ADO.NET 使用稱為連線池的優化方法。

連線池減少新連線需要開啟的次數。 池程式保持物理連線的所有權。 通過為每個給定的連線配置保留一組活動連線來管理連線。 只要使用者在連線上呼叫 Open,池程式就會檢查池中是否有可用的連線。 如果某個池連線可用,會將該連線返回給呼叫者,而不是開啟新連線。 應用程式對該連線呼叫 Close 時,池程式會將連線返回到活動連線池集中,而不是真正關閉連線。 連線返回到池中之後,即可在下一個 Open 呼叫中重複使用。

只有配置相同的連線可以建立池連線。 ADO.NET 同時保留多個池,每個配置一個池。 連線由連線字串以及 Windows 標識(在使用整合的安全性時)分為多個池。 還根據連線是否已在事務中登記來建立池連線。

池連線可以顯著提高應用程式的效能和可縮放性。 預設情況下,ADO.NET 中啟用連線池。除非顯式禁用,否則,連線在應用程式中開啟和關閉時,池程式將對連線進行優化。 還可以提供幾個連線字串修飾符來控制連線池的行為。


池的建立和分配

在初次開啟連線時,將根據完全匹配演算法建立連線池,該演算法將池與連線中的連線字串關聯。 每個連線池都與一個不同的連線字串相關聯。 開啟新連線時,如果連線字串並非與現有池完全匹配,將建立一個新池。 按程式、按應用程式域、按連線字串以及(在使用整合的安全性時)按 Windows 標識來建立池連線。 連線字串還必須是完全匹配的;按不同順序為同一連線提供的關鍵字將分到單獨的池中。

在以下 C# 示例中建立了三個新的 SqlConnection 物件,但是管理時只需要兩個連線池。 注意,根據為 Initial Catalog 分配的值,第一個和第二個連線字串有所不同。

   1: using (SqlConnection connection = new SqlConnection(
   2:   "Integrated Security=SSPI;Initial Catalog=Northwind"))
   3:     {
   4:         connection.Open();
   5:         // Pool A is created.
   6:     }
   7: 
   8: using (SqlConnection connection = new SqlConnection(
   9:   "Integrated Security=SSPI;Initial Catalog=pubs"))
  10:     {
  11:         connection.Open();
  12:         // Pool B is created because the connection strings differ.
  13:     }
  14: 
  15: using (SqlConnection connection = new SqlConnection(
  16:   "Integrated Security=SSPI;Initial Catalog=Northwind"))
  17:     {
  18:         connection.Open();
  19:         // The connection string matches pool A.
  20:     }

如果 MinPoolSize 在連線字串中未指定或指定為零,池中的連線將在一段時間不活動後關閉。 但是,如果指定的 MinPoolSize 大於零,在 AppDomain 被解除安裝並且程式結束之前,連線池不會被破壞。 非活動或空池的維護只需要最少的系統開銷。

注意:

當出現故障轉移等錯誤時,會自動清除池。

新增連線

連線池是為每個唯一的連線字串建立的。 當建立一個池後,將建立多個連線物件並將其新增到該池中,以滿足最小池大小的要求。 連線根據需要新增到池中,但是不能超過指定的最大池大小(預設值為 100)。 連線在關閉或斷開時釋放回池中。

在請求 SqlConnection 物件時,如果存在可用的連線,將從池中獲取該物件。 連線要可用,必須未使用,具有匹配的事務上下文或未與任何事務上下文關聯,並且具有與伺服器的有效連結。

連線池程式通過在連線釋放回池中時重新分配連線,來滿足這些連線請求。 如果已達到最大池大小且不存在可用的連線,則該請求將會排隊。 然後,池程式嘗試重新建立任何連線,直到到達超時時間(預設值為 15 秒)。 如果池程式在連線超時之前無法滿足請求,將引發異常。

警告:
我們強烈建議您在使用完連線後總是將其關閉,以使連線返回到池中。要關閉連線,可以使用 Connection 物件的 CloseDispose 方法,也可以通過在 C# 的 using 語句中或在 Visual Basic 的 Using 語句中開啟所有連線。 不是顯式關閉的連線可能不會新增或返回到池中。

移除連線

如果連線長時間空閒,或池程式檢測到與伺服器的連線已斷開,連線池程式會將該連線從池中移除。 注意,只有在嘗試與伺服器進行通訊之後才能檢測到斷開的連線。 如果發現某連線不再連線到伺服器,則會將其標記為無效。 無效連線只有在關閉或重新建立後,才會從連線池中移除。

如果存在與已消失的伺服器的連線,那麼即使連線池管理程式未檢測到已斷開的連線並將其標記為無效,仍有可能將此連線從池中取出。 這種情況是因為檢查連線是否仍有效的系統開銷將造成與伺服器的另一次往返,從而抵消了池程式的優勢。 發生此情況時,初次嘗試使用該連線將檢測連線是否曾斷開,並引發異常。

清除池

ADO.NET 2.0 引入了清除池的兩種新方法: ClearAllPools 和 ClearPool。 ClearAllPools 清除給定提供程式的連線池,ClearPool 清除與特定連線關聯的連線池。 如果在呼叫時連線正在使用,將進行相應的標記。 連線關閉時,將被丟棄,而不是返回池中。

使用連線字串關鍵字控制連線池

下表列出了 ConnectionString 內連線池值的有效名稱。有關更多資訊,請參見 SQL Server 連線池 (ADO.NET)。

Connection Lifetime

0

當連線被返回到池時,將其建立時間與當前時間作比較,如果時間長度(以秒為單位)超出了由 Connection Lifetime 指定的值,該連線就會被銷燬。這在聚集配置中很有用(用於強制執行執行中的伺服器和剛置於聯機狀態的伺服器之間的負載平衡)。

零 (0) 值將使池連線具有最大的連線超時。

Connection Reset

'true'

確定從池中提取資料庫連線時是否重置資料庫連線。對於 SQL Server 7.0 版,設定為 false 可避免獲取連線時再有一次額外的伺服器往返行程,但須注意此時並未重置連線狀態(如資料庫上下文)。

只要不將 Connection Reset 設定為 false,連線池程式就不會受到 ChangeDatabase 方法的影響。連線在退出相應的連線池以後將被重置,並且伺服器將移回登入時資料庫。不會建立新的連線,也不會重新進行身份驗證。如果將 Connection Reset 設定為 false,則池中可能會產生不同資料庫的連線。

Enlist

'true'

當該值為 true 時,池程式在建立執行緒的當前事務上下文中自動登記連線。可識別的值為 true、false、yes 和 no。

Load Balance Timeout

0

連線被銷燬前在連線池中生存的最短時間(以秒為單位)。

Max Pool Size

100

池中允許的最大連線數。

Min Pool Size

0

池中允許的最小連線數。

Pooling

'true'

當該值為 true 時,系統將從適當的池中提取 SQLConnection 物件,或在需要時建立該物件並將其新增到適當的池中。可識別的值為 true、false、yes 和 no。

 

 

 

從深藍居的部落格上找到的描述:

前幾天同事問我一個問題,一種CS架構的程式,直接把SQL Server作為服務端,每個客戶端直接連線資料庫操作(kay注:S2的cs專案就是這種架構),如果客戶端開啟的數量過多時SQL Server的連線數將會特別高,資料庫端形成效能瓶頸,這種情況下怎麼辦?想了想,造成這種情況的原因是ADO.NET的內部機制造成的。ADO.NET中為了提高效能,所以使用了連線池,這樣每個請求就不必都建立一個連線,然後認證,然後執行SQL,而是從連線池中直接取出連線執行SQL,執行完成後也並不是真正關閉連線,而是將該連線重新放回連線池中。如果有100個客戶端,每個客戶端在使用一段時間後連線池中儲存了10個連線,那麼在這種情況下,即使不在客戶端做任何操作,SQL Server上都有1000個連線,這樣不出效能問題才怪。

既然是連線池的問題,那麼我就針對該問題想到了2個解決辦法:

1.關閉ADO.NET的連線池,每次執行SQL時都是新建一個連線執行,然後關閉。這樣做將使資料查詢有所減慢(每次都建立連線,每次都認證,當然會慢了),不過這個慢是毫秒級的,一般感覺不到的,但是如果一個操作就涉及到幾百個SQL語句的情況可能會明細感覺到減慢。修改方法特別簡單,都不用修改程式碼,在資料庫連結字串中加入Pooling=False;即可。

2.修改架構,這種CS架構除了效能問題外還會出現其他的比如安全上的問題。可以將直接連資料庫的方法改成連線服務,這其中可以使用Remoting、Web服務等,當然現在可以統一用WCF了。這樣做就只有服務程式去連線資料庫,而客戶端只連線服務程式,這樣就不會出現連線池造成的瓶頸。不過這樣做程式碼修改量很大,若真要改還是很痛苦的。

以下是網上找到的一篇介紹ADO.NET連線池的文章,感覺不錯。

連線池允許應用程式從連線池中獲得一個連線並使用這個連線,而不需要為每一個連線請求重新建立一個連線。一旦一個新的連線被建立並且放置在連線池中,應用程式就可以重複使用這個連線而不必實施整個資料庫連線建立過程。

當應用程式請求一個連線時,連線池為該應用程式分配一個連線而不是重新建立一個連線;當應用程式使用完連線後,該連線被歸還給連線池而不是直接釋放。

如何實現連線池

確保你每一次的連線使用相同的連線字串(和連線池相同);只有連線字串相同時連線池才會工作。如果連線字串不相同,應用程式就不會使用連線池而是建立一個新的連線。

優點

使用連線池的最主要的優點是效能。建立一個新的資料庫連線所耗費的時間主要取決於網路的速度以及應用程式和資料庫伺服器的(網路)距離,而且這個過程通常是一個很耗時的過程。而採用資料庫連線池後,資料庫連線請求可以直接通過連線池滿足而不需要為該請求重新連線、認證到資料庫伺服器,這樣就節省了時間。

缺點

資料庫連線池中可能存在著多個沒有被使用的連線一直連線著資料庫(這意味著資源的浪費)。

技巧和提示

1. 當你需要資料庫連線時才去建立連線池,而不是提前建立。一旦你使用完連線立即關閉它,不要等到垃圾收集器來處理它。

2. 在關閉資料庫連線前確保關閉了所有使用者定義的事務。

3. 不要關閉資料庫中所有的連線,至少保證連線池中有一個連線可用。如果記憶體和其他資源是你必須首先考慮的問題,可以關閉所有的連線,然後在下一個請求到來時建立連線池。

連線池FAQ

1. 何時建立連線池?

當第一個連線請求到來時建立連線池;連線池的建立由資料庫連線的連線字元創來決定。每一個連線池都與一個不同的連線字串相關。當一個新的連線請求到來時如果連線字串和連線池使用的字串相同,就從連線池取出一個連線;如果不相同,就新建一個連線池。

2. 何時關閉連線池?

當連線池中的所有連線都已經關閉時關閉連線池。

3. 當連線池中的連線都已經用完,而有新的連線請求到來時會發生什麼?

當連線池已經達到它的最大連線數目時,有新的連線請求到來時,新的連線請求將放置到連線佇列中。當有連線釋放給連線池時,連線池將新釋放的連線分配給在佇列中排隊的連線請求。你可以呼叫close和dispose將連線歸還給連線池。

4. 我應該如何允許連線池?

對於.NET應用程式而言,預設為允許連線池。(這意味著你可以不必為這件事情做任何的事情)當然,如果你可以在SQLConnection物件的連線字串中加進Pooling=true;確保你的應用程式允許連線池的使用。

5. 我應該如何禁止連線池?

ADO.NET預設為允許資料庫連線池,如果你希望禁止連線池,可以使用如下的方式:

1) 使用SQLConnection物件時,往連線字串加入如下內容:Pooling=False;

2) 使用OLEDBConnection物件時,往連線字串加入如下內容:OLE DB Services=-4;

 

 

通過上面的兩篇文章希望大家可以明白什麼是資料庫連線池,什麼時候適用,什麼時候不適用。關於效能測試,我做了一個小例子,大家可以看看:

 

 

   1: string connStringUsePool = "server=.;database=pubs;uid=sa;pwd=123456;pooling=true;connection lifetime=0;min pool size = 1;max pool size=50";
   2: string connStringUnUsePool = "server=.;database=pubs;uid=sa;pwd=123456;pooling=false";
   3: 
   4: private void button1_Click(object sender, EventArgs e)
   5: {
   6:
   7: 
   8:     int count = 50;
   9: 
  10:     DateTime start = DateTime.Now;
  11:     for (int i = 0; i < count; i++)
  12:     {
  13:         using (SqlConnection conn = new SqlConnection(connStringUsePool))
  14:         {
  15:             conn.Open();
  16:             conn.Close();
  17:         }
  18:     }
  19:     DateTime end = DateTime.Now;
  20:     TimeSpan ts = end - start;
  21:     label1.Text = "使用連線池"+ts.Milliseconds.ToString();
  22: 
  23:     start = DateTime.Now;
  24:     for (int i = 0; i < count; i++)
  25:     {
  26:         using (SqlConnection conn = new SqlConnection(connStringUnUsePool))
  27:         {
  28:             conn.Open();
  29:             conn.Close();
  30:         }
  31:     }
  32:     end = DateTime.Now;
  33:     ts = end - start;
  34:     label2.Text = "不使用連線池" + ts.Milliseconds.ToString();
  35: }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/16436858/viewspace-671988/,如需轉載,請註明出處,否則將追究法律責任。

相關文章