使用 CONTEXT_INFO 验证连接池服务器端

Tom*_*m V 7 sql-server connection-pooling microsoft-dynamics

我正在使用 3 层应用程序 Microsoft Dynamics AX,其中中间层维护与 SQL Server 的连接。几个客户端连接到这个中间层服务器。

中间层服务器通常有几个连接到 SQL Server,所以我很确定它们正在池中,但是没有关于如何实现的文档。

通常情况下,我们可以不涉及SPID的用户或客户端应用程序,但在这里我们可以设置一个注册表项(具体到Microsoft Dynamics AX),这使得在提供这些信息的选择context_info的领域sys.dm_exec_sessions

同样,没有关于如何实现的文档。我们在这方面的唯一信息是MSDN 上一个模糊的博客条目

帖子提到

添加此信息的性能开销很小。

因此,我们不知道任何实现细节,例如:

  1. 信息是以某种方式包含在连接字符串中还是由 SET CONTEXT_INFO 完成?
  2. 什么时候重用连接?
  3. 可以预期的确切影响

有什么方法可以确定服务器端连接池的工作方式以及 context_info 的影响是什么?

更新:
这里使用这个查询

SELECT des.program_name,
       des.login_name,
       des.host_name,
--       der.database_id,
       COUNT(des.session_id) AS [Connections]
FROM sys.dm_exec_sessions des
INNER JOIN sys.dm_exec_connections DEC
        ON des.session_id = DEC.session_id
WHERE des.is_user_process = 1
--AND des.status <> 'running'
GROUP BY des.program_name,
         des.login_name,
         des.host_name
--         ,der.database_id
HAVING COUNT(des.session_id) > 2
ORDER BY COUNT(des.session_id) DESC
Run Code Online (Sandbox Code Playgroud)

我可以看到使用了连接池。

Sol*_*zky 6

首先,CONTEXT_INFO是会话的属性,而不是连接。当同一个会话被重用并执行第一批时,它会被sp_reset_connection重置。似乎在 SQL Server 2000 和可能更早的版本CONTEXT_INFO没有重置,但从 SQL Server 2005 开始它肯定被重置为NULL.

这里的部分混淆是该问题特定于“Microsoft Dynamics AX”,因为一般的 .NET 编程不会具有该博客文章中提到的注册表项。此外,Microsoft Dynamics 随后存储的信息CONTEXT_INFO是其应用程序会话详细信息,这与 SQL Server SPID 无关,不能用于推断连接池正在发生,因为应用程序会话自然会跨越多个连接以及 SPID .

用于设置的机制CONTEXT_INFO必须是在该会话的任何其他查询之前执行的单独的附加查询。类似的东西:

SqlConnection _Connection = new SqlConnection("{connection-string}");
_Connection.Open();

if(_IsConnectionContextRegistryKeySet)
{
  SqlCommand _Command = _Connection.CreateCommand();
  _Command.CommandType = CommandType.Text;

  _Command.CommandText = @"DECLARE @BinaryInfo VARBINARY(128);
                           SET @BinaryInfo = CONVERT(VARBINARY(128), @StringInfo);
                           SET CONTEXT_INFO @BinaryInfo;";

  SqlParameter _ParamInfo = new SqlParameter("@StringInfo", SqlDbType.VarChar, 100);
  _ParamInfo.Value = String.Format("{0} {1} {2}...", AXuserID, AXsessionID, ...);
  _Command.Parameters.Add(_ParamInfo);

  _Command.ExecuteNonQuery();
  _Command.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

因此,为设置此信息而产生的少量额外开销来自此额外查询的执行。

其次,您可以使用 SQL Server Profiler 测试连接池。在“Stored Procedures”类别中选择“RPC:Completed”事件,确保为该事件检查“TextData”、“ClientProcessID”和“SPID”(至少,如果您愿意,可以选择其他列)。然后,转到“列过滤器”,选择“TextData”,并在“Like”条件中添加以下条件:exec sp[_]reset[_]connection. 现在运行该跟踪。如果您看到exec sp_reset_connection 的实例通过,则这是由于使用了连接池。

此外,连接池的两个副作用可能会或可能不会出现在 DMV 中,具体取决于请求的连接数量。下面的查询应该捕获许多/大部分被池化的连接(请注意,它与 CONTEXT_INFO 无关):

SqlConnection _Connection = new SqlConnection("{connection-string}");
_Connection.Open();

if(_IsConnectionContextRegistryKeySet)
{
  SqlCommand _Command = _Connection.CreateCommand();
  _Command.CommandType = CommandType.Text;

  _Command.CommandText = @"DECLARE @BinaryInfo VARBINARY(128);
                           SET @BinaryInfo = CONVERT(VARBINARY(128), @StringInfo);
                           SET CONTEXT_INFO @BinaryInfo;";

  SqlParameter _ParamInfo = new SqlParameter("@StringInfo", SqlDbType.VarChar, 100);
  _ParamInfo.Value = String.Format("{0} {1} {2}...", AXuserID, AXsessionID, ...);
  _Command.Parameters.Add(_ParamInfo);

  _Command.ExecuteNonQuery();
  _Command.Dispose();
}
Run Code Online (Sandbox Code Playgroud)

此查询查找正在使用的连接池的以下指示:

  • 当前 session_id 与之前的 session_id 不同。如果这两个 ID 相同,那么它可能是也可能不是使用池的连接,因为可以重复使用相同的 SPID。但是,如果它们不同,那么这只能是连接池的结果。
  • 建立连接和会话开始之间的时间超过 50 毫秒(尽管该阈值可能因系统而异)。通常在连接上创建的第一个会话在连接后不到 30 毫秒,但它“通常”不应超过 50,即使执行多个SqlCommands。

沿着类似的线,它也应该通过创建一个临时表,并且每隔几秒钟,捕获两个是可能的测试连接池[session_id](INT)和[connection_id]从(UNIQUEIDENTIFIER) sys.dm_exec_connections。然后,只需查找具有相同 connection_id 但不同 session_id 的行。

最后,问题中发布的查询对于指示大多数 Web 应用程序的连接池无效。这里的问题是,通常情况下,“program_name”、“login_name”和“host_name”的属性对于 web/app 服务器建立的所有连接都是相同的(因此需要每个处理器/每个核心的许可模型而不仅仅是拥有 CAL 模型)。