天天看點

ADO.NET中SQL Server資料庫連接配接池

實際上,大多數應用程式僅使用一個或幾個不同的連接配接配置。 這意味着在執行應用程式期間,許多相同的連接配接将反複地打開和關閉。 為了使打開的連接配接成本最低,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 對象的 Close 或 Dispose 方法,也可以通過在 C# 的 using 語句中或在 Visual Basic 的 Using 語句中打開所有連接配接。 不是顯式關閉的連接配接可能不會添加或傳回到池中。

移除連接配接

如果連接配接長時間空閑,或池程序檢測到與伺服器的連接配接已斷開,連接配接池程序會将該連接配接從池中移除。 注意,隻有在嘗試與伺服器進行通信之後才能檢測到斷開的連接配接。 如果發現某連接配接不再連接配接到伺服器,則會将其标記為無效。 無效連接配接隻有在關閉或重建立立後,才會從連接配接池中移除。

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

清除池

ADO.NET 2.0 引入了清除池的兩種新方法: ClearAllPools 和 ClearPool。 ClearAllPools 清除給定提供程式的連接配接池,ClearPool 清除與特定連接配接關聯的連接配接池。 如果在調用時連接配接正在使用,将進行相應的标記。 連接配接關閉時,将被丢棄,而不是傳回池中。

使用連接配接字元串關鍵字控制連接配接池

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

Connection Lifetime

當連接配接被傳回到池時,将其建立時間與目前時間作比較,如果時間長度(以秒為機關)超出了由 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

連接配接被銷毀前在連接配接池中生存的最短時間(以秒為機關)。

Max Pool Size

100

池中允許的最大連接配接數。

Min Pool Size

池中允許的最小連接配接數。

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;

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

第一次運作:

ADO.NET中SQL Server資料庫連接配接池

多次運作後:

ADO.NET中SQL Server資料庫連接配接池

測試按鈕的代碼如下:

  • 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: {      
    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: }