Skip to main content

sqlserver-diagnostics

Identify SQL Server performance bottlenecks using DMV queries. Covers wait stats, slow queries, active requests, performance counters, and connection analysis.

Source facts

Repository
vince-winkintel/sql-server-skills
Last source activity
March 6, 2026 at 15:22
Detected SKILL.md language
English
Stars
1
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
sqlserver-diagnostics
description
Identify SQL Server performance bottlenecks using DMV queries. Covers wait stats, slow queries, active requests, performance counters, and connection analysis.
# SQL Server Diagnostics Use this skill when a SQL Server instance is slow, queries are timing out, or you need to identify the source of a performance problem. Start here before diving into indexes or execution plans. --- ## Recommended Workflow ``` 1. Check wait stats → scripts/wait-stats.sql 2. Find top slow queries → scripts/top-slow-queries.sql 3. See what's running NOW → scripts/active-queries.sql 4. Drill into a specific query → sqlserver-execution-plans/SKILL.md 5. Act on findings → sqlserver-indexes or sqlserver-query-optimization ``` --- ## 1. Wait Stats Analysis Wait stats are the single best indicator of what SQL Server is struggling with. Every time SQL Server can't proceed, it records a wait type. High cumulative waits tell you the category of problem. **Script:** `../scripts/wait-stats.sql` ```sql -- Quick inline version (run from any database) SELECT TOP 20 wait_type, wait_time_ms / 1000.0 AS wait_time_s, (wait_time_ms - signal_wait_time_ms) / 1000.0 AS resource_wait_s, signal_wait_time_ms / 1000.0 AS signal_wait_s, waiting_tasks_count, CAST(100.0 * wait_time_ms / SUM(wait_time_ms) OVER () AS DECIMAL(5,2)) AS pct_of_total FROM sys.dm_os_wait_stats WHERE wait_type NOT IN ( 'SLEEP_TASK','BROKER_TO_FLUSH','BROKER_TASK_STOP','CLR_AUTO_EVENT', 'CLR_MANUAL_EVENT','DISPATCHER_QUEUE_SEMAPHORE','FT_IFTS_SCHEDULER_IDLE_WAIT', 'HADR_FILESTREAM_IOMGR_IOCOMPLETION','HADR_WORK_QUEUE','LAZYWRITER_SLEEP', 'LOGMGR_QUEUE','ONDEMAND_TASK_QUEUE','REQUEST_FOR_DEADLOCK_SEARCH', 'RESOURCE_QUEUE','SERVER_IDLE_CHECK','SLEEP_DBSTARTUP','SLEEP_DCOMSTARTUP', 'SLEEP_MASTERDBREADY','SLEEP_MASTERMDREADY','SLEEP_MASTERUPGRADED', 'SLEEP_MSDBSTARTUP','SLEEP_SYSTEMTASK','SLEEP_TEMPDBSTARTUP', 'SNI_HTTP_ACCEPT','SP_SERVER_DIAGNOSTICS_SLEEP','SQLTRACE_BUFFER_FLUSH', 'WAITFOR','XE_DISPATCHER_WAIT','XE_TIMER_EVENT','DBMIRROR_EVENTS_QUEUE', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP','WAIT_XTP_OFFLINE_CKPT_NEW_LOG', 'BROKER_EVENTHANDLER','CHECKPOINT_QUEUE','DBMIRROR_WORKER_QUEUE', 'SQLTRACE_WAIT_ENTRIES','WAIT_XTP_ONLINE_CKPT_NEW_LOG','ASYNC_NETWORK_IO' ) ORDER BY wait_time_ms DESC; ``` ### Wait Type Interpretation Guide | Wait Type | Meaning | Typical Fix | |-----------|---------|-------------| | `PAGEIOLATCH_SH` / `PAGEIOLATCH_EX` | Reading/writing data pages from disk (I/O bound) | Add RAM, faster storage, better indexes to reduce I/O | | `LCK_M_*` | Lock waits (blocking) | Find and fix blocking queries; review transaction isolation | | `CXPACKET` / `CXCONSUMER` | Parallelism — threads waiting on each other | Review MAXDOP setting; fix skewed parallel queries | | `SOS_SCHEDULER_YIELD` | CPU pressure — threads giving up CPU time | Query tuning, reduce CPU-intensive operations | | `WRITELOG` | Log writes — transaction log is a bottleneck | Move log file to faster disk; reduce transaction size | | `RESOURCE_SEMAPHORE` | Memory grants waiting — queries need more memory | Fix memory-spilling queries; increase `max server memory` | | `ASYNC_NETWORK_IO` | Results sent to client faster than client reads | Reduce result set sizes; check client application | | `LATCH_EX` / `LATCH_SH` | Internal memory structure contention | Usually TempDB contention — add TempDB files | | `PAGELATCH_*` | In-memory page contention (TempDB allocation) | Add TempDB data files (8 files is common recommendation) | | `HADR_*` | Always On Availability Groups activity | Normal in AG environments; high values = AG lag | **Rule of thumb:** If one wait type dominates (>30% of total), that's your problem category. --- ## 2. Top Slow Queries Identify the queries consuming the most elapsed time, CPU, or I/O since SQL Server last restarted. **Script:** `../scripts/top-slow-queries.sql` ```sql -- Quick inline version SELECT TOP 25 qs.total_elapsed_time / qs.execution_count / 1000.0 AS avg_elapsed_ms, qs.total_elapsed_time / 1000.0 AS total_elapsed_ms, qs.execution_count, qs.total_logical_reads / qs.execution_count AS avg_logical_reads, qs.total_worker_time / qs.execution_count / 1000.0 AS avg_cpu_ms, qs.total_logical_reads AS total_logical_reads, DB_NAME(qt.dbid) AS database_name, SUBSTRING(qt.text, (qs.statement_start_offset/2)+1, ((CASE qs.statement_end_offset WHEN -1 THEN DATALENGTH(qt.text) ELSE qs.statement_end_offset END - qs.statement_start_offset)/2)+1) AS query_text, qs.query_hash FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt ORDER BY avg_elapsed_ms DESC; ``` **Key columns:** - `avg_elapsed_ms` — wall-clock time per execution (most useful for user-facing queries) - `avg_logical_reads` — data pages read from buffer pool (index problems show up here) - `avg_cpu_ms` — CPU time per execution (CPU-bound queries) - `execution_count` — high count + high avg = highest total impact --- ## 3. Currently Running Queries See what's actively executing right now, including wait info and blocking. **Script:** `../scripts/active-queries.sql` ```sql -- Quick inline version SELECT r.session_id, s.login_name, r.status, r.wait_type, r.wait_time / 1000.0 AS wait_time_s, r.cpu_time / 1000.0 AS cpu_time_s, r.logical_reads, r.total_elapsed_time / 1000.0 AS elapsed_time_s, r.blocking_session_id, SUBSTRING(t.text, (r.statement_start_offset/2)+1, ((CASE r.statement_end_offset WHEN -1 THEN DATALENGTH(t.text) ELSE r.statement_end_offset END - r.statement_start_offset)/2)+1) AS current_statement, s.host_name, s.program_name FROM sys.dm_exec_requests r JOIN sys.dm_exec_sessions s ON r.session_id = s.session_id CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t WHERE r.session_id != @@SPID AND s.is_user_process = 1 ORDER BY r.total_elapsed_time DESC; ``` **What to look for:** - `blocking_session_id > 0` → that session is being blocked (see monitoring/SKILL.md) - `wait_type = 'PAGEIOLATCH_*'` → query doing heavy I/O right now - `status = 'suspended'` + long `wait_time_s` → stuck on a resource --- ## 4. Performance Counters Key performance counters via DMV (no need for PerfMon): ```sql SELECT counter_name, instance_name, cntr_value FROM sys.dm_os_performance_counters WHERE counter_name IN ( 'Page life expectancy', -- Buffer pool health (want > 300s, ideally > 1000s) 'Batch Requests/sec', -- Server load 'SQL Compilations/sec', -- High = lots of ad-hoc queries or plan cache pressure 'SQL Re-Compilations/sec', -- High = unstable plans, check sp_recompile usage 'Page reads/sec', -- High = I/O pressure 'Page writes/sec', 'Lazy writes/sec', -- High = buffer pool too small 'Memory Grants Pending', -- > 0 regularly = memory pressure for query execution 'User Connections' ) AND instance_name IN ('', 'SQLServer:Buffer Manager') ORDER BY counter_name; ``` **Key thresholds:** - **Page Life Expectancy (PLE)** < 300s: buffer pool is too small — data is cycling out before reuse - **Memory Grants Pending** > 0: queries waiting for memory grants — potential sort/hash spills - **Lazy Writes/sec** > 20: SQL Server is flushing dirty pages too aggressively — add RAM - **SQL Re-Compilations/sec** high: check for `WITH RECOMPILE` stored procs or temp table schema changes --- ## 5. Connection Counts ```sql -- Active connections by database and login SELECT DB_NAME(dbid) AS database_name, loginame, COUNT(*) AS connection_count FROM sys.sysprocesses WHERE dbid > 0 GROUP BY dbid, loginame ORDER BY connection_count DESC; -- Total connection count SELECT COUNT(*) AS total_connections FROM sys.dm_exec_connections; ``` --- ## 6. Memory Usage ```sql -- Buffer pool usage by database SELECT DB_NAME(database_id) AS database_name, COUNT(*) * 8 / 1024.0 AS buffer_pool_mb FROM sys.dm_os_buffer_descriptors WHERE database_id > 4 -- exclude system databases GROUP BY database_id ORDER BY buffer_pool_mb DESC; -- Total SQL Server memory usage SELECT physical_memory_in_use_kb / 1024 AS memory_used_mb, page_fault_count FROM sys.dm_os_process_memory; ``` --- ## Diagnostic Checklist When a performance complaint comes in: - [ ] Run `wait-stats.sql` — what's the dominant wait type? - [ ] Run `top-slow-queries.sql` — any obvious offenders (high avg_elapsed_ms)? - [ ] Run `active-queries.sql` — any current blocking or long-running queries? - [ ] Check PLE via performance counters — is the buffer pool healthy? - [ ] Check `Memory Grants Pending` — are queries spilling to disk? - [ ] If blocking found → go to `sqlserver-monitoring/SKILL.md` - [ ] If slow query found → go to `sqlserver-execution-plans/SKILL.md` - [ ] If missing indexes suggested → go to `sqlserver-indexes/SKILL.md`
View on GitHub