ADO.NET 2.0技术内幕_连接池

3.6  连  接  池

与微软以前的数据访问技术类似,ADO.NET包括对连接池的内置支持。

3.6.1  连接句柄和物理连接

如果正在使用Visual Studio,可以使用Visual Studio调试工具检查对象的一些内部私有属性。例如,编写一些代码来打开一个SqlConnection,并在调用Open方法的地方设置断点。右击代码中的对象,并选择【添加监视】,将该对象添加到【监视】窗口。在【监视】窗口中,展开标有Non-Pubic Members的区域。向下滚动,将会看到一个称为InnerConnection的私有属性。

从结构上讲,InnerConnection属性的内容是一个非常薄的层,位于数据库的物理连接之上。为在这里进行讨论,InnerConnection属性和到该数据库的物理连接是可交换的。在逐步执行代码时,将会看到在打开和关闭连接时,InnerConnection属性的值发生变化。当调用Open方法时,SQL Client .NET数据提供程序将SqlConnection对象关联至该数据库的物理连接,所以可以执行查询并返回结果。

打开和关闭数据库连接的代价非常高。为了帮助节省资源并提高性能,.NET Framework中的.NET数据提供程序在默认情况下均使用连接池。

3.6.2  连接池是什么

连接池是一种在打开数据存储区的连接时提高应用程序性能的机制。在调用SqlConnection对象的Close方法时,SQL Client .NET数据提供程序并不实际关闭内部连接。相反,数据提供程序将该内部连接存储到一个池中,以便在以后再次使用。甚至在SqlConnection对象被处理之后,该内部连接也保留在池中。如果在以后使用相同连接字符串和凭据调用SqlConnection对象的Open方法,将会再次使用同一内部连接与数据库进行通信。

如果希望确认是否真正再次利用了同一内部连接,可以使用.NET Reflection中的功能以可编程方式访问私有InnerConnection属性的内容。以下代码(其需要对System.Reflection命名空间的引用)在Using代码块中打开一个SqlConnection,并存储SqlConnection的InnerConnection属性的值。通过利用Using代码块,在该代码块的末尾隐式处理了SqlConnection。此代码在Using代码块中打开另一个SqlConnection,并存储SqlConnection的InnerConnection属性的值。最后,此代码对比InnerConnection属性的内容,确认它们实际上为同一对象。

Visual Basic

Dim strConn As String = "Data Source=./SQLExpress;Integrated Security=True;"

Dim propInnerConn As PropertyInfo

propInnerConn = GetType(SqlConnection).GetProperty("InnerConnection", _

                            BindingFlags.NonPublic Or BindingFlags.Instance)

Dim objInnerConn1, objInnerConn2 As Object

Using cn As New SqlConnection(strConn)

    cn.Open()

    objInnerConn1 = propInnerConn.GetValue(cn, Nothing)

    cn.Close()

End Using

Using cn As New SqlConnection(strConn)

    cn.Open()

    objInnerConn2 = propInnerConn.GetValue(cn, Nothing)

    cn.Close()

End Using

Console.WriteLine(objInnerConn1 Is objInnerConn2)

Visual C#

string strConn = @"Data Source=./SQLExpress;Integrated Security=True;";

PropertyInfo propInnerConn;

propInnerConn = typeof(SqlConnection).GetProperty("InnerConnection",

                                BindingFlags.NonPublic | BindingFlags.Instance);

object objInnerConn1, objInnerConn2;

using (SqlConnection cn = new SqlConnection(strConn))

{

    cn.Open();

    objInnerConn1 = propInnerConn.GetValue(cn, null);

    cn.Close();

}

using (SqlConnection cn = new SqlConnection(strConn))

{

    cn.Open();

    objInnerConn2 = propInnerConn.GetValue(cn, null);

    cn.Close();

}

Console.WriteLine(objInnerConn1 == objInnerConn2);

