做者:可米小子
出處:http://liuhaorain.cnblogs.com html
在上篇文章《你必須知道的ADO.NET(四) 品味Connection對象》中,我已經強調過,創建一個數據庫鏈接是一件很是耗時(消耗時間)耗力(消耗資源)的事情。之因此會這樣,是由於鏈接到數據庫服務器須要經歷幾個漫長的過程:創建物理通道(例如套接字或命名管道),與服務器進行初次握手,分析鏈接字符串信息,由服務器對鏈接進行身份驗證,運行檢查以便在當前事務中登記等等。咱們先無論爲何會有這樣的機制,存在老是有它的道理。既然新建一條鏈接如此痛苦,那麼爲何不重複利用已有的鏈接呢?數據庫
實際上,ADO.NET已經爲咱們提供了名爲鏈接池的優化方法。鏈接池就是這樣一個容器:它存放了必定數量的與數據庫服務器的物理鏈接。所以,當咱們須要鏈接數據庫服務器的時候,只需去池(容器)中取出一條空閒的鏈接,而不是新建一條鏈接。這樣的話,咱們就能夠大大減小鏈接數據庫的開銷,從而提升了應用程序的性能。windows
PS:原本作了2張圖片來描述鏈接池的,無奈公司裝有監控軟件,不能上傳,因此只能等下次有時間上傳了。安全
須要說明的是,鏈接池是具備類別區分的。也就是說,同一個時刻同一應用程序域能夠有多個不一樣類型的鏈接池。那麼,鏈接池是如何標識區分的?細緻的講,是由進程、應用程序域、鏈接字符串以及windows標識(在使用集成的安全性時)共同組成簽名來標識區分的。但對於同一應用程序域來講,通常只由鏈接字符串來標識區分。當打開一條鏈接時,若是該條鏈接的類型簽名與現有的鏈接池類型不匹配,則建立一個新的鏈接池。反之,則不建立新的鏈接池。服務器
一個典型的建立鏈接的實例:性能
//建立鏈接對象1
using (SqlConnection conn1 =
new SqlConnection( "DataSource=(local);Integrated Security=SSPI;Initial Catalog=Northwind"))
{
conn1.Open();
}
//建立鏈接對象2
using (SqlConnection conn2 =
new SqlConnection( "DataSource=(local);Integrated Security=SSPI;Initial Catalog=pubs"))
{
conn2.Open();
}
//建立鏈接對象3
using (SqlConnection conn3 =
new SqlConnection( "DataSource=(local);Integrated Security=SSPI;Initial Catalog=Northwind"))
{
conn3.Open();
}
上面實例中,我建立了三個SqlConnection對象,可是管理時只須要兩個鏈接池。細心的朋友,可能早已發現conn1與conn3的鏈接字符串相同,因此能夠共享一個鏈接池,而conn2與conn1與conn3不一樣,因此須要建立新的鏈接池。優化
當用戶建立鏈接請求或者說調用Connection對象的Open時,鏈接池管理器首先須要根據鏈接請求的類型簽名找到匹配類型的鏈接池,而後盡力分配一條空閒鏈接。具體狀況以下:ui
無效鏈接,即不能正確鏈接到數據庫服務器的鏈接。對於鏈接池來講,存儲的與數據庫服務器的鏈接的數量是有限的。所以,對於無效鏈接,若是如不及時移除,將會浪費鏈接池的空間。其實你不用擔憂,鏈接池管理器已經很好的爲咱們處理了這些問題。若是鏈接長時間空閒,或檢測到與服務器的鏈接已斷開,鏈接池管理器會將該鏈接從池中移除。spa
當咱們使用完一條鏈接時,應當及時關閉或釋放鏈接,以便鏈接能夠返回池中重複利用。咱們能夠經過Connection對象的Close或Dispose方法,也能夠經過C#的using語句來關閉鏈接。3d
鏈接池的行爲能夠經過鏈接字符串來控制,主要包括四個重要的屬性:
仍是看一個實例來理解鏈接池的屬性吧。代碼以下:
SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
connStr.DataSource = @".\SQLEXPRESS";
connStr.InitialCatalog = "master";
connStr.IntegratedSecurity = true;
connStr.Pooling = true; //開啓鏈接池
connStr.MinPoolSize = 0; //設置最小鏈接數爲0
connStr.MaxPoolSize = 50; //設置最大鏈接數爲50
connStr.ConnectTimeout = 10; //設置超時時間爲10秒
using( SqlConnection conn = new SqlConnection(connStr.ConnectionString))
{
;//todo
}
當用戶打開一個鏈接而沒有正確或者及時的關閉時,常常會引起「鏈接泄露」問題。泄露的鏈接,會一直保持打開狀態,直到調用Dispose方法,垃圾回收器(GC)才關閉和釋放鏈接。與ADO不一樣,ADO.NET須要手動的關閉使用完的鏈接。一個重要的誤區是:當鏈接對象超出局部做用域範圍時,就會關閉鏈接。實際上,當超出做用域時,釋放的只是鏈接對象而非鏈接資源。好吧,仍是先看看一個實例吧。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
namespace ConnectionPool
{
class Program
{
static void Main(string[] args)
{
SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
connStr.DataSource = @".\SQLEXPRESS";
connStr.InitialCatalog = "master";
connStr.IntegratedSecurity = true;
connStr.MaxPoolSize = 5;//設置最大鏈接池爲5
connStr.ConnectTimeout = 1;//設置超時時間爲1秒
SqlConnection conn = null;
for (int i = 1; i <= 100; ++i)
{
conn = new SqlConnection(connStr.ConnectionString);
try
{
conn.Open();
Console.WriteLine("Connection{0} is linked",i);
}
catch(Exception ex)
{
Console.WriteLine("\n異常信息:\n{0}",ex.Message);
break;
}
}
Console.Read();
}
}
}
爲了使結果更明顯,我特意將最大鏈接數設置爲5,超時時間爲1秒。運行後,很快獲得如下結果。
從上面的結果咱們很明顯的知道,鏈接出現了異常。咱們已經知道鏈接池的最大鏈接數爲5,當建立第6條鏈接時,因爲鏈接池中鏈接數量已經達到了最大數而且沒有空閒的鏈接,所以須要等待鏈接直到超時。當超過超時時間時,就出現了上述的鏈接異常。所以,我必須再次強調,使用完的鏈接應當儘快的正確的關閉和釋放。
第一步:打開MSSMS管理器,單擊「活動監視器」圖標。
第二步:在打開活動監視器視圖中,單擊「進程」選項卡。
第三步:運行 #4 鏈接池異常與處理方法 中的例子,則能夠看到打開的5條鏈接,以下圖所示。
一樣,經過執行系統存儲過程sp_who,咱們也能夠監視鏈接狀態。
exec sp_who
可獲得如下結果:
用好鏈接池將會大大提升應用程序的性能。相反,若是使用不當的話,則百害而無一益。通常來講,應當遵循如下原則:
提示:池碎片是許多 Web 應用程序中的一個常見問題,應用程序可能會建立大量在進程退出後纔會釋放的池。 這樣,將打開大量的鏈接,佔用許多內存,從而致使性能下降。