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 事件统一写入监控库,形成可查询的历史。

六类采集机制的分层组合与数据流向 采集层(数据来源) WMI 服务状态/实例清单 30~60s 轮询 开销:中 性能计数器 PLE/CPU/IO/批次 1~15s 采样 开销:低 DMV / DMF 请求/等待/索引/语句 10~60s 快照 开销:中 扩展事件 XE 死锁/超时/错误 事件驱动+过滤 开销:低~中 SQL Trace 旧机制/兼容 服务器端跟踪 开销:高 自定义脚本 T-SQL/PS 作业 组合与落库 开销:可控 汇聚层(统一采集与落库) 采集作业调度(SQL Agent) 差值计算 / 阈值判定 监控库表(历史+事件) 告警与看板输出 选型决策:按“问题类型”匹配机制 实例是否存活/服务异常 → WMI + 计数器 整体资源趋势/容量 → 性能计数器 + DMV 当前阻塞/慢语句定位 → DMV 即时查询 死锁/超时/错误取证 → 扩展事件 XE 仅当旧系统兼容或 XE 未覆盖时 → 保留 SQL Trace,并规划迁移 所有来源统一由自定义脚本编排 → 形成可查询、可告警、可复盘的历史

组合策略:分层采集,而不是全量采集

把六类机制放在一起,合理的组合思路是“三层频率、三层粒度”。

第一层:秒级轻量指标。用性能计数器以 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 的时间表。

选型口诀:计数器看趋势,DMV 看当下,XE 看事件,WMI 看存活,Trace 只做过渡,脚本负责统一落库。任何一层都不应单独承担全部监控职责。

一个可落地的采集频率参考

机制采集频率典型对象落库方式
性能计数器5 秒PLE、CPU、Batch Requests/sec聚合值直接写入
DMV 等待任务30 秒dm_os_waiting_tasks快照 + 差值
DMV 语句统计60 秒dm_exec_query_stats累计值 + 差值
扩展事件事件驱动死锁、错误、超时event_file 归档
WMI60 秒服务状态、实例清单状态表
SQL Trace过渡期按需旧跟踪定义文件 + 定期导入

这张表不是固定标准,而是起点。实际频率要根据实例负载、监控库容量和告警时效要求调整。判断依据是:采集本身带来的 CPU 增量不超过实例总 CPU 的 3%~5%,监控库日增量在可维护范围内,同时关键告警能在 1 分钟内触发。达到这个平衡,采集机制的设计才算真正落地。