理解.NET中的数据库连接池

作者Joydip Kanjilal

摘要:

连接池能在程度上提高数据库访问性能。本文讨论到底何为连接池,它如何提高数据库访问性能,以及如何在.NET中创建连接池并增加或移除连接。

<!--[if !supportEmptyParas]--> <!--[endif]-->

导言

连接数据库是应用程序中耗费大量资源且相对较慢的操作,但它们又是至关紧要的。连接池是已打开的及可重用的数据库连接的一个容器。连接池在所有的数据库连接都关闭时才从内存中释放。使用连接池最基本的好处是提高应用程序的性能及可伸缩性,而其主要缺点是会有一个或多个数据库连接将一直保持打开状态,即使当前不在使用。ADO.NET的Data Providers将默认情况下将使用连接池,如果你不想使用连接池,必须在连接字符串中指定”Polling=false”。连接池中为你提供了空闲的打开的可重用的数据库连接,而不再需要每次在请求数据库数据时新打开一个数据库连接。当数据库连接关闭或释放时,将返回到连接池中保持空闲状态直到新的连接请求到来。如果我们有效地使用连接池,打开和关闭数据库将不再很耗费资源。本文讨论连接池的相关内容以及如何有效的使用连接池来提高应用程序的效率及可伸缩性。

<!--[if !supportEmptyParas]--> <!--[endif]-->

连接池如何工作

连接池中包含打开的可重用的数据库连接。在同一时刻同一应用程序域中可以有多个连接池,但连接池不可以跨应用程序域共享。注意:一个连接池是通过一个唯一的连接字符串来创建。连接池是根据第一次请求数据库连接的连接字符串来创建的,当另外一个不同的连接字符串请求数据库连接时,将创建另一个连接池。因此一个连接字符中对应一个连接池而不是一个数据库对应一个连接池。如以下代码所示

代码1

// 新建一个连接池
SqlConnection sqlConnection = new SqlConnection();
sqlConnection.ConnectionString = 
"Server=localhost;Database=test;User ID=joydip;Password=joydip;Trusted_Connection=False";
sqlConnection.Open();      

代码2

// 因为连接字符串不同,新建另一个连接池
SqlConnection conn = new SqlConnection();
sqlConnection.ConnectionString = 
"Server=localhost;Database=test;User ID=test;Password=test;Trusted_Connection=False";
sqlConnection.Open();   

代码3

// 因为连接字符串与代码1相同,不再创建连接池.
SqlConnection conn = new SqlConnection();
sqlConnection.ConnectionString = 
"Server=localhost;Database=test;User ID=joydip;Password=joydip;Trusted_Connection=False";
sqlConnection.Open();      
<!--[if !supportEmptyParas]--> <!--[endif]-->

<!--[if !supportEmptyParas]--> <!--[endif]-->

当有新的数据库连接请求到来时,连接池中连接进行了响应而不用创建一个新的数据库连接,也就是说数据库连接可以被重用,而不需要重新新建连接。因此这提高了应用程序的效率和可伸缩性。当你在应用程序中关闭一个打开的数据库连接时,该连接返回到连接池中等待重新连接直到等待超时。在这个时间内等待同一数据库相同连接信息的连接请求。如果这个时间内没有连接请求,这个数据库连接将被关闭,并从连接池中移除这个连接实例。

<!--[if !supportEmptyParas]--> <!--[endif]-->

当一个新的连接池创建后,数据库连接被添加到池中,连接池和池中的连接立即可被使用。连接池中将填满连接字个串中指定的最小连接数量的连接。连接池中连接在长时间不活动或超出指定的生存期时将被移除。

<!--[if !supportEmptyParas]--> <!--[endif]-->

连接池由连接池管理器维护。当后续的连接请求到来,连接池管理器在连接池中寻找可用的空闲的连接,如果存在就交给应用程序使用。以下描述了当一个新的连接请求到来时连接管理器如何工作

<!--[if !supportEmptyParas]--> <!--[endif]-->

·         如果有未用连接可用,返回该连接

·         如果池中连接都已用完,创建一个新连接添加到池中

·       如果池中连接已达到最大连接数,请求进入等待队列直到有空闲连接可用

<!--[if !supportEmptyParas]--> <!--[endif]-->

通过连接字符串中传递的参数可以控制连接池。基本的参数包括:

·         Connect Timeout

·         Min Pool Size

·       

·       

<!--[if !supportEmptyParas]--> <!--[endif]-->

为了有效的使用连接池,记住数据库操作完成后马上关闭连接,这样连接才能返回连接池中。

<!--[if !supportEmptyParas]--> <!--[endif]-->

提高连接池性能

我们应该在最晚时刻打开连接并在最早时刻释放连接,即在使用完成后立即释放。数据库连接应该在真正请求数据时才打开,而不应在使用之前就请求连接,这会减少池中可用连接的数量,因此有害于连接池的操作及应用程序性能。数据库连接应使用完成后立即释放,这能促进连接池更好的使用,因为连接可以返回池中被重新使用。以下代码展示如何在应用程序中有效地打开和关闭连接

<!--[if !supportEmptyParas]--> <!--[endif]-->

代码4

SqlConnection sqlConnection = new SqlConnection(connectionString);
try
{
  sqlConnection.Open();
  //Some Code
}
 
finally
{
  sqlConnection.Close();
}

代码4可以使用”using”关键字进一步简化如以下代码所示

代码5

using(SqlConnection sqlConnection = new SqlConnection(connectionString))
{
  sqlConnection.Open();
  //Some Code
}

<!--[if !supportEmptyParas]--> <!--[endif]-->

注:以上代码5中的”using”关键字将隐含地生成try-finally块

<!--[if !supportEmptyParas]--> <!--[endif]-->

<!--[if !supportEmptyParas]--> <!--[endif]-->

<!--[if !supportEmptyParas]--> <!--[endif]-->

以下列举了更好地使用连接池的几个可参考要点

·         在需要使用时才打开连接,并在完成操作后马上关闭

·         在关闭连接时先关闭相关用户定义的事务

·       确保维持连接池中至少有一个打开的连接

·         在使用集成身份验证的情况下避免使用连接池

<!--[if !supportEmptyParas]--> <!--[endif]-->

连接池可以通过以下途径进行监控

·         使用sp_who或sp_who2存储过程

·        使用SQL Server的Profiler

·        使用性能监视器的性能计数器

<!--[if !supportEmptyParas]--> <!--[endif]-->

参考文献

Tuning Up ADO.NET Connection Pooling in ASP.NET Applications

Connection Pooling for the .NET Framework Data Provider for SQL Server

The .NET Connection Pool Lifeguard

ADO.NET Connection Pooling Explained

<!--[if !supportEmptyParas]--> <!--[endif]-->

结语

连接池是数据库连接对象的容器,只要其中存在活动的或打开的连接它维持活动状态。当使用一个连接字符串来请求数据库连接时,将分配一个新的连接池。通过在应用程序中使用相同的连接字符串我们可以提高应用程序的性能和可伸缩性。然而如果我们不正确地使用连接池可能给我们的应用程序带来负效果。MSDN中说“连接是提高应用程序性能的有力工具,但如果使用不当连接池非但不是有益的而且是害的”。本文讨论了连接池的相关内容以及如何有效的使用连接池来提高应用程序的效率及可伸缩性。

<!--[if !supportEmptyParas]--> <!--[endif]-->
ref: