Ado.net中SQLServer數據庫鏈接池

關於數據庫鏈接池,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 被卸載而且進程結束以前,鏈接池不會被破壞。 非活動或空池的維護只須要最少的系統開銷。優化

注意:ui

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

添加鏈接 

鏈接池是爲每一個惟一的鏈接字符串建立的。 當建立一個池後,將建立多個鏈接對象並將其添加到該池中,以知足最小池大小的要求。 鏈接根據須要添加到池中,可是不能超過指定的最大池大小(默認值爲 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 指定的值,該鏈接就會被銷燬。這在彙集配置中頗有用(用於強制執行運行中的服務器和剛置於聯機狀態的服務器之間的負載平衡)。

零 (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;

 

 

經過上面的兩篇文章但願你們能夠明白什麼是數據庫鏈接池,何時適用,何時不適用。關於性能測試,我作了一個小例子,你們能夠看看:

第一次運行:

image

屢次運行後:

image

測試按鈕的代碼以下:

 

 

 

   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: }
相關文章
相關標籤/搜索