SQL Server无直接获取CPU使用率的内置函数,最常用方法是查询sys.dm_os_performance_counters视图(需VIEW SERVER STATE权限),或通过sys.dm_os_ring_buffers估算系统级CPU利用率;sp_monitor和sys.dm_exec_sessions等无法提供实时百分比。

SQL Server 本身不提供直接返回 CPU 使用率的内置函数(比如 GET_CPU_USAGE() 这类),你不能像调用 GETDATE() 那样简单拿到一个百分比数字。真实可行的路径只有两条:查 sys.dm_os_performance_counters 视图,或调用 Windows 性能计数器(需启用 xp_cmdshell,但不推荐)。
查 sys.dm_os_performance_counters 获取近似 CPU 使用率
这是最常用、权限要求最低、也最安全的方式。核心思路是读取 SQL Server 实例级别的 % Processor Time 计数器,它反映的是 SQL Server 进程(sqlservr.exe)占用 CPU 的比例,不是整机负载。
注意:该值是采样统计,非实时瞬时值;且依赖 PERFMON 数据采集机制,首次查询可能为空(需等待几秒后重试)。
- 必须确保当前登录账号有
VIEW SERVER STATE权限 - 计数器对象名通常是
SQLServer:Resource Pool Stats或SQLServer:SQL Statistics,但最稳定的是Perfmon底层暴露的Processor类别 —— 实际在 SQL Server 中对应的是SQLServer:SQL Statistics下的Batch Requests/sec并不能反映 CPU,真正可用的是SQLServer:Resource Pool Stats中的CPU usage %(仅企业版支持资源调控器时才有) - 更通用的做法是查
sys.dm_os_performance_counters中object_name='SQLServer:SQL Statistics'且counter_name='Batch Requests/sec'没用;你应该找:object_nameLIKE'%Processor%'ANDcounter_name='% Processor Time'ANDinstance_name='_Total'—— 但这其实属于 Windows 层面的计数器,在 SQL Server 中默认不可见,除非你启用了sys.dm_os_windows_info关联或使用扩展事件 - 实际生产中最可靠、免配置的写法是:
SELECT CAST(100.0 * (a.cntr_value - b.cntr_value) / NULLIF((c.cntr_value - d.cntr_value), 0) AS DECIMAL(5,2)) AS [CPU_Usage_Pct] FROM sys.dm_os_performance_counters a CROSS JOIN sys.dm_os_performance_counters b CROSS JOIN sys.dm_os_performance_counters c CROSS JOIN sys.dm_os_performance_counters d WHERE a.counter_name = 'CPU usage %' AND a.object_name LIKE '%Resource Pool Stats%' AND a.instance_name = 'default' AND b.counter_name = 'CPU usage % base' AND b.object_name LIKE '%Resource Pool Stats%' AND b.instance_name = 'default' AND c.counter_name = 'CPU usage %' AND c.object_name LIKE '%Resource Pool Stats%' AND c.instance_name = 'default' AND d.counter_name = 'CPU usage % base' AND d.object_name LIKE '%Resource Pool Stats%' AND d.instance_name = 'default';
⚠️ 该查询仅在启用资源调控器(Resource Governor)的企业版/开发者版中有效。多数标准版用户会发现查不到数据 —— 这就是为什么它“看起来有,实则常失效”。
用 sys.dm_os_ring_buffers 估算最近 2 分钟 CPU 峰值
当无法依赖性能计数器时,这个 DMV 是退而求其次的实用选择。它记录了 SQL Server 内部 Ring Buffer 中的系统健康事件,包括 CPU 压力快照(每 1 分钟左右采样一次)。
关键点:
- 数据是环形缓冲区,只保留最近约 256 条记录,时间窗口通常为 1–2 分钟
- 字段
record是 XML,需用.value()提取systemHealth节点下的processUtilization - 返回的是整数百分比(0–100),代表当时 Windows 系统级整体 CPU 利用率,不是 SQL Server 进程独占值
- 无需额外权限,只要能查 DMV 即可(
VIEW SERVER STATE)
示例语句:
SELECT TOP 1
record_time = DATEADD(ms, -1 * (ts_now - [timestamp]), GETDATE()),
sql_cpu_utilization = CAST(record.value('(Record/@id)[1]', 'int') AS int),
system_idle = CAST(record.value('(Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS int),
sql_process_cpu_utilization =
100 - CAST(record.value('(Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS int) -
CAST(record.value('(Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS int)
FROM sys.dm_os_ring_buffers
CROSS JOIN (SELECT ts_now = ms_ticks FROM sys.dm_os_sys_info) AS t
WHERE ring_buffer_type = 'RING_BUFFER_SCHEDULER_MONITOR'
AND record LIKE '%<SystemHealth>%'
ORDER BY [timestamp] DESC;注意:上面 sql_process_cpu_utilization 是粗略反推(总 CPU − 系统空闲 − 其他进程),并不精确,但足够用于告警阈值判断。
避免踩坑:为什么 sp_monitor 和 sys.dm_exec_sessions 不行
新手常误以为这些能查 CPU 使用率,结果白忙一场:
-
sp_monitor只返回自上次执行以来的平均批处理数、I/O、CPU 时间(单位毫秒),不是百分比,也无法换算成当前利用率 -
sys.dm_exec_sessions中的cpu_time是该会话累计消耗的 CPU 毫秒数,和“此刻是否卡顿”毫无关系 -
sys.dm_exec_requests的cpu_time同理,且只对正在运行的请求有效;一旦查询结束,数值就冻结,无法反映系统负载趋势 - 试图用
WAIT_TIME或last_wait_type推断 CPU 是否瓶颈?错——高 CXPACKET 或 SOS_SCHEDULER_YIELD 才提示 CPU 紧张,但它们是间接信号,不是使用率本身
真正要监控 CPU,得接受一个现实:SQL Server 不是操作系统,它不测量也不暴露“当前 CPU 占用率”这个指标。你能拿到的,要么是 Windows 层的全局值(通过 Ring Buffer 或 Perfmon),要么是 SQL Server 进程自身的相对耗时(需自己做基线比对)。别指望一条 SELECT 就返回一个干净的 92.3 —— 那个数字背后,永远藏着采样周期、权限边界和版本限制。


















