SqlClient 中的事件计数器

适用于:Not supported..NET Framework .NET Core .NET Standard

下载 ADO.NET

重要

面向 .NET Core 3.1 及更高版本或 .NET Standard 2.1 及更高版本时,可以使用事件计数器 。 Microsoft.Data.SqlClient 版本 3.0.0 及更高版本支持此功能。

可以使用 Microsoft.Data.SqlClient 事件计数器来监视应用程序的状态及其使用的连接资源。 事件计数器可以通过 .NET CLI global toolsperfView 监视,或者可以使用 System.Diagnostics.Tracing 命名空间中的 EventListener 类以编程方式访问。

可用事件计数器

当前有 16 个不同的事件计数器可用于 Microsoft.Data.SqlClient,如下表所述:

“属性” Display name 说明
active-hard-connections 当前与服务器建立的实际活动连接数 当前对数据库服务器开放的连接数。
hard-connects 与服务器的实际连接率 每秒向数据库服务器开放的连接数。
hard-disconnects 与服务器的实际断开连接率 每秒与数据库服务器的断开连接数。
active-soft-connects 从连接池中检索的活动连接数 从连接池中使用的已开放连接数。
soft-connects 从连接池中检索的连接率 每秒从连接池使用的连接数。
soft-disconnects 返回到连接池的连接率 每秒返回到连接池的连接数。
number-of-non-pooled-connections 未使用连接池的连接数 未存入池中的活动连接的数量。
number-of-pooled-connections 连接池管理的连接数 由连接池基础结构管理的活动连接的数量。
number-of-active-connection-pool-groups 活动的唯一连接字符串数 处于活动状态的唯一连接池组的数量。 此计数器由 AppDomain 中唯一连接字符串的数量控制。
number-of-inactive-connection-pool-groups 等待删除的唯一连接字符串数 标记为删除的唯一连接池组的数量。 此计数器由 AppDomain 中唯一连接字符串的数量控制。
number-of-active-connection-pools 活动连接池数 连接池的总数。
number-of-inactive-connection-pools 非活动连接池数 最近未进行任何活动且等待被释放的非活动连接池的数量。
number-of-active-connections 活动连接数 当前正在使用的活动连接的数量。
number-of-free-connections 连接池中的就绪连接数 连接池中可用的打开的连接数。
number-of-stasis-connections 当前正在等待就绪的连接数 当前正在等待操作完成且应用程序无法使用的连接数。
number-of-reclaimed-connections 从 GC 回收的连接数 已通过垃圾回收回收的连接数,其中应用程序未调用 CloseDispose。 注意:不显式关闭或处理连接会影响性能。

检索事件计数器值

使用 EventCounters 主要方式有两种:进程内或进程外。 有关详细信息,请参阅使用 EventCounters

进程外使用

在 Windows 中,可以使用 PerfViewXperf 收集事件计数器数据。 有关详细信息,请参阅在 SqlClient 中启用事件跟踪。 可以使用跨平台 .NET 工具 dotnet-countersdotnet-trace来监视和收集事件计数器数据。

进程外示例

以下命令每秒运行并收集一次 SqlClient 事件计数器值。 将 EventCounterIntervalSec=1 替换为较大的值可以在计数器数据中收集粒度更粗略的小型跟踪。

PerfView /onlyProviders=*Microsoft.Data.SqlClient.EventSource:EventCounterIntervalSec=1 run "<application-Path>"

以下命令每秒收集一次 SqlClient 事件计数器值。

dotnet-trace collect --process-id <pid> --providers Microsoft.Data.SqlClient.EventSource:0:1:EventCounterIntervalSec=1

以下命令每三秒监视一次 SqlClient 事件计数器值。

dotnet-counters monitor Microsoft.Data.SqlClient.EventSource -p <process-id> --refresh-interval 3

以下命令每秒监视一次所选 SqlClient 事件计数器值。

dotnet-counters monitor Microsoft.Data.SqlClient.EventSource[hard-connects,hard-disconnects] -p <process-id>

进程内使用

可以通过 EventListener API 使用计数器值。 EventListener 是一种过程内方法,用于使用由应用程序中的 EventSource 实例编写的任何事件。 有关详细信息,请参阅 EventListener

进程内示例

下面的示例代码使用 EventCounterIntervalSec=1 捕获 Microsoft.Data.SqlClient.EventSource 事件。 它会在每次事件计数器更新时写入计数器名称及其 Mean 值。

注意

启用此事件时,需要指定 EventCounterIntervalSec 属性值。

using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;

// This listener class will listen for events from the SqlClientEventSource class.
// SqlClientEventSource is an implementation of the EventSource class which gives 
// it the ability to create events.
public class EventCounterListener : EventListener
{
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        // Only enable events from SqlClientEventSource.
        if (eventSource.Name.Equals("Microsoft.Data.SqlClient.EventSource"))
        {
            var options = new Dictionary<string, string>();
            // define time interval 1 second
            // without defining this parameter event counters will not enabled
            options.Add("EventCounterIntervalSec", "1");
            // enable for the None keyword
            EnableEvents(eventSource, EventLevel.Informational, EventKeywords.None, options);
        }
    }

    // This callback runs whenever an event is written by SqlClientEventSource.
    // Event data is accessed through the EventWrittenEventArgs parameter.
    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (eventData.Payload.FirstOrDefault(p => p is IDictionary<string, object> x && x.ContainsKey("Name")) is IDictionary<string, object> counters)
        {
            if (counters.TryGetValue("DisplayName", out object name) && name is string cntName
                && counters.TryGetValue("Mean", out object value) && value is double cntValue)
            {
                // print event counter's name and mean value
                Console.WriteLine($"{cntName}\t\t{cntValue}");
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Create a new event listener
        using (var listener = new EventCounterListener())
        {
            string connectionString = "Data Source=localhost; Integrated Security=true";

            for (int i = 0; i < 50; i++)
            {
                // Open a connection
                SqlConnection cnn = new SqlConnection(connectionString);
                cnn.Open();
                // wait for sampling interval happens
                System.Threading.Thread.Sleep(500);
            }
        }
    }
}
Actual active connections currently made to servers           0
Active connections retrieved from the connection pool         26
Number of connections not using connection pooling            0
Number of connections managed by the connection pool          26
Number of active unique connection strings              1
Number of unique connection strings waiting for pruning       0
Number of active connection pools               1
Number of inactive connection pools             0
Number of active connections            26
Number of ready connections in the connection pool            0
Number of connections currently waiting to be ready           0
...

另请参阅