两个SqlConnection对象是在不同的Using代码块中创建的,所以其资源将在每个Using代码块的末尾被清除。InnerConnection属性的内容及其所封装的物理连接没有被处理,而是存储在池中,以便在以后被再次利用。

 注意    如果在连接字符串中禁用了连接池(稍后将解释如何禁用),将会看到此内部连接不能被重复利用。

3.6.3  连接池如何改进代码

考虑一个访问SQL Server数据库的典型ASP.NET或WebServices应用程序。客户端应用程序每次需要查询数据库时,就会在服务器端代码中进行往返,以打开SqlConnection来执行查询。在许多此类应用程序中,这一代码以相同凭据一次又一次地连接到相同数据库。理论上,这意味着客户端应用程序每次需要执行查询时,服务器端代码需要执行三个操            作——登录到数据库(需要检查所提供的凭据)、执行查询、然后注销。

连接池可以真正地提高此类应用程序的性能。通过将内部连接存储在池中,并在以后进行重复利用,就不再因为登录数据库以及从中注销而降低性能。对SqlConnection对象的Open和Close方法的调用可以短时间内返回,从而可以提高代码的性能和响应速度(请参见图3.4)。

图3.4  典型ASP.NET或WebServices应用程序中的连接池

3.6.4  启用连接池

在ADO.NET中,连接池是默认启用的。以下代码段将同一SqlConnection对象打开和关闭5次。由于连接池是默认启用的,所以当调用Close方法时,到数据库的实际连接没有被实际关闭,而是将该数据库连接发送到池中,以便在以后重复利用。

Visual Basic

Dim strConn As String

strConn = "Data Source=./SQLExpress;Integrated Security=True;"

Dim cn As New SqlConnection(strConn)

For intCounter As Integer = 1 To 5

    cn.Open()

    cn.Close()

Next intCounter

Visual C#

string strConn;

strConn = @"Data Source=./SQLExpress;Integrated Security=True;";

SqlConnection cn = new SqlConnection(strConn);

for (int intCounter = 1; intCounter <= 5; intCounter++)

{

    cn.Open();

    cn.Close();

}

3.6.5  放入池中的连接何时关闭

在调用Close方法时,SqlClient将该连接返回到池中。假定该连接没有被再次使用,将在大约5分钟后将其从池中删除。但具体在多少秒后删除,并没有确切的数值。其行为取决于所生成的随机数以及创建该池时的相对湿度(relative humidity)。当然,如果在退出应用程序时存在已打开的连接池,那么作为应用程序正常清除过程的一部分,这些连接将被关闭和处理。

3.6.6  禁用连接池

您可能不希望使用连接池。例如,如果正在使用一个直接与数据库进行通信的简单Windows应用程序,那么可能希望禁用连接池。在采用这一架构时,各个客户端应用程序需要自己的连接。在启用连接池时,每个应用程序的连接被放入池中,如果在清除连接池之前重新打开该连接,将重复利用放入池中的连接。所以,如果应用程序频繁重复使用连接,那么在启用连接池的情况下,对SqlConnection.Open的调用将会更快速地返回。但是,这种方法将会导致在任意给定时刻存在许多活动的数据库连接。禁用连接池将会降低任意时刻的活动数据库连接数目,但这样会强制所有对SqlConnection.Open的调用都建立一个新的数据库连接。

如果希望禁用连接池,可以通过向连接字符串中添加Pooling=False,逐个连接地禁用连接池。

幸运的是,在ADO.NET 2.0中不再需要记忆诸如此类的属性。如果存在疑问,可以检查SqlConnectionStringBuilder类的选项。在这个类中可以找到一个Pooling属性,其取值为Boolean类型。默认情况下,此值被设置为True。将该值设置为False将会禁止将该连接放入池中。因此,在调用SqlConnection对象的Close方法时,将会关闭与数据库的实际连接。

 注意    在“偶尔进行连接”的Windows应用程序中,使用连接池可能很有帮助,具体取决于应用程序。如果应用程序希望定期重新连接到数据库,则可以发挥连接池的作用,将与数据库的物理连接保持打开状态,至少暂时如此。如果在从池中删除该物理连接之前,应用程序尝试重新连接到该数据库,则连接池逻辑(pooling logic)将会重新使用与该数据库的物理连接。

3.6.7  有关连接池的常见问题

学习连接池的开发人员越多,出现的问题也会越多。例如,在我听到的连接池相关问题中,最常见的一个是“我怎样才能知道与数据库的物理连接是被真正关闭了,还是仅仅被放入池中了?”,另一个常见问题是“我怎样才能分辨刚刚打开的连接是建立了一个新物理连接,还是重新利用了一个被放在池中的连接?”。

有许多工具可以帮助回答有关连接池的问题。其中一些工具更出色一些。我定期使用SQL Server事件探查器来监视对SQL Server数据库的连接和查询。在ADO.NET的2.0版中,还可以使用Windows中的【性能监视器】。

ADO.NET 2.0中的SQL Client .NET数据提供程序包括用于连接池的性能计数器。现在可以使用诸如【性能监视器】等工具来查看以下数目:放入池中的连接、活动连接、自由连接、活动与非活动连接池及活动与非活动连接池组。还可以搜集有关在每秒内进行连接和断开连接数目的信息。

在某些情况下,维护性能计数器会产生一些性能影响。为此,SQL Client .NET数据提供程序没有维护以下性能计数器:活动或自由连接的数目,或者每秒内放入池中的连接数目或断开连接的数目。可以通过向应用程序的配置文件中添加一项,以启用应用程序中的这些性能计数器。如需有关使用这些性能计数器的详细信息,请参阅MSDN网站上的文章“Using ADO.NET Performance Counters”(使用ADO.NET性能计数器)。

为便于您提出有关连接池的问题,也便于我回答这些问题,我已经开发了一个示例应用程序,如图3.5所示,它可以作为本书示例代码中的一部分进行下载。这一应用程序允许使用如图3.3所示的SqlConnectionStringBuilder/PropertyGrid对话框生成连接字符串。可以很容易地生成新的SqlConnection,打开和关闭现有连接,以及调用ClearPool和ClearAllPools方法。此示例还可以通过【性能监视器】访问SQL Client性能计数器,而不需要以手动方式添加性能计数器。此应用程序的配置文件中包含一项,其能够启用在默认情况下被关闭的性能计数器。在每次创建、打开或关闭SqlConnection或关闭一个或所有连接池时,此示例中的性能计数器都会被更新。

图3.5  研究连接池

3.6.8  ADO.NET如何确定是否使用放入池中的连接

简单地说,假定连接池未被禁用,则SQL Client .NET数据提供程序在您调用SqlConnection对象的Open方法时检查ConnectionString,并确定池中是否存在可用连接。如果存在可用连接,则SQL Client使用该连接。否则,打开一个到数据库的新连接。

实际上还有一些需要说明的内容。设想一个ASP.NET应用程序,其中有多位用户以模拟(impersonation)登录同一数据库,每位用户都使用自己的凭据访问SQL Server数据库。每位用户的连接字符串都是相同的,但他们的凭据有很大不同。由于SQL Client考虑了用户权限,所以用于确定池中有哪些连接可供使用的逻辑要稍微复杂一些。

3.6.9  强制ADO.NET使用新池

在某些时候,可能不再希望从旧连接池中提取连接,而希望建立一个新池。在这种情况下,目标就是采用某种能够对池缓存(pooling)产生影响的方式来修改连接字符串,而对应用程序的剩余部分不产生影响。达到这一目标的最简单方式是在连接字符串的末尾添加一个空格。

3.6.10  手动释放池中缓存的连接

前面的技巧对于ADO.NET 1.x版非常方便,因为API的特性都不能帮助释放缓存在池中的连接。在ADO.NET 2.0中,SqlConnection类中有两个新的静态方法可提供帮助——ClearPool和ClearAllPools。

ClearPool方法取得一个SqlConnection对象,并释放缓存在一个连接池中的所有连接,此连接池与该SqlConnection对象相关。假定有10个SqlConnection对象,均使用同一连接字符串和凭据,并启用了连接池。可以调用所有10个对象的Open方法,然后调用其中三个对象的Close方法。

共有10个到SQL Server数据库的开放式连接。这些连接中的7个连接与7个打开的SqlConnection对象相关联。为了使用性能计数器所使用的术语,我们说这7个连接是“活动的”。剩下的三个连接存在于连接池中。再一次使用性能计数器的术语,它们是“自由”连接。调用ClearPool方法将释放这三个存在于连接池中的自由连接,但不会影响由7个开放式SqlConnection对象所使用的活动连接。

ClearAllPools方法没有参数,它会清除所有自由SqlConnection。

3.6.11  其他连接池选项

现在简单看一下其他常用连接池选项。每个选项都可通过SqlConnectionStringBuilder和连接字符串使用。

1. Connection Reset

如果仔细查看一个SQL事件探查器的轨迹,可能会注意到调用了一个名为sp_reset_connection的存储过程,您可能希望知道是何时调用这个存储过程的,以及为什么要调用它。

简单地重复使用缓存在池中的SqlConnection可能会产生意料之外的结果。由于缺乏更合适的术语,所以可以说:存在某个与已缓存(pooled)连接相关联的“残余”。开发人员不会总是在关闭连接时清除这些混乱,以将连接返回到其原始状态。在调用Close方法时,可能已经存在与该连接相关联的开放式游标或事务,甚至更糟。如果发出一个“USE AdventureWorks”之类的查询,该连接可能被关联到一个不同于该数据库被打开时的数据库。如果通过调用sq_setapprol为连接指定应用程序角色,这些权限仍然被应用到该连接,直到sp_unsetapprole被调用为止,或者直到sp_reset_connection被调用为止,或者直到该连接被真正关闭(而不仅仅是被放入池中)为止。

SQL Client .NET数据提供程序跟踪哪些SqlConnection使用了从连接池中提取的连接。在调用Open方法时,SqlClient没有调用sp_reset_connection存储过程,而是恰好在该连接的第一个操作之前执行这一查询。

如果能够绝对肯定没有在服务器上为池中缓存的连接留下任何“残余”,就可以向连接字符串中添加Connection Reset=False;。这一设置告诉SqlClient,当重新利用缓存在池中的连接时,不需要调用sp_reset_connection存储过程。但是,我建议不要将Connection Reset设置为False。

2. Min Pool Size

从其名称可以看出,Min Pool Size控制池中的最少连接数目。默认情况下,此属性被设置为0。

Min Pool Size属性可以帮助您准备一个连接池。假定将属性设置为5。在打开第一个连接之后,SqlClient将在一后台线程中打开另外4个连接。在该池中总是至少有5个连接。假定代码创建了10个SqlConnection对象,并将它们全部打开。和以前一样,在打开第一个SqlConnection时,SqlClient将在后台线程打开其他4个连接。接下来的4个SqlConnection对象将使用来自池中的剩余连接。剩余的5个SqlConnection对象将建立新连接。

现在,假定关闭了这10个SqlConnection对象中的8个对象。所有这8个连接都将在池中保持存活。当SqlClient清除此连接池之后(假定大约在这些SqlConnection被关闭5分钟之后,并且没有被重新使用),它将确保至少有5个连接(Min Pool Size设置)仍然存在于连接池中。此数目包括目前使用的连接,两种连接仍在使用。所以将有三个其他连接保存在连接池中。其余5个连接将被丢弃。

Min Pool Size的主要缺点是池中将至少有设定数目的连接保持活动。在ASP.NET应用程序中,这些连接的存在时间可能过长。因此,最好使Min Pool Size保持为零。

3. Max Pool Size

Max Pool Size设置的理解稍微简单一些。此设置用作一种限制特性,防止在单个池中打开的连接数目超出指定数值。Max Pool Size的默认值为100。如果达到了连接池中的最大连接数目,那么在下一次尝试打开一个连接时,经过Connect Timerout设置所指定的时间之后会引发一个InvalidOperationException,其说明“超时时间已到。在从池中获取连接之前超时时间已过。出现这种情况可能是因为所有池连接都已被使用,并已达到最大池         大小”。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ADO.NET 2.0技术内幕(高清 中文 带书签 全) 编辑推荐 核心主题全面涵盖,深入剖析个人精髓;示例丰富,同时提供Visual Basic和Visual C#两种版本;独特的常见问题解析方式可使难题迎刃而解。 引导开发人员创建分布式、以数据为中心的应用程序的权威指南。 本书可以使您的专业知识不再停留在基础阶段,它会引领您深入研究ADO.NET 2.0的重要编程主题。本书作为全面的技术内幕参考书,提供了专家指南、实用指令及用Visual c#和Visual Basic编写的代码示例,帮助您深入掌握如何开发Micrsoft Windows和Web数据库应用程序。 本书重要主题:在Microsoft Visual Studio 2005中使用设计时数据访问特性;使用Microsoft .NET Framework 2.0数据提供程序连接数据库;构建连接字符串,启用连接池;执行查询,包括参数化查询和非同步查询,并获取结果;创建DataSet对象以处理脱机数据,并研究常用场景;使用Microsoft SQL Server 2005中新的公共语言运行时特性和XML数据类型;为简单和复杂的更新生成逻辑和使用存储过程;使用ASP.NET 2.0中的新数据访问性设计Web应用程序——包括无代码数据绑定功能。 内容简介 本书介绍了如何用ADO.NET 2.0对独立应用、企业级应用和Web应用程序中的数据进行访问、排序和操作。作者针对如何利用Visual Studio 2005中的新工具和向导,编写、测试并调试数据库应用程序代码,用丰富的示例代码、教程式的风格及特色段落介绍了最佳实践。本书描述了ADO.NET对象模型及其用于Web扩展的XML特性,还涉及Microsoft SQL Server 2000和SQL Server 2005以及其他一些核心主题。 本书学习ADO.NET的理想参考书,也是造就专业编程高手的指南,适用于所有在.NET应用程序中编写数据访问代码的人员。 作者简介 斯赛帕,David Sceppa,SQL Server开发团队的项目经理,也是ADO.NET专家。作为Microsoft Developer Support团队的前技术邻导人,他曾经帮助客户使用Visual Basic和Microsoft Visual FoxPro来构建数据库应用程序。David拥有微软.NET MCSD认证,并有丰富的教学经验。 目录 第Ⅰ部分 Microsoft ADO.NET 2.0入门 第1章 ADO.NET概述 1.1 没有新的对象模型 1.2 ADO.NET对象模型 1.3 常见问题 第2章 用Microsoft Visual Studio 2005生成第一个ADO.NET应用程序 2.1 演示 2.2 不编写代码而生成数据访问窗体 2.3 常见问题 第Ⅱ部分 获取连接:使用.NET数据提供程序 第3章 连接数据库 3.1 生成SqlConnection对象 3.2 打开SqlConnection对象 3.3 关闭SqlConnection对象 3.4 自行清除连接 3.5 连接字符串 3.6 连接池 3.7 将SqlConnection用作起始点 3.8 Visual Studio设计时特性 3.9 SqlConnection对象参考 3.10 常见问题 第4章 数据库查询 4.1 在代码中使用SqlCommand对象 4.2 使用SqlDataReader 4.3 使用参数化查询 4.4 Microsoft Visual Studio设计时特性 4.5 SqlCommand,SqlDataReader和SqlParameter的对象参考 4.6 常见问题 第5章 用SqlDataAdapfer对象获取数据 5.1 何为SqlDataAdapter对象 5.2 创建并使用SqlDataAdapter对象 5.3 Visual Studio 2005设计时特性 5.4 SqlDataAdapter参考 5.5 常见问题 第Ⅲ部分 处理脱机数据——ADO.NET DataSet 第6章 处理DataSet对象 6.1 DataSet类的特性 6.2 使用DataSet对象 6.3 在Visual Studio中处理DataSet对象 6.4 DataSet,DataTable,DataColumn,DataRow,UniqueConstraint和ForeignKeyConstraint类参考 6.5 常见问题 第7章 处理关系数据 7.1 关系数据访问概述 7.2 在代码中使用DataRelation对象 7.3 在Microsoft Visual Studio中创建DataRelation对象 7.4 DataRelation对象参考 7.5 常见问题 第8章 排序、搜索和筛选 8.1 使用DataTable类的搜索和筛选功能 8.2 何为DataView对象 8.3 在代码中使用DataView对象 8.4 在Visual Studio中创建DataView对象 8.5 DataView对象参考 8.6 常见问题 第9章 使用强类型DataSet对象和TableAdapter 9.1 强类型DataSet 9.2 创建强类型DataSet对象 9.3 使用强类型DataSet对象 9.4 何时使用强类型DataSet对象 9.5 TableAdapter简介 9.6 选择自己的方法 9.7 常见问题 第10章 向数据库中提交更新 10.1 使用参数化SqlCommand提交更新 10.2 使用SqlDataAdapter提交更新 10.3 使用SqlDataAdapter对象提交更新 10.4 手动配置SqlDataAdapter对象 10.5 使用SqlCommandBuilder对象生成更新逻辑 10.6 使用Visual Studio【TableAdaptet 配置向导】生成更新逻辑 10.7 返回DataAdapter 10.8 在SqlTransaction中提交更新 10.9 SqlCommandBuildet对象参考 10.10 常见问题 第11章 复杂更新 11.1 在提交更新后刷新一行 11.2 获取新生成的自动增量值 11.3 提交分层更改 11.4 分离和重新集成更改 11.5 处理失败的更新尝试 11.6 使用分布式事务 11.7 批量查询 11.8 SQL批量复制 11.9 DataSet对象与事务 11.10 在处理复杂更新方案时,使用AD0.NET 11.11 常见问题 第12章 使用XML数据 12.1 架起XML和数据访问之间的桥梁 12.2 读写XML数据 12.3 DataSet+XmlDocument=XmlDataDocument 12.4 使用SQL Server 2005的XML特性 12.5 通过SElECT...FOR XML从SQL Server 2000中获取XML数据 12.6 SQL XML.NET数据提供程序 12.7 简单的AD0.NET和XML示例 12.8 常见问题 第Ⅳ部分 用ADO.NET 2.O构造高效的应用程序 第13章 生成基于WindOWS的高效应用程序 13.1 用数据绑定快速生成用户界面 13.2 应用程序设计的考虑事项 第14章 生成高效的Web应用程序 14.1 Web应用程序简介 14.2 连接到数据库 14.3 在ASP.NET 1.0中与数据库进行交互时存在的问题 14.4 数据源控件简介 14.5 缓存往返的数据 14.6 分页 14.7 常见问题 第15章 SQL Server2005公共语言运行时集成 15.1 扩展SQL Server的旧方法——扩展存储过程 15.2 扩展SQL Server的新方法——CLR集成 15.3 使用Microsoft Visual Studio 2005简化SQL CLR代码的生成过程 15.4 SQL CLR方案 15.5 创建SQL CLR用户定义类型 15.6 小结 15.7 常见问题 第Ⅴ部分 附录 附录A 使用其他.NET数据提供程序 附录B 示例和工具
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值