ADO.Net 之 数据库连接池(二)
连接到数据库服务器通常由几个需要很长时间的步骤组成。必须建立物理通道(例如套接字或命名管道),必须与服务器进行初次握手,必须分析连接字符串信息,必须由服务器对连接进行身份验证,必须运行检查以便在当前事务中登记,等等。
实际上,大多数应用程序仅使用一个或几个不同的连接配置。这意味着在执行应用程序期间,许多相同的连接将反复地打开和关闭。为了使打开连接花费的系统开销最小,ADO.NET 使用称为连接池的优化方法。
连接池使新连接必须打开的次数得以减少。池进程保持物理连接的所有权。通过为每个给定的连接配置保留一组活动连接来管理连接。每当用户在连接上调用 Open时,池进程就会查找池中可用的连接。如果某个池连接可用,会将该连接返回给调用者,而不是打开新连接。应用程序在该连接上调用 Close 时,池进程会将连接返回到活动连接池集中,而不是关闭连接。连接返回到池中之后,即可在下一个 Open 调用中重复使用。
只有配置相同的连接可以建立池连接。ADO.NET 同时保留多个池,每种配置各一个。在使用集成的安全性时,连接按照连接字符串以及 Windows 标识分到多个池中。还根据连接是否已在事务中登记来建立池连接。
连接池可以显著提高应用程序的性能和可缩放性。默认情况下,在 ADO.NET 中启用连接池。除非显式禁用,否则,在应用程序中打开和关闭连接时,池进程会对连接进行优化。还可以提供几个连接字符串修饰符来控制连接池的行为。有关更多信息,请参见本主题后面的“使用连接字符串关键字控制连接池”。
池的创建和分配
在以下 C# 示例中创建了三个新的 SqlConnection 对象,但是管理时只需要两个连接池。 注意,根据为 Initial Catalog 分配的值,第一个和第二个连接字符串有所不同。
using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=Northwind")) { connection.Open(); // Pool A is created. } using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=pubs")) { connection.Open(); // Pool B is created because the connection strings differ. } using (SqlConnection connection = new SqlConnection( "Integrated Security=SSPI;Initial Catalog=Northwind")) { connection.Open(); // The connection string matches pool A. }
如果 MinPoolSize 在连接字符串中未指定或指定为零,池中的连接将在一段时间不活动后关闭。但是,如果指定的 MinPoolSize 大于零,在 AppDomain 被卸载并且进程结束之前,连接池不会被破坏。非活动或空池的维护只需要最少的系统开销。
注意:当出现故障转移等错误时,会自动清除池。
添加链接
在请求 SqlConnection 对象时,如果存在可用的连接,将从池中获取该对象。 连接要可用,必须未使用,具有匹配的事务上下文或未与任何事务上下文关联,并且具有与服务器的有效链接。
连接池进程通过在连接释放回池中时重新分配连接,来满足这些连接请求。 如果已达到最大池大小且不存在可用的连接,则该请求将会排队。 然后,池进程尝试重新建立任何连接,直至到达超时时间(默认值为 15 秒)。 如果池进程在连接超时之前无法满足请求,将引发异常。
注意:
[1] 建议在使用完连接时一定要关闭连接,以便连接可以返回池。 要关闭连接,可以使用 Connection 对象的 Close 或 Dispose 方法,也可以通过在 C# 的 using 语句中打开所有连接。 不是显式关闭的连接可能不会添加或返回到池中。
[2] 不要在类的 Finalize 方法中对 Connection、DataReader 或任何其他托管对象调用 Close 或 Dispose。 在终结器中,仅释放类直接拥有的非托管资源。 如果类不拥有任何非托管资源,则不要在类定义中包含 Finalize 方法。
[3] 从连接池中提取连接或连接返回连接池时,服务器不会引发登录和注销事件。 这是因为连接返回连接池时实际上没有关闭。
移除连接
如果存在一个与已消失的服务器的连接,即使连接池进程尚未检测到断开的连接,也可以从池中取出此连接并将连接标记为无效。 这种情况是因为检查连接是否仍有效的系统开销将造成与服务器的另一次往返,从而抵消了池进程的优势。 发生此情况时,初次尝试使用该连接将检测连接是否曾断开,并引发异常。
清除池
ADO.NET 2.0 引入了两种新的方法来清除池:ClearAllPools 和 ClearPool。 ClearAllPools 将清除给定提供程序的连接池,而 ClearPool 将清除与特定连接相关联的连接池。 如果在调用时连接正在使用,将对它们进行相应的标记。 连接关闭时,将被丢弃,而不是返回池中。
事务支持
连接是根据事务上下文来从池中取出并进行分配的。 除非在连接字符串中指定了 Enlist=false,否则连接池将确保连接在 Current 上下文中登记。 如果连接使用登记的 System.Transactions 事务关闭并返回到池中,连接将保留在池中,以便使用相同 System.Transactions 事务对该连接池的下一次请求将返回相同的连接(如果可用)。 如果发出这样的请求,而没有可用的池连接,则会从池的非事务性部分取出一个连接并登记。 如果在池的每个区域都没有可用的连接,则会创建一个新的连接并登记。
当连接关闭时,它将被释放回池中,并根据其事务上下文放入相应的子部分。 因此,即使分布式事务仍然挂起,仍可以关闭该连接而不会生成错误。 这样,您就可以在之后提交或中止分布式事务。
使用连接字符串关键字控制连接池
SqlConnection 对象的 ConnectionString 属性支持连接字符串键/值对,可以用于调整连接池逻辑的行为。
池碎片
1、因为集成安全性产生的池碎片
连接根据连接字符串以及用户标识来建立池连接。 因此,如果使用网站上的基本身份验证或 Windows 身份验证以及集成的安全登录,每个用户将获得一个池。 尽管这样可以提高单个用户的后续数据库请求的性能,但是该用户无法利用其他用户建立的连接。 这样还使每个用户至少产生一个与数据库服务器的连接。 这对特定 Web 应用程序结构会产生副作用,因为开发人员必须衡量安全性和审计要求。
2、因为许多数据库产生的池碎片
许多 Internet 服务提供商在一台服务器上托管多个网站。 他们可能使用单个数据库确认窗体身份验证登录,然后为该用户或用户组打开与特定数据库的连接。 与身份验证数据库的连接将建立池连接,供每个用户使用。 但是,每个数据库的连接存在一个独立的池,这会增加与服务器的连接数。
这也会对应用程序设计产生副作用。 但是,可以通过一个相对简单的方式避免此副作用,而又不会影响连接 SQL Server 时的安全性。 不是为每个用户或组连接独立的数据库,而是连接到服务器上的相同数据库,然后执行 Transact-SQL USE 语句来切换为所需的数据库。 以下代码段演示如何创建与 master 数据库的初始连接,然后切换到 databaseName 字符串变量中指定的所需数据库。
// Assumes that command is a SqlCommand object and that // connectionString connects to master. command.Text = "USE DatabaseName"; using (SqlConnection connection = new SqlConnection( connectionString)) { connection.Open(); command.ExecuteNonQuery(); }
应用程序角色和连接池
应用程序角色替代项
如果您使用的是 SQL Server 2005,建议您使用可以替代应用程序角色的新安全机制。
下表列出了 ConnectionString 中的关键字值的有效名称。
关键字 |
默认值 |
说明 |
---|---|---|
Addr |
不可用 |
“数据源”的同义词。 |
地址 |
不可用 |
“数据源”的同义词。 |
App |
不可用 |
“应用程序名称”的同义词。 |
Application Name |
不可用 |
应用程序的名称,或者“.Net SqlClient Data Provider”(如果不提供应用程序名称)。 应用程序名称可以有 128 个字符或更少。 |
Asynchronous Processing - 或 - Async |
'false' |
如果设置为 true,则启用异步操作支持。 可识别的值为 true、false、yes 和 no。 |
AttachDBFilename - 或 - 扩展属性 - 或 - Initial File Name |
不可用 |
主数据库文件的名称,包括可连接数据库的完整路径名。 只有具有 .mdf 扩展名的主数据文件才支持 AttachDBFilename。 如果在连接字符串中指定了 AttachDBFileName 键的值,则附加数据库,且数据库成为连接的默认数据库。 如果不指定此键,且如果数据库是以前附加的,则不会重新附加该数据库将。 之前连接的数据库将用作连接的默认数据库。 如果连同 AttachDBFileName 键指定此键,则将把该键的值用作别名。 但是,如果该名称已在另一个附加数据库中使用,则此连接将失败。 该路经可以是绝对路径,也可以是相对路径,这取决于是否使用 DataDirectory 替换字符串。 如果使用 DataDirectory,则对应的数据库文件必须存在于替换字符串指向的目录的子目录中。 注意
远程服务器、HTTP 及 UNC 路径名不受支持。
必须按照如下方式使用关键字“database”(或其别名之一)指定数据库名称: "AttachDbFileName=|DataDirectory|\data\YourDB.mdf;integrated security=true;database=YourDatabase" 如果数据文件所在的目录中存在日志文件,并且在附加主数据文件时使用了“database”关键字,则会生成错误。 这种情况下,请移除日志文件。 附加了数据库后,系统将根据物理路径自动生成一个新的日志文件。 |
Connection Lifetime - 或 - Load Balance Timeout |
0 |
当连接被返回到池时,将其创建时间与当前时间作比较,如果时间长度(以秒为单位)超出了由 Connection Lifetime 指定的值,该连接就会被销毁。 这在聚集配置中很有用(用于强制执行运行中的服务器和刚置于联机状态的服务器之间的负载平衡)。 零 (0) 值将使池连接具有最大的连接超时。 |
Connect Timeout - 或 - Connection Timeout - 或 - 超时 |
15 |
在终止尝试并产生错误之前,等待与服务器的连接的时间长度(以秒为单位)。 有效值大于或等于 0 且小于或等于 2147483647 。 |
Context Connection |
'false' |
如果应对 SQL Server 进行进程内连接,则为 true。 |
Current Language - 或 - Language |
不可用 |
设置用于数据库服务器的警告或错误消息的语言。 语言名称可以有 128 个字符或更少。 |
数据源 - 或 - 服务器 - 或 - 地址 - 或 - Addr - 或 - Network Address |
不可用 |
要连接的 SQL Server 实例的名称或网络地址。 可以在服务器名称之后指定端口号: server=tcp:servername, portnumber 指定本地实例时,始终使用 (local)。 若要强制使用某个协议,请添加下列前缀之一: np:(local), tcp:(local), lpc:(local) 数据源必须使用 TCP 格式或命名的管道格式。 TCP 格式如下所示:
TCP 格式必须以前缀 "tcp:" 开始,后面跟着数据库实例,指其由主机名和实例名指定。 该主机名称“必须”按以下一种方式指定:
实例名称用于解析数据库实例所承载的特定 TCP/IP 端口号。 另外,还允许直接指定 TCP/IP 端口号。 如果两个实例名称和端口号不存在,则使用默认的数据库实例。 Named Pipes 格式如下所示:
命名管道格式必须以前缀"np:" 开始,后面跟着一个命名的管道名称。 该主机名称“必须”按以下一种方式指定:
管道名称用于标识数据库实例,.NET Framework 应用程序将连接至该数据库实例。 如果 Network 键的值已指定,则前缀“tcp:”与“np:”不应指定。 注意
ADO.NET 2.0 不支持在 SQL Server 2000 或更低版本的共享内存上执行异步命令。但是,可以强制用 TCP 代替共享内存,方法是:向连接字符串中的服务器名称添加“tcp:”前缀,或者使用“本地主机”。
|
Encrypt |
'false' |
当该值为 true 时,如果服务器端安装了证书,则 SQL Server 将对所有在客户端和服务器之间传送的数据使用 SSL 加密。 可识别的值为 true、false、yes 和 no。 |
Enlist |
'true' |
true 表明 SQL Server 连接池程序在创建线程的当前事务上下文中自动登记连接。 |
Failover Partner |
不可用 |
在其中配置数据库镜像的故障转移合作伙伴服务器的名称。 如果该键的值为 "",则 Initial Catalog 必须存在且值不能为 ""。 服务器名称可为 128 个字符或更少。 如果您指定故障转移合作伙伴,且未配置数据库镜像的故障转移合作伙伴,而且(用服务器关键字指定的)主服务器不可用, 则连接将失败。 如果您指定故障转移合作伙伴,且未配置数据库镜像的主服务器,那么主服务器可用时,(用服务器关键字指定)的主服务器的连接会成功。 .NET Framework 1.0 或 1.1 版不支持 Failover Partner 关键字。 |
Initial Catalog - 或 - 数据库 |
不可用 |
数据库的名称。 数据库名称可以有 128 个字符或更少。 |
Integrated Security - 或 - Trusted_Connection |
'false' |
当为 false 时,将在连接中指定用户 ID 和密码。 当为 true 时,将使用当前的 Windows 帐户凭据进行身份验证。 可识别的值为 true、false、yes、no 以及与 true 等效的 sspi(强烈推荐)。 如果指定了用户 ID 和密码,且集成安全性设置为 true,则用户 ID 和密码将被忽略,并使用集成安全性。 |
Min Pool Size |
0 |
池中允许的最大连接数。 有效值大于或等于 0。 此字段中的零 (0) 是指最初并未打开任何最小连接。 大于最大池大小的值将生成错误。 |
Max Pool Size |
100 |
池中允许的最大连接数。 有效值大于或等于 1。 小于最小池大小的值将生成错误。 |
MultipleActiveResultSets |
'false' |
如果为 true,则应用程序可以维护多活动结果集 (MARS)。 如果为 false,则应用程序必须在执行该连接上的任何其他批处理之前处理或取消一个批处理中的多个结果集。 可识别的值为 true 和 false。 .NET Framework 1.0 或 1.1 版不支持该关键字。 |
Network Library - 或 - 网络 - 或 - Net |
不可用 |
用于建立与 SQL Server 实例的连接的网络库。 支持的值包括: dbnmpntw(命名管道) dbmsrpcn(多协议,Windows RPC) dbmsadsn (Apple Talk) dbmsgnet (VIA) dbmslpcn(共享内存) dbmsspxn (IPX/SPX) dbmssocn (TCP/IP) Dbmsvinn (Banyan Vines) 相应的网络 DLL 必须安装在要连接的系统上。 如果不指定网络而使用一个本地服务器(比如“.”或“(local)”),则使用共享内存。在此示例中,网络库为 Win32 Winsock TCP/IP (dbmssocn),使用的端口为 1433。 Network Library=dbmssocn;Data Source=000.000.000.000,1433;
|
Packet Size |
8192 |
用来与 SQL Server 的实例进行通信的网络数据包的大小,以字节为单位。 包大小可以大于或等于 512 并小于或等于 32767。 |
密码 - 或 - PWD |
不可用 |
SQL Server 帐户登录的密码。 建议不要使用。 为保持高安全级别,我们强烈建议您使用 Integrated Security 或Trusted_Connection 关键字。 密码必须为 128 个字符或更少。 |
Persist Security Info - 或 - PersistSecurityInfo |
'false' |
当该值设置为 false 或 no(强烈推荐)时,如果连接是打开的或者一直处于打开状态,那么安全敏感信息(如密码)将不会作为连接的一部分返回。 重置连接字符串将重置包括密码在内的所有连接字符串值。 可识别的值为 true、false、yes 和 no。 |
Pooling |
'true' |
当此密钥的值设置为 true 时,在通过应用程序进行关闭时任何新创建的连接都被将添加到池。 在下一次尝试打开相同的连接时,该连接将会从池中被绘制。 如果连接具有相同的连接字符串,则将它们视为相同。 不同连接具有不同连接字符串。 此密钥的值可以为 "true"、"false"、"yes" 或 "no"。 |
复制 |
'false' |
如果使用连接来支持复制,则为 true。 |
Transaction Binding |
Implicit Unbind |
控制与登记的 System.Transactions 事务关联的连接。 可能的值包括: Transaction Binding=Implicit Unbind; Transaction Binding=Explicit Unbind; Implicit Unbind 可使连接在事务结束时从事务中分离。 分离后,连接上的其他请求将以自动提交模式执行。 在事务处于活动状态的情况下执行请求时,不会检查 System.Transactions.Transaction.Current 属性。 事务结束后,其他请求将以自动提交模式执行。 如果系统在最后一个命令完成之前结束该事务(在 Using 块的范围中),则它将引发 InvalidOperationException。 Explicit Unbind 可使连接保持连接到事务,直到连接关闭或调用显式 SqlConnection.TransactionEnlist(null)。 从 .NET Framework 4 版 开始,对 Implicit Unbind 所做的更改将使得 Explicit Unbind 过时。 如果 Transaction.Current 不是登记的事务或登记的事务未处于活动状态,则引发 InvalidOperationException。 |
TrustServerCertificate |
'false' |
如果设置为 true,则使用 SSL 对通道进行加密,但不通过证书链对可信度进行验证。 如果将 TrustServerCertificate 设置为 true并将 Encrypt 设置为 false,则不对通道进行加密。 可识别的值为 true、false、yes 和 no。 有关更多信息,请参见 连接字符串语法 (ADO.NET)。 |
Type System Version |
不可用 |
指示应用程序期望的类型系统的字符串值。 可能的值包括: Type System Version=SQL Server 2000; Type System Version=SQL Server 2005; Type System Version=SQL Server 2008; Type System Version=Latest; 如果设置为 SQL Server 2000,将使用 SQL Server 2000 类型系统。 与 SQL Server 2005 实例连接时,执行下列转换: XML 与 NTEXT UDT 到 VARBINARY VARCHAR(MAX)、NVARCHAR(MAX) 和 VARBINARY(MAX) 分别与 TEXT、NEXT 和 IMAGE。 如果设置为 SQL Server 2005,将使用 SQL Server 2005 类型系统。 对 ADO.NET 的当前版本不进行任何转换。 如果设置为 Latest,将使用此客户端-服务器对无法处理的最新版本。 这个最新版本将随着客户端和服务器组件的升级自动更新。 |
用户 ID - 或 - UID - 或 -
|
不可用 |
SQL Server 登录帐户。 建议不要使用。 为保持高安全级别,我们强烈建议您使用 Integrated Security 或 Trusted_Connection关键字。 用户 ID 所包含的字符数不能超过 128 个。 |
User Instance |
'false' |
一个值,用于指示是否将连接从默认的 SQL Server Express 实例重定向到调用方帐户下运行的运行时启动的实例。 |
Workstation ID - 或 - WSID |
本地计算机名称 |
连接到 SQL Server 的工作站的名称。 ID 所包含的字符数不能超过 128 个。 |
下表包含 ConnectionString 内连接池值的有效名称。 有关更多信息,请参见 SQL Server 连接池 (ADO.NET)。
-
Connection Lifetime(或 Load Balance Timeout)
-
Enlist
-
Max Pool Size
-
Min Pool Size
-
Pooling
当设置需要布尔值的关键字或连接池值时,您可以使用“yes”代替“true”,用“no”代替“false”。 整数值表示为字符串。
注意:
SQL Server .NET Framework 数据提供程序使用它自己的协议与 SQL Server 进行通信。 因此,当连接到 SQL Server 时,它不支持 ODBC 数据源名称 (DSN) 的使用,因为它不添加 ODBC 层。
用于 SQL Server 的 .NET Framework 数据提供程序不支持通用数据链接 (UDL) 文件。
在此版本中,在应用程序中根据用户输入构造连接字符串时(例如,从对话框中检索用户 ID 和密码信息并将其追加到连接字符串时)应相当谨慎。 应用程序应确保用户无法在这些值中嵌入其他连接字符串参数(例如,输入“validpassword;database=somedb”作为密码,以尝试连接到其他数据库)。 如果需要根据用户输入构造连接字符串,请使用新的 SqlConnectionStringBuilder,它可以验证连接字符串,并有助于消除此问题。 有关更多信息,请参见 连接字符串生成器 (ADO.NET)。
示例
private static void OpenSqlConnection() { string connectionString = GetConnectionString(); using (SqlConnection connection = new SqlConnection()) { connection.ConnectionString = connectionString; connection.Open(); Console.WriteLine("State: {0}", connection.State); Console.WriteLine("ConnectionString: {0}", connection.ConnectionString); } } static private string GetConnectionString() { // To avoid storing the connection string in your code, // you can retrieve it from a configuration file. return "Data Source=MSSQL1;Initial Catalog=AdventureWorks;" + "Integrated Security=true;"; }