SqlClient 中的事件计数器
适用于: .NET Framework .NET Core .NET Standard
重要
面向 .NET Core 3.1 及更高版本或 .NET Standard 2.1 及更高版本时,可以使用事件计数器 。 Microsoft.Data.SqlClient
版本 3.0.0 及更高版本支持此功能。
可以使用 Microsoft.Data.SqlClient 事件计数器来监视应用程序的状态及其使用的连接资源。 事件计数器可以通过 .NET CLI global tools
和 perfView
监视,或者可以使用 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 回收的连接数 | 已通过垃圾回收回收的连接数,其中应用程序未调用 Close 或 Dispose 。 注意:不显式关闭或处理连接会影响性能。 |
检索事件计数器值
使用 EventCounters 主要方式有两种:进程内或进程外。 有关详细信息,请参阅使用 EventCounters。
进程外使用
在 Windows 中,可以使用 PerfView 和 Xperf 收集事件计数器数据。 有关详细信息,请参阅在 SqlClient 中启用事件跟踪。 可以使用跨平台 .NET 工具 dotnet-counters 和 dotnet-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
...