SQL Server Diagnostics 是一个用于识别 SQL Server 性能瓶颈的技能,基于 DMV 查询实现。当 SQL Server 实例变慢、查询超时,或需要定位性能问题来源时,可先使用本技能,再深入索引或执行计划。
推荐工作流:
- 检查等待统计 → scripts/wait-stats.sql
- 查找最慢查询 → scripts/top-slow-queries.sql
- 查看当前正在运行的内容 → scripts/active-queries.sql
- 深入特定查询 → sqlserver-execution-plans/SKILL.md
- 根据发现采取行动 → sqlserver-indexes 或 sqlserver-query-optimization
一、等待统计(Wait Stats)分析 等待统计是判断 SQL Server 正在为何挣扎的最佳指标。每当 SQL Server 无法继续执行时,都会记录一个等待类型。累计等待高说明问题类别。 脚本:../scripts/wait-stats.sql 提供了一段可直接从任意数据库运行的内联查询,从 sys.dm_os_wait_stats 中取前 20 个等待类型,计算等待时间(秒)、资源等待时间、信号等待时间、等待任务数以及占总等待时间的百分比,并排除了一系列系统空闲等待类型。 等待类型解读指南(等待类型 / 含义 / 典型修复):
- PAGEIOLATCH_SH / PAGEIOLATCH_EX:从磁盘读写数据页(I/O 受限)→ 增加内存、更快的存储、更好的索引以减少 I/O
- LCK_M_*:锁等待(阻塞)→ 查找并修复阻塞查询;审查事务隔离级别
- CXPACKET / CXCONSUMER:并行度——线程互相等待 → 审查 MAXDOP 设置;修复倾斜的并行查询
- SOS_SCHEDULER_YIELD:CPU 压力——线程让出 CPU 时间 → 查询调优,减少 CPU 密集型操作
- WRITELOG:日志写入——事务日志成为瓶颈 → 将日志文件移到更快的磁盘;减小事务大小
- RESOURCE_SEMAPHORE:内存授予等待——查询需要更多内存 → 修复内存溢出的查询;增加最大服务器内存
- ASYNC_NETWORK_IO:结果发送给客户端快于客户端读取 → 减小结果集大小;检查客户端应用
- LATCH_EX / LATCH_SH:内部内存结构争用 → 通常是 TempDB 争用——增加 TempDB 文件
- PAGELATCH_*:内存中页争用(TempDB 分配)→ 增加 TempDB 数据文件(常见建议 8 个文件)
- HADR_*:Always On 可用性组活动 → 在 AG 环境中属正常;数值高表示 AG 延迟 经验法则:如果某一等待类型占主导(超过总量的 30%),那就是你的问题类别。
二、最慢查询 识别自 SQL Server 上次重启以来消耗最多耗时、CPU 或 I/O 的查询。 脚本:../scripts/top-slow-queries.sql 提供了一段内联查询,从 sys.dm_exec_query_stats 取前 25 条,输出平均耗时(毫秒)、总耗时、执行次数、平均逻辑读、平均 CPU 时间、总逻辑读、数据库名、查询文本和 query_hash,按平均耗时降序排列。 关键列:
- avg_elapsed_ms:每次执行的挂钟时间(对面向用户的查询最有用)
- avg_logical_reads:从缓冲池读取的数据页(索引问题会在此体现)
- avg_cpu_ms:每次执行的 CPU 时间(CPU 密集型查询)
- execution_count:高次数 + 高平均值 = 总影响最大
三、当前正在运行的查询 查看当前正在执行的内容,包括等待信息和阻塞情况。 脚本:../scripts/active-queries.sql 提供了一段内联查询,从 sys.dm_exec_requests 与 sys.dm_exec_sessions 关联,输出 session_id、登录名、状态、等待类型、等待时间、CPU 时间、逻辑读、已用时间、阻塞会话 ID、当前语句、主机名和程序名,排除当前会话且仅限用户进程,按总已用时间降序排列。 需要关注:
- blocking_session_id > 0 → 该会话正被阻塞(见 monitoring/SKILL.md)
- wait_type = 'PAGEIOLATCH_*' → 查询当前正在做大量 I/O
- status = 'suspended' + 较长的 wait_time_s → 卡在某个资源上
四、性能计数器 通过 DMV 获取关键性能计数器(无需 PerfMon): 查询 sys.dm_os_performance_counters,筛选以下计数器:Page life expectancy(缓冲池健康,希望 > 300 秒,理想 > 1000 秒)、Batch Requests/sec(服务器负载)、SQL Compilations/sec(高 = 大量即席查询或计划缓存压力)、SQL Re-Compilations/sec(高 = 计划不稳定,检查 sp_recompile 使用)、Page reads/sec(高 = I/O 压力)、Page writes/sec、Lazy writes/sec(高 = 缓冲池太小)、Memory Grants Pending(经常 > 0 = 查询执行的内存压力)、User Connections。 关键阈值:
- Page Life Expectancy (PLE) < 300 秒:缓冲池太小——数据在被重用前就被换出
- Memory Grants Pending > 0:查询在等待内存授予——可能出现排序/哈希溢出
- Lazy Writes/sec > 20:SQL Server 过于激进地刷新脏页——增加内存
- SQL Re-Compilations/sec 高:检查 WITH RECOMPILE 存储过程或临时表架构变更
五、连接数 提供按数据库和登录名统计活动连接的查询(基于 sys.sysprocesses),以及总连接数查询(基于 sys.dm_exec_connections)。
六、内存使用 提供按数据库统计缓冲池使用量的查询(基于 sys.dm_os_buffer_descriptors,排除系统数据库),以及 SQL Server 总内存使用查询(基于 sys.dm_os_process_memory,输出已用内存 MB 和页面错误计数)。
诊断检查清单(当收到性能投诉时):
- 运行 wait-stats.sql——主导等待类型是什么?
- 运行 top-slow-queries.sql——是否有明显的问题查询(高 avg_elapsed_ms)?
- 运行 active-queries.sql——当前是否有阻塞或长时间运行的查询?
- 通过性能计数器检查 PLE——缓冲池是否健康?
- 检查 Memory Grants Pending——查询是否溢出到磁盘?
- 如果发现阻塞 → 转到 sqlserver-monitoring/SKILL.md
- 如果发现慢查询 → 转到 sqlserver-execution-plans/SKILL.md
- 如果建议缺失索引 → 转到 sqlserver-indexes/SKILL.md