-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSQL-PerformanceMonitor.ps1
424 lines (362 loc) · 12.6 KB
/
SQL-PerformanceMonitor.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
<#
.SYNOPSIS
Advanced SQL Server performance monitoring and health analytics toolkit.
.DESCRIPTION
This script provides comprehensive SQL Server monitoring capabilities:
- Performance Monitoring:
* Real-time CPU utilization tracking
* Memory usage and buffer cache analysis
* Batch request monitoring
* Page life expectancy tracking
* Blocking detection and analysis
- Health Analytics:
* Database status verification
* Index fragmentation assessment
* Backup status monitoring
* Job execution tracking
* Version and edition validation
- Reporting Features:
* Detailed HTML performance reports
* Real-time metric visualization
* Historical trend analysis
* Custom threshold alerts
* Job status summaries
- Alert Management:
* CPU threshold monitoring
* Memory utilization alerts
* Blocking detection notifications
* Backup status warnings
* Critical condition reporting
.NOTES
Author: 13city
Compatible with:
- SQL Server 2016+
- Windows Server 2012 R2+
- PowerShell 5.1+
Requirements:
- SqlServer PowerShell module
- SQL Server Management Tools
- Sysadmin or appropriate monitoring rights
- Write access to report directory
- Network connectivity to SQL instances
.PARAMETER ServerInstance
SQL Server instance name
Default: "." (local)
Format: ServerName\InstanceName
Example: SQLSERVER01\PROD
.PARAMETER DatabaseName
Target database name
Default: "*" (all databases)
Supports wildcards
Filters monitoring scope
.PARAMETER ReportPath
HTML report output path
Default: Desktop\SQL-HealthReport.html
Creates parent directories
Requires write permissions
.PARAMETER AlertThresholdCPU
CPU usage alert threshold
Default: 80 (percent)
Range: 0-100
Triggers warning alerts
.PARAMETER AlertThresholdMemory
Memory usage alert threshold
Default: 85 (percent)
Range: 0-100
Triggers warning alerts
.PARAMETER MonitoringDuration
Duration of monitoring in minutes
Default: 60 minutes
Range: 1-1440
Affects data collection period
.PARAMETER SampleInterval
Seconds between metric samples
Default: 5 seconds
Range: 1-3600
Controls monitoring granularity
.EXAMPLE
.\SQL-PerformanceMonitor.ps1
Basic monitoring with defaults:
- Local SQL instance
- All databases
- Default thresholds
- 60-minute duration
.EXAMPLE
.\SQL-PerformanceMonitor.ps1 -ServerInstance "SQLSERVER01\PROD" -DatabaseName "CustomerDB" -MonitoringDuration 120
Targeted monitoring:
- Specific server/database
- Extended duration
- Default thresholds
- Standard sampling
.EXAMPLE
.\SQL-PerformanceMonitor.ps1 -AlertThresholdCPU 70 -AlertThresholdMemory 80 -SampleInterval 10 -ReportPath "D:\Reports\SQLHealth.html"
Custom threshold monitoring:
- Lower alert thresholds
- Custom sampling interval
- Specific report location
- All databases included
#>
param (
[Parameter(Mandatory=$false)]
[string]$ServerInstance = ".",
[Parameter(Mandatory=$false)]
[string]$DatabaseName = "*",
[Parameter(Mandatory=$false)]
[string]$ReportPath = "$env:USERPROFILE\Desktop\SQL-HealthReport.html",
[Parameter(Mandatory=$false)]
[int]$AlertThresholdCPU = 80,
[Parameter(Mandatory=$false)]
[int]$AlertThresholdMemory = 85,
[Parameter(Mandatory=$false)]
[int]$MonitoringDuration = 60,
[Parameter(Mandatory=$false)]
[int]$SampleInterval = 5
)
# Function to test SQL Server connectivity
function Test-SqlConnection {
param (
[string]$ServerInstance
)
try {
$query = "SELECT @@VERSION AS Version"
$result = Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query -ErrorAction Stop
return $true
}
catch {
Write-Error "Failed to connect to SQL Server $ServerInstance : $_"
return $false
}
}
# Function to get SQL Server version and edition
function Get-SqlServerInfo {
param (
[string]$ServerInstance
)
$query = @"
SELECT
SERVERPROPERTY('ProductVersion') AS Version,
SERVERPROPERTY('Edition') AS Edition,
SERVERPROPERTY('ProductLevel') AS ServicePack,
SERVERPROPERTY('IsClustered') AS IsClustered
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to get database status
function Get-DatabaseStatus {
param (
[string]$ServerInstance,
[string]$DatabaseName
)
$query = @"
SELECT
name AS DatabaseName,
state_desc AS Status,
recovery_model_desc AS RecoveryModel,
log_reuse_wait_desc AS LogReuseWait,
compatibility_level AS CompatibilityLevel
FROM sys.databases
WHERE name LIKE '$DatabaseName'
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to get performance metrics
function Get-PerformanceMetrics {
param (
[string]$ServerInstance
)
$query = @"
SELECT
(SELECT cntr_value FROM sys.dm_os_performance_counters
WHERE counter_name = 'CPU usage %' AND instance_name = '_Total') AS CPUUsage,
(SELECT cntr_value FROM sys.dm_os_performance_counters
WHERE counter_name = 'Buffer cache hit ratio' AND object_name LIKE '%Buffer Manager%') AS BufferCacheHitRatio,
(SELECT cntr_value FROM sys.dm_os_performance_counters
WHERE counter_name = 'Page life expectancy' AND object_name LIKE '%Buffer Manager%') AS PageLifeExpectancy,
(SELECT SUM(cntr_value) FROM sys.dm_os_performance_counters
WHERE counter_name = 'Batch Requests/sec') AS BatchRequestsPerSec
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to get blocking information
function Get-BlockingInfo {
param (
[string]$ServerInstance
)
$query = @"
SELECT
r.session_id AS BlockedSessionID,
r.blocking_session_id AS BlockingSessionID,
r.wait_time AS WaitTime,
r.wait_type AS WaitType,
s.login_name AS LoginName,
s.host_name AS HostName,
DB_NAME(r.database_id) AS DatabaseName,
r.command AS Command,
t.text AS SQLText
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.blocking_session_id > 0
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to get index fragmentation
function Get-IndexFragmentation {
param (
[string]$ServerInstance,
[string]$DatabaseName
)
$query = @"
SELECT
DB_NAME(database_id) AS DatabaseName,
OBJECT_NAME(object_id) AS TableName,
index_id AS IndexID,
avg_fragmentation_in_percent AS FragmentationPercent,
page_count AS Pages
FROM sys.dm_db_index_physical_stats
(DB_ID('$DatabaseName'), NULL, NULL, NULL, 'LIMITED')
WHERE avg_fragmentation_in_percent > 30
AND page_count > 1000
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to get backup status
function Get-BackupStatus {
param (
[string]$ServerInstance,
[string]$DatabaseName
)
$query = @"
SELECT
d.name AS DatabaseName,
MAX(CASE WHEN b.type = 'D' THEN b.backup_finish_date END) AS LastFullBackup,
MAX(CASE WHEN b.type = 'I' THEN b.backup_finish_date END) AS LastDiffBackup,
MAX(CASE WHEN b.type = 'L' THEN b.backup_finish_date END) AS LastLogBackup
FROM sys.databases d
LEFT JOIN msdb.dbo.backupset b ON d.name = b.database_name
WHERE d.name LIKE '$DatabaseName'
GROUP BY d.name
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to get job status
function Get-JobStatus {
param (
[string]$ServerInstance
)
$query = @"
SELECT
j.name AS JobName,
CASE j.enabled WHEN 1 THEN 'Enabled' ELSE 'Disabled' END AS Status,
h.run_date AS LastRunDate,
h.run_time AS LastRunTime,
CASE h.run_status
WHEN 0 THEN 'Failed'
WHEN 1 THEN 'Succeeded'
WHEN 2 THEN 'Retry'
WHEN 3 THEN 'Canceled'
ELSE 'Unknown'
END AS LastRunStatus
FROM msdb.dbo.sysjobs j
LEFT JOIN msdb.dbo.sysjobhistory h ON j.job_id = h.job_id
WHERE h.step_id = 0
"@
return Invoke-Sqlcmd -ServerInstance $ServerInstance -Query $query
}
# Function to generate HTML report
function New-HTMLReport {
param (
[object]$ServerInfo,
[object]$DatabaseStatus,
[object]$PerformanceMetrics,
[object]$BlockingInfo,
[object]$IndexFragmentation,
[object]$BackupStatus,
[object]$JobStatus
)
$html = @"
<!DOCTYPE html>
<html>
<head>
<title>SQL Server Health Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #4CAF50; color: white; }
tr:nth-child(even) { background-color: #f2f2f2; }
h2 { color: #4CAF50; }
.warning { background-color: #fff3cd; }
.critical { background-color: #f8d7da; }
</style>
</head>
<body>
<h1>SQL Server Health Report - $(Get-Date -Format 'yyyy-MM-dd HH:mm')</h1>
<h2>Server Information</h2>
$($ServerInfo | ConvertTo-Html -Fragment)
<h2>Database Status</h2>
$($DatabaseStatus | ConvertTo-Html -Fragment)
<h2>Performance Metrics</h2>
$($PerformanceMetrics | ConvertTo-Html -Fragment)
<h2>Blocking Information</h2>
$($BlockingInfo | ConvertTo-Html -Fragment)
<h2>Index Fragmentation</h2>
$($IndexFragmentation | ConvertTo-Html -Fragment)
<h2>Backup Status</h2>
$($BackupStatus | ConvertTo-Html -Fragment)
<h2>Job Status</h2>
$($JobStatus | ConvertTo-Html -Fragment)
</body>
</html>
"@
return $html
}
# Main execution
try {
Write-Host "Starting SQL Server health check..."
# Test connection
if (-not (Test-SqlConnection -ServerInstance $ServerInstance)) {
throw "Failed to connect to SQL Server"
}
# Collect metrics
$serverInfo = Get-SqlServerInfo -ServerInstance $ServerInstance
Write-Host "Server information collected"
$databaseStatus = Get-DatabaseStatus -ServerInstance $ServerInstance -DatabaseName $DatabaseName
Write-Host "Database status collected"
$performanceMetrics = Get-PerformanceMetrics -ServerInstance $ServerInstance
Write-Host "Performance metrics collected"
$blockingInfo = Get-BlockingInfo -ServerInstance $ServerInstance
Write-Host "Blocking information collected"
$indexFragmentation = Get-IndexFragmentation -ServerInstance $ServerInstance -DatabaseName $DatabaseName
Write-Host "Index fragmentation information collected"
$backupStatus = Get-BackupStatus -ServerInstance $ServerInstance -DatabaseName $DatabaseName
Write-Host "Backup status collected"
$jobStatus = Get-JobStatus -ServerInstance $ServerInstance
Write-Host "Job status collected"
# Generate and save report
$report = New-HTMLReport -ServerInfo $serverInfo `
-DatabaseStatus $databaseStatus `
-PerformanceMetrics $performanceMetrics `
-BlockingInfo $blockingInfo `
-IndexFragmentation $indexFragmentation `
-BackupStatus $backupStatus `
-JobStatus $jobStatus
$report | Out-File -FilePath $ReportPath -Encoding UTF8
Write-Host "Health report generated successfully at: $ReportPath"
# Check for critical conditions
if ($performanceMetrics.CPUUsage -gt $AlertThresholdCPU) {
Write-Warning "High CPU usage detected: $($performanceMetrics.CPUUsage)%"
}
if ($blockingInfo) {
Write-Warning "Blocking detected in the database"
}
$oldBackups = $backupStatus | Where-Object {
$_.LastFullBackup -lt (Get-Date).AddDays(-7)
}
if ($oldBackups) {
Write-Warning "Some databases haven't been backed up in the last 7 days"
}
}
catch {
Write-Error "An error occurred during health check: $_"
}