Lesson Learned #407:Exploring Azure SQL Database Performance with sys.dm_os_performance_counters
Published Jul 27 2023 12:53 PM 1,763 Views

In this blog post, we will embark on an exciting journey into the world of Azure SQL Database performance monitoring using PowerShell and the powerful sys.dm_os_performance_counters system view. By leveraging PowerShell, we'll create a custom script that fetches critical performance metrics such as 'Logins/sec,' 'Logouts/sec,' and 'Batch Requests/sec' from the sys.dm_os_performance_counters view. This script will empower database administrators to gain valuable insights into database workload patterns and optimize performance for enhanced efficiency and responsiveness.

 

Discovering the sys.dm_os_performance_counters View:

 

The sys.dm_os_performance_counters view provides a treasure trove of valuable performance data for your Azure SQL Database. This dynamic management view (DMV) allows us to query real-time performance counters that track various aspects of database operations.

 

Creating the PowerShell Script:

 

  1. Building the Connection: The first step is to establish a connection to your Azure SQL Database. Replace the placeholders in the script with your specific database details.

  2. Crafting the Query: With the connection set, we'll construct a SQL query targeting the 'Logins/sec,' 'Logouts/sec,' and 'Batch Requests/sec' counters from sys.dm_os_performance_counters.

  3. Uncovering the Metrics: The script will periodically fetch the performance metrics at customizable intervals (e.g., every 2 minutes) for a specified duration (e.g., 30 minutes).

  4. Analyzing the Metrics: To glean more knowledge from the data, the script will calculate the average, maximum, minimum, and deviation of each performance metric obtained in every iteration.

  5. Presenting the Findings: The script will elegantly display the extracted metrics, showcasing the instance name, object name, counter name, and the calculated values in a clear and comprehensible format.

Regularly running this script and observing the output will empower you to make informed decisions, optimize database performance, and ensure seamless operations.

 

Feel free to extend the script to explore additional performance counters or integrate it with other monitoring tools to create a comprehensive performance monitoring solution tailored to your unique needs.

 

# Azure SQL DB details
$serverName = "servername.database.windows.net"
$databaseName = "dbname"
$userId = "username"
$password = "pwd" # Replace this with your actual password

# Define the interval in seconds (2 seconds in this case)
$intervalSeconds = 2

# Define the total duration in seconds (30 minutes in this case)
$totalDurationSeconds = 1800

# Calculate the number of iterations based on the interval and total duration
$numberOfIterations = [Math]::Ceiling($totalDurationSeconds / $intervalSeconds)

# Build the connection string to the database
$connectionString = "Server=$serverName;Database=$databaseName;User Id=$userId;Password=$password;"

# Create a connection to the database
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString

# Open the connection
$connection.Open()

# Query to get performance metrics
$query = @"
SELECT
    instance_name,
    object_name,
    counter_name,
    cntr_value
FROM
    sys.dm_os_performance_counters
WHERE
    [counter_name] IN ('Logins/sec', 'Logouts/sec', 'Batch Requests/sec')
"@

# Create the command
$command = $connection.CreateCommand()
$command.CommandText = $query

# Function to calculate the average, maximum, minimum, and deviation
function CalculateMetrics([int]$currentIndex, [double[]]$currentValues, [double[]]$previousValues) {
    try {
        $metrics = @{}
        for ($i = 0; $i -lt $currentValues.Length; $i++) {
            $instanceName = $instances[$i].Trim() # Remove leading and trailing white spaces
            $objectName = $objects[$i].Trim() # Remove leading and trailing white spaces
            $counterName = $counters[$i].Trim() # Remove leading and trailing white spaces
            $currentValue = $currentValues[$i]
            $previousValue = if ($currentIndex -gt 0) { $previousValues[$i] } else { $currentValue }

            $average = ($currentValue + $previousValue) / 2
            $maximum = [Math]::Max($currentValue, $previousValue)
            $minimum = [Math]::Min($currentValue, $previousValue)
            $deviation = [Math]::Abs($currentValue - $previousValue)

            # Output the metric names in one line with a different color
            Write-Host -ForegroundColor DarkCyan ("Metric Name: Instance - {0}, Object - {1}, Counter - {2}" -f $instanceName, $objectName, $counterName)

            # Output the metric values in the next line with a different color
            Write-Host -ForegroundColor Green ("Metric Value: Avg: {0:F2}, Max: {1:F2}, Min: {2:F2}, Deviation: {3:F2}" -f $average, $maximum, $minimum, $deviation)
        }
    } catch {
        Write-Host "Error in CalculateMetrics function: $_"
    }
}

# Lists to store instance names, object names, counters, and values
$instances = @()
$objects = @()
$counters = @()
$currentValues = @()
$previousValues = @()

for ($i = 1; $i -le $numberOfIterations; $i++) {
    try {
        # Execute the query
        $result = $command.ExecuteReader()

        # Create a hashtable to store the results of this iteration
        $iterationResults = @{}

        Write-Host "=== Iteration $i ==="
        while ($result.Read()) {
            $instanceName = $result["instance_name"]
            $objectName = $result["object_name"]
            $counterName = $result["counter_name"]
            $counterValue = $result["cntr_value"]

            # Store the instance names, object names, counters, and values in the lists
            if ($i -eq 1) {
                $instances += $instanceName
                $objects += $objectName
                $counters += $counterName
                $previousValues += $counterValue
            }
            $currentValues += $counterValue

            $iterationResults["$instanceName - $objectName - $counterName"] = $counterValue
        }

        # Close the reader after reading the data
        $result.Close()

        # Calculate and show metrics every 2 iterations
        if ($i % 2 -eq 0) {
            CalculateMetrics $i $currentValues $previousValues
        }

        # Update previous values for the next iteration
        $previousValues = $currentValues
        $currentValues = @()

    } catch {
        Write-Host "Error connecting to the database: $_"
    }

    # Wait for the specified interval before the next iteration
    Start-Sleep -Seconds $intervalSeconds
}

# Close the connection
$connection.Close()

 

Enjoy!

Version history
Last update:
‎Jul 27 2023 12:53 PM
Updated by: