Microsoft.Data.SqlClient 连接池会重用经过身份验证的物理连接。
SqlConnection.Open 或者 OpenAsync 检查池中是否有可用的连接。
Close、Dispose 或 DisposeAsync 会将其重置并返回。 这种方法避免了每次操作都需要网络连接、认证和会话设置。
默认情况下,池化是启用的。 请使用以下应用模式:
await using var connection = new SqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
using var command = new SqlCommand(sql, connection);
await command.ExecuteNonQueryAsync(cancellationToken);
延迟打开,尽早释放,让池来管理物理连接。 不要在全局范围内保留一个SqlConnection处于打开状态。
了解池密钥
连接只能从其对应的连接池中复用。 池密钥包含的不仅仅是目标服务器。
| Input | 池塘行为 |
|---|---|
| 连接字符串 | 文字必须完全一致。 关键词顺序的差异会产生独立的池,即使有效设置相同。 |
| Windows 集成身份验证 | Windows 身份是密钥的一部分。 同一字符串在不同身份下使用会创建不同的池。 |
SqlCredential |
对象实例是密钥的一部分。 即使包含相同的用户名和密码,独立实例也会创建不同的池。 |
SqlConnection.AccessToken |
访问令牌的值是密钥的一部分。 替换令牌字符串可能会创建新的连接池,并使现有连接池中保留那些仍使用旧令牌完成身份验证的连接。 |
SqlConnection.AccessTokenCallback |
回调是键的一部分。 对于应该共享池的连接,重复使用同一个回调实例。 返回的代币值不是池密钥。 |
| 自定义SSPI上下文提供者 | 提供者实例参与连接配置。 对于应共享同一个连接池的连接,请复用同一个提供程序实例。 |
| 环境事务 | 已登记的连接在匹配池内使用针对事务的细分。 |
数据库、身份验证模式、加密选项、应用程序名称、池化选项以及连接字符串中的所有其他值,都由同一个连接字符串决定。
建立一个规范的连接字符串并重复使用。 避免在 Application Name、Workstation ID 或其他关键字中使用针对每个请求的值。
选择可以池化的令牌 API
对于 Microsoft Entra ID 访问令牌,请使用 Microsoft.Data.SqlClient 提供的身份验证模式或稳定的 AccessTokenCallback。
AccessTokenCallback是在 Microsoft.Data.SqlClient 5.2 中引入的。 驱动程序在需要令牌时会调用它,并且可以为复用的池请求刷新后的令牌。 保持驱动提供的认证参数的回调确定性,并重复使用同一个代理实例。
当代码直接设置 AccessToken 时:
- 令牌字符串成为池密钥的一部分。
- 应用程序负责令牌的过期和刷新。
- 连接池中的物理连接,其存续时间可能长于用于创建该连接的令牌。
- 如果该池已无法安全使用,请在替换过期令牌后调用 ClearPool。
不要为每个请求都创建新的回调 Lambda 表达式或凭据对象。 对象身份差异可能导致池的碎片化。
Microsoft。Data.SqlClient 7.0 增加了SspiContextProvider自定义 Kerberos 或 NTLM 协商功能。 将提供者视为应用范围的连接配置,而非每个请求的状态。
每个池的规模
这些连接字符串选项控制同一个连接池:
| 关键 字 | 默认 | Effect |
|---|---|---|
Pooling |
true |
启用或禁用池化。 |
Min Pool Size |
0 |
设定池创建后保留的最少物理连接数。 |
Max Pool Size |
100 |
设定池中物理连接的最大数量。 |
Connect Timeout |
15 秒 | 设置在没有可用连接时 Open 的等待时长。 |
Load Balance Timeout |
0 秒 |
如果连接的年龄超过配置值,返回池时会丢弃该连接。
Connection Lifetime 是别名。 |
随着需求增长,池子会创建连接,直到达到 Max Pool Size。 当所有连接都已使用时,后续打开等待连接返回。 如果等待时间超过 Connect Timeout,则开局失败。
检查前不要提交 Max Pool Size:
- 每条路径上都布置了各个连接和读取器。
- 命令和交易都能迅速完成。
- 查询工作负载没有被阻塞或过于饱和。
- 数据库连接上限可以处理每个应用程序实例中每个池数量乘以
Max Pool Size。
Min Pool Size 为正值时,会在空闲期间保持连接处于打开状态。 只有在测量结果证明需要热连接时才使用。 它通常不利于缩容至零、无服务器自动暂停以及突发型云架构设计。
在默认的 Load Balance Timeout=0 情况下,定期清理通常会在大约四到八分钟后移除高于 Min Pool Size 的未使用连接,或者在检测到服务器连接断开时由池将其移除。 应将该时间间隔视为实现层面的行为特征,而不是针对每个连接的空闲时长保证。 连接池不会在每次检出连接前都发送验证查询,因为那样的往返开销会抵消连接池带来的大部分收益。
处理认证封锁期
在认证超时或其他认证失败后,池可能会进入阻塞期。 在此期间,匹配的开放尝试会重新抛出原始异常,而无需再次尝试认证。
首次阻止期为五秒。 又一次失败后,时间会翻倍到一分钟。
Pool Blocking Period 控制此行为:
| 价值 | Behavior |
|---|---|
Auto |
对普通 SQL Server 端点启用阻止,并对已识别的 Azure SQL 端点后缀禁用阻止。 虚 DNS 名称可能不会接收 Azure 行为。 |
AlwaysBlock |
为每个终结点启用阻止时间段。 |
NeverBlock |
禁用阻挡期。 |
保留 Auto,除非应用程序经验证的重试设计要求作出不同的选择。 禁用封锁期可能会将凭证、防火墙或故障问题变成认证风暴。
阻断时间和可配置的重试逻辑是分开的。 在阻塞期间打开同一池的重试提供者会收到缓存异常。
管理连接生存期和清除
当池检测到致命错误(例如故障切换)时,会自动清空受影响的池。 连接池会关闭空闲连接,并在已检出连接归还时将其丢弃。
使用清除 API 处理已知的配置或凭据边界:
-
ClearPool 清除与某个
SqlConnection配置关联的池。 - ClearAllPools清除进程或应用程序域中的所有 Microsoft.Data.SqlClient 连接池。
池关闭了清空池中的空闲连接。 池会标记当前正在使用的连接,因此在返回时会丢弃它们。
清空池会导致后续打开操作执行实际登录。 请勿将其用作定期维护、通用错误处理程序,或替代处理连接。
Load Balance Timeout 提供基于年龄的渐进人员流动。 当部署或集群服务需要让原有的物理连接随着时间逐步断开时,请使用它。 确认所选值不会导致过多的硬连接。
了解事务
使用默认设置 Enlist=true 时,在 System.Transactions.Transaction.Current 内部打开的连接会自动加入该事务。
当一个已加入事务的连接关闭时,连接池会将其放入事务专用的子池中。 同一交易下后续的开启可以重复使用。 物理连接直到交易完成后才会返回到通用池。
因此,长期或废弃的环境事务会:
- 把物理连接排除在一般资源池之外。
- 在逻辑连接关闭后仍然消耗池容量。
- 保持服务器锁和事务状态有效。
保持交易有界限,明确完成,并监控停滞连接。 仅当连接必须保持在环境事务之外时,才设置 Enlist=false。
防止池碎片化
池碎片化会产生许多小池,而不是几个可重复使用的池。 常见原因包括:
- 连接字符串关键字顺序或别名差异。
- 每个客户、用户、请求或数据库对应一个连接字符串。
- 在多个 Windows 身份下集成认证。
- 新
SqlCredential、访问令牌回调或按请求创建的 SSPI 提供程序实例。 - 每次刷新时都会更换直接访问令牌。
- 高基数应用程序名称或工作站 ID。
使用 SqlConnectionStringBuilder 规范化连接字符串,并集中处理连接创建。
如果应用程序有意连接多个数据库或身份,应将所得的池计数纳入容量规划。 不要使用不受信任的数据库名称来运行 USE 来合并池。 数据库隔离、权限、会话状态和池重置行为必须保持明确。
考虑应用程序角色和会话状态
池会在将物理连接分配给另一个逻辑连接之前,重置可重用的 SQL Server 会话状态。 应用代码仍应在其工作单元内设置所需的会话状态。
使用 sp_setapprole 激活的 SQL Server 应用程序角色无法安全地重置回常规池化状态。 优先使用数据库用户、包含的用户、角色、行级安全或其他授权设计。 如果某个应用角色不可避免,请采用有文档记录的基于 Cookie 的反转模式,或者在测试后对该隔离路径禁用池。
释放读取器,完成或回滚事务,并且不要在连接关闭时让命令继续运行。 不要指望临时表或其他会话状态能够跨逻辑连接保留下来。
使用云托管的池化模式
对于 Azure 应用服务、Azure Functions、容器、Kubernetes 和其他横向扩展的主机:
- 计算所有实例、进程、池键和副本之间可能的数据库连接。
- 使用管理身份或稳定访问令牌回调,而不是在连接对象中轮换令牌字符串。
- 保持
Min Pool Size=0,除非经过测量的冷启动要求证明有必要使用保留会话。 - 预计新实例会从一个空池开始。
- 在服务相同工作负载的实例之间保持连接字符串一致。
- 限制连接尝试次数和重试次数,以避免在故障切换或横向扩展期间出现同步登录激增。
- 设置
MultiSubnetFailover=true,用于 Azure SQL 和其他受支持的多地址 TCP 端点。
连接池是应用进程本地的。 它们不会在应用实例、容器或主机之间共享。
诊断池行为
使用 SqlClient 诊断计数器 观察以下内容:
- 硬连接和断开连接,代表物理服务器连接。
- 软连接和断开连接,分别代表池的检出与归还。
- 活动连接和空闲池化连接。
- 主动池组和池。
- 停滞连接。
- 重新收回连接,应用代码没有丢弃逻辑连接。
将客户端计数器与 SQL Server 会话、等待、阻塞和资源限制相关联。 池超时可能意味着连接泄漏、查询缓慢、事务阻塞、过多并发、池碎片化或数据库容量限制。
使用 事件源跟踪 对池化器进行有针对性的跟踪。 跟踪信息过于冗长。 启用它作为有界诊断窗口,并保护捕获的连接元数据。
生产核对清单
- 使池保持启用状态。
- 对每个工作负载和数据库,重复使用同一个标准连接字符串。
- 在每条路径上丢弃连接、命令、读取器和事务。
- 复用凭据、令牌回调和SSPI提供者实例。
- 为连接和命令设置有限的超时时间。
- 为每个应用实例计算总连接预算。
- 监控硬连接数、连接池计数、空闲连接数、停滞状态和超时。
- 仅在提供程序无法检测到凭据、令牌或配置边界,或诊断确认仍存在陈旧连接时,才清除连接池。
- 在生产环境上线前,对横向扩展、故障转移和凭据刷新行为进行负载测试。