串連到資料庫伺服器通常由幾個需要很長時間的步驟組成。 必須建立物理通道(例如通訊端或具名管道),必須與伺服器進行初次握手,必須分析連接字串資訊,必須由伺服器對串連進行身分識別驗證,必須運行檢查以便在當前事務中登記,等等。
實際上,大多數應用程式僅使用一個或幾個不同的串連配置。 這意味著在執行應用程式期間,許多相同的串連將反覆地開啟和關閉。 為了使開啟的串連成本最低,ADO.NET 使用稱為串連池的最佳化方法。
串連池減少新串連需要開啟的次數。 池進程保持物理串連的所有權。 通過為每個給定的串連配置保留一組活動串連來管理串連。 只要使用者在串連上調用 Open,池進程就會檢查池中是否有可用的串連。 如果某個池串連可用,會將該串連返回給調用者,而不是開啟新串連。 應用程式對該串連調用 Close 時,池進程會將串連返回到活動串連池集中,而不是真正關閉串連。 串連返回到池中之後,即可在下一個 Open 調用中重複使用。
只有配置相同的串連可以建立池串連。 ADO.NET 同時保留多個池,每個配置一個池。 串連由連接字串以及 Windows 標識(在使用整合的安全性時)分為多個池。 還根據串連是否已在事務中登記來建立池串連。
池串連可以顯著提高應用程式的效能和可縮放性。 預設情況下,ADO.NET 中啟用串連池。除非顯式禁用,否則,串連在應用程式中開啟和關閉時,池進程將對串連進行最佳化。 還可以提供幾個連接字串修飾符來控制串連池的行為。
池的建立和分配
在初次開啟串連時,將根據完全符合演算法建立串連池,該演算法將池與串連中的連接字串關聯。 每個串連池都與一個不同的連接字串相關聯。 開啟新串連時,如果連接字串並非與現有池完全符合,將建立一個新池。 按進程、按應用程式定義域、按連接字串以及(在使用整合的安全性時)按 Windows 標識來建立池串連。 連接字串還必須是完全符合的;按不同順序為同一串連提供的關鍵字將分到單獨的池中。
在以下 C# 樣本中建立了三個新的 SqlConnection 對象,但是管理時只需要兩個串連池。 注意,根據為 Initial Catalog 分配的值,第一個和第二個連接字串有所不同。
using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=Northwind")) {
connection.Open(); // Pool A is created.
}
using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=pubs")) {
connection.Open(); // Pool B is created because the connection strings differ.
}
using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=Northwind")) {
connection.Open(); // The connection string matches pool A.
}
如果 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
0
當串連被返回到池時,將其建立時間與目前時間作比較,如果時間長度(以秒為單位)超出了由 Connection Lifetime 指定的值,該串連就會被銷毀。這在聚集配置中很有用(用於強制執行運行中的伺服器和剛置於聯機狀態的伺服器之間的Server Load Balancer)。
零 (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: }
原地址:http://developer.51cto.com/art/200906/131898.htm 在此感謝作者!