【IT168 技术文档】21世纪什么最贵?数据库连接。对于以数据库做数据存储基石的应用系统来说,数据库连接是整个系统中最珍贵的资源之一。数据库连接池是为了更有效地利用数据库连接的最重要措施。它对于一个大型的应用系统的性能至关重要,特别是Web应用。ADO.NET Data Provider(以下简称Data Provider)会帮我们管理连接池,所以有人说使用连接池就像游儿童池一样轻松。但并不是说有了Data Provider程序员就万事无忧的,不正确地使用连接池可能导致你的应用在池里淹死。笔者希望通过本文能让读者彻底明白连接池的重要性以及能根据实际情况正确配置连接池的参数,明白实际应用中出现的连接泄漏、“死连接”等异常情况和应对方法,让应用轻松畅游连接池。本文主要介绍ADO.NET 1.1的连接池。
1、什么是连接池
连接池是Data Provider提供的一个机制,使得应用程序使用的连接保存在连接池里而避免每次都要完成建立/关闭连接的完整过程。要理解连接池,先要理解程序里SqlConnection.Open()、SqlConnection.Close()和打开/关闭一个“物理连接”的关系。
Data Provider在收到连接请求时建立连接的完整过程是:先连接池里建立新的连接(即“逻辑连接”),然后建立该“逻辑连接”对应的“物理连接”。建立“逻辑连接”一定伴随着建立“物理连接”。Data Provider关闭一个连接的完整过程是先关闭“逻辑连接”对应的“物理连接”然后销毁“逻辑连接”。销毁“逻辑连接”一定伴随着关闭“物理连接”。SqlConnection.Open()是向Data Provider请求一个连接,Data Provider不一定需要完成建立连接的完整过程,可能只需要从连接池里取出一个可用的连接就可以;SqlConnection.Close()是请求关闭一个连接,Data Provider不一定需要完成关闭连接的完整过程,可能只需要把连接释放回连接池就可以。
下面以一个例子来说明。本文例子都使用Console Application。我们使用操作系统的性能监视器来比较使用连接池与否,数据库的“物理连接”数量的不同。因为性能监视器至少每一秒采集一次数据,为方便观察效果,代码中Open和Close连接后都Sleep一秒。
SqlConnection con=new SqlConnection("server=.;
database=northwind;pooling=false;trusted_connection=true");
for(inti=0;i<10;i++)
{
try
{
con.Open();
System.Threading.Thread.Sleep(1000);
}
catch(Exception e){Console.WriteLine(e.Message);}
finally
{
con.Close();
System.Threading.Thread.Sleep(1000);
}
}
首先,不使用连接池做测试。以上程序中pooing = false表示不使用连接池,程序使用同一个连接串Open & Close了10次连接,使用性能计数器观察SQL Server的“物理连接”数量。从下面的锯齿图可以看出每执行一次con.Open(),SQLServer的“物理连接”数量都增加一,而每执行一次con.Close(),SQLServer的“物理连接”数量都减少一。由于不使用连接池,每次Close连接的时候Data Provider需要把“逻辑连接”和“物理连接”都销毁了,每次Open连接的时候Data Provider需要 建立“逻辑连接”和“物理连接”,锯齿图因此而成。
图1
下面启用连接池再测试一次。把连接串的pooling参数改为true,另外在for循环后加上Console.Read()。
从下图可以看出,从第一次Open到第执行完Console.Read()这段时间内,SQL Server的“物理连接”数量一直保持为1,直到关闭console应用程序的进程后SQL Server的“物理连接”数量才变为0。由于使用了连接池,每次Close连接的时候Data Provider只需把“逻辑连接”释放回连接池,对应的“物理连接”则保持打开的状态。每次Open连接的时候,Data Provider只需从连接池取出一个“逻辑连接”,这样就可以使用其对应“物理连接”而不需建立新的“物理连接”,直线图因此而成。
图2
在ADO.NET 1.1下使用性能计数器观察连接池有关计数器需要注意两个bug。
(1)当应用程序进程关闭后,计数器“SqlClient: Current # pooled connections”和“SqlClient: Current # connection pools”不会减为0,所以每重新运行一次应用程序性能计数器的值在上次的值的基础上一直累加。这是计数器的错误显示,实际上当应用程序关闭后connection pool和pooled connection就减为0。因为关闭应用程序后把性能监视器也关闭,重启应用程序后再重新打开性能监视器就可以看出“SqlClient: Current # pooled connections”和“SqlClient: Current # connection pools”是重新从0开始上升的。
(2)用断点调试的情况下,连接串为"server = .;database = northwind;pooling = true;trusted_connection = true" 的connnection第一次Open的时候“SqlClient: Current # pooled connections”就从0变为2。但根据连接串参数的意义,只Open了一个connection,“SqlClient: Current # pooled connections”应该从0变为1(图2是在没有断点调试的情况下得出的曲线)。这不是计数器显示错误,而是ADO.ENT 1.1本身的bug,因为“User Connections”也随着“SqlClient: Current # pooled connections”从0变为2。
为什么需要连接池?
完成建立/关闭一个连接的完整过程是一个消耗大量资源和时间的一个过程。想象一下一个ASP.NET的系统,里面包含大量访问数据库的代码片,系统有大量的用户同时在使用系统,如果程序每次Open/Close一个连接Data Provider都完成建立/关闭一个连接的完整过程,这样的系统性能肯定让人无法接受。
Data Provider提供连接池并通过连接池实现“物理连接”重复使用而避免频繁地建立和关闭“物理连接”,从而大大提高应用系统的性能。图1描述一个应用的不同Client App使用连接池访问数据库,Data Provider负责建立和管理一个或者多个的连接池,每一个连接池里有一个或者多个连接,池里的连接就是“逻辑连接”。连接池里有N个连接表示该连接池与数据库之间有N个“物理连接”。增加一个连接,连接池与数据库的“物理连接”就增加一个,减少一个连接,连接池与数据库的“物理连接”就减少一个。
图3