3. 监控数据采集机制设计
SQL Server 的监控数据来源并不单一。操作系统层、实例层、查询执行层、等待与锁层、错误与审计层,各自暴露不同的采集接口。如果只用一种机制包打天下,要么采集频率过高拖垮实例,要么粒度太粗无法定位问题。本节的核心任务,是把六类采集机制放到同一张“能力地图”上,理解它们各自能回答什么问题、代价在哪里,再组合成一套分层采集方案。
六类采集机制的能力边界
WMI(Windows Management Instrumentation)面向操作系统与实例的“外圈”信息。通过 root\Microsoft\SQLServer\ComputerManagement* 命名空间,可以读取 SQL Server 服务状态、实例列表、协议配置、部分性能对象。它适合做实例存活探测、服务启停告警、多实例清单盘点。WMI 查询本身有较高开销,不适合秒级轮询,通常 30~60 秒一次即可。
性能计数器(Performance Counters)是操作系统与 SQL Server 共同暴露的数值型指标流,例如 SQLServer:Buffer Manager\Page life expectancy、SQLServer:SQL Statistics\Batch Requests/sec、Processor(_Total)\% Processor Time。它采样轻、频率高(1~15 秒),适合趋势与阈值告警。局限是只有聚合值,没有语句级、会话级细节,无法回答“是谁造成的”。
DMV/DMF(动态管理视图与函数)是实例内部的实时快照。例如 sys.dm_exec_requests 看当前请求与等待,sys.dm_os_wait_stats 看累计等待,sys.dm_db_index_usage_stats 看索引使用,sys.dm_exec_query_stats 看语句级聚合。DMV 是“按需查询”的,查询本身消耗 CPU 与内存,且多数是累计值,需要两次采样做差值。它适合 10~60 秒的常规巡检和故障时的即时诊断。
扩展事件(Extended Events,XE)是 SQL Server 2008 之后官方推荐的轻量事件框架,取代 SQL Trace。它支持事件过滤、谓词、目标(ring_buffer、event_file、histogram、pair_matching),可以精确捕获 sqlserver.sql_statement_completed、sqlserver.wait_info、sqlserver.lock_deadlock、sqlserver.error_reported 等。开销可控,适合长期运行的高价值事件采集,例如死锁图、超时语句、登录失败。
SQL Trace / Profiler是旧机制,通过 sp_trace_create 或服务器端跟踪写文件。它对高并发实例开销明显,微软已明确建议迁移到扩展事件。仅在维护老系统、需要复用既有 Trace 定义或某些 XE 尚未覆盖的场景下保留。
自定义采集脚本是用 T-SQL、PowerShell 或作业把上述来源按业务语义组合、落库、计算。它解决的是“采集之后怎么用”的问题:把 DMV 差值、计数器阈值、XE 事件统一写入监控库,形成可查询的历史。
组合策略:分层采集,而不是全量采集
把六类机制放在一起,合理的组合思路是“三层频率、三层粒度”。
第一层:秒级轻量指标。用性能计数器以 5 秒间隔采集 CPU、PLE、Batch Requests/sec、Page reads/sec、Log flushes/sec 等。这些指标写入监控库时只保留聚合值,用于趋势图和阈值告警。示例阈值:PLE 持续低于 300 秒告警,Batch Requests/sec 突降 50% 告警。
第二层:分钟级状态快照。用 DMV 每 30 秒采集一次 sys.dm_exec_requests、sys.dm_os_waiting_tasks、sys.dm_os_performance_counters 的关键行,做差值后落库。这一层回答“当前谁在等、等什么、等了多久”。
第三层:事件驱动取证。用扩展事件长期运行一个轻量会话,只捕获死锁、错误、超时、登录失败等低频高价值事件,写入 event_file 并定期归档。SQL Trace 仅在迁移过渡期保留。
自定义采集脚本负责把三层数据统一写入监控库。下面是一个采集作业的骨架示例,展示如何把 DMV 差值写入历史表:
-- 示例:每 30 秒采集等待任务快照(教学示例,非实测结果)
INSERT INTO dbo.Mon_WaitingTasks
(CollectTime, session_id, wait_type, wait_duration_ms, blocking_session_id)
SELECT
SYSDATETIME(),
wt.session_id,
wt.wait_type,
wt.wait_duration_ms,
wt.blocking_session_id
FROM sys.dm_os_waiting_tasks AS wt
WHERE wt.session_id IS NOT NULL
AND wt.wait_duration_ms > 5000; -- 只落库超过 5 秒的等待,控制数据量
-- 示例:采集语句级累计执行统计,供后续差值计算
INSERT INTO dbo.Mon_QueryStats
(CollectTime, query_hash, total_worker_time, total_elapsed_time, execution_count)
SELECT
SYSDATETIME(),
qs.query_hash,
qs.total_worker_time,
qs.total_elapsed_time,
qs.execution_count
FROM sys.dm_exec_query_stats AS qs
WHERE qs.query_hash IS NOT NULL;
落库后,用两次快照的差值计算“本周期新增等待时长”“本周期平均 CPU 时间”,再与计数器趋势对齐,就能判断是资源瓶颈还是语句问题。
选型时最容易踩的三个坑
坑一:用 DMV 做秒级轮询。sys.dm_exec_requests 和 sys.dm_exec_query_stats 在高并发下查询代价不低,秒级轮询会明显增加 CPU。正确做法是计数器做秒级,DMV 做 30 秒以上。
坑二:扩展事件会话不加过滤。如果直接捕获 sql_statement_completed 且不加谓词,事件量会非常大。应使用 WHERE duration > 500000(微秒)或 WHERE sqlserver.database_name = '业务库' 之类的谓词,把事件量压到可接受范围。
坑三:SQL Trace 长期开启。服务器端跟踪写文件会带来 IO 与锁竞争,且文件轮转管理复杂。若必须保留,应限制事件列、加过滤、设置文件上限,并制定迁移到 XE 的时间表。
一个可落地的采集频率参考
| 机制 | 采集频率 | 典型对象 | 落库方式 |
|---|---|---|---|
| 性能计数器 | 5 秒 | PLE、CPU、Batch Requests/sec | 聚合值直接写入 |
| DMV 等待任务 | 30 秒 | dm_os_waiting_tasks | 快照 + 差值 |
| DMV 语句统计 | 60 秒 | dm_exec_query_stats | 累计值 + 差值 |
| 扩展事件 | 事件驱动 | 死锁、错误、超时 | event_file 归档 |
| WMI | 60 秒 | 服务状态、实例清单 | 状态表 |
| SQL Trace | 过渡期按需 | 旧跟踪定义 | 文件 + 定期导入 |
这张表不是固定标准,而是起点。实际频率要根据实例负载、监控库容量和告警时效要求调整。判断依据是:采集本身带来的 CPU 增量不超过实例总 CPU 的 3%~5%,监控库日增量在可维护范围内,同时关键告警能在 1 分钟内触发。达到这个平衡,采集机制的设计才算真正落地。