SqlClient 中的事件计数器

适用于 : .NET .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 监视,或者可以使用 EventListener 命名空间中的 System.Diagnostics.Tracing 类以编程方式访问。

可用事件计数器

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

名称 显示名称 说明
活动硬连接 当前与服务器建立的实际活动连接数 当前对数据库服务器开放的连接数。
hard-connects 与服务器的实际连接率 每秒向数据库服务器开放的连接数。
强制断开 服务器实际断连率 每秒与数据库服务器断开的连接数。
active-soft-connects 从连接池中检索的活动连接 从连接池中使用的已开放连接数。
软连接 从连接池中检索的连接率 每秒从连接池使用的连接数。
soft-disconnects 返回到连接池的连接率 每秒返回到连接池的连接数。
非池化连接数 未使用连接池的连接数 未存入池中的活动连接的数量。
池化连接数 连接池管理的连接数 由连接池基础架构管理的活动连接数。
活动连接池组数量 活跃的唯一连接字符串数量 处于活动状态的唯一连接池组的数量。 此计数器由 AppDomain 中唯一连接字符串的数量控制。
非活动连接池组数量 等待删除的唯一连接字符串数 标记为删除的唯一连接池组的数量。 此计数器由 AppDomain 中唯一连接字符串的数量控制。
活动连接池数量 活跃连接池数量 连接池的总数。
非活动连接池数量 非活跃连接池数量 最近未进行任何活动且等待被释放的非活动连接池的数量。
活动连接数 活动连接数 当前正在使用的活跃连接数。
闲连接数 连接池中的就绪连接数 连接池中可用的打开的连接数。
停滞连接数 当前正在等待就绪的连接数 当前正在等待操作完成且应用程序无法使用的连接数。
已回收连接数 从 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 --counters Microsoft.Data.SqlClient.EventSource -p <process-id> --refresh-interval 3

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

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

进程内使用

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

进程内示例

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

注意

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

using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using Microsoft.Data.SqlClient;

// 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
...