Collect Cross-Environment Statistics Using Powershell

Using Simple Powershell Processes

Your DBA is receiving complaints about a slow server at random periods throughout the week.  How do you assess the environment and troubleshoot performance issues at different periods?

Using a series of PowerShell scripts, you can easily gather key statistics across your entire environment, and run the process several times a day to capture those statistics during different time periods and activity levels.  The data is stored in a series of tables, which can then be used for alerting and trend reporting.

Unique Design Efficiencies:

The process has some unique design efficiencies that expedite the collection of data:

  • Gather Data Across Entire Environment at One Time: Code can gather data across the an entire environment dynamically, utilizing connection information stored in a separate database or table.
  • Gather Data At Different Time Intervals: The process can be run scheduled to run several times a day to capture statistics during different time periods/activity levels.
  • Executes Stats Gathering Jobs in Parallel: The scripts utilize parameters to insert multiple stats gathering jobs in parallel, so that stats are gathered across your environment in the most efficient way possible

Data Available for Reporting: The process stores collective history of data into a series of tables.
Data can then be used for trending and reporting and troubleshooting.

How it Works:

The process utilizes the following components:

  1. Scheduled Batch Script: A controlling batch script can be scheduled several times a day to execute an Update Script.
  2. Update Script: An update script loops through each server (connection information is dynamically gathered, and identified in a separate table).
  3. Gather Stats Script: The Update script executes a Gather Stats Script to gather statistics on all servers, such as:
    • Logical Disk Space
    • Physical Average Disk Space
    • Free Memory
    • SQLServerAgentStatus
    • Processor Time
    • Paging File Usage
  4. Store Data: The Gather Stats script then calls a stored procedure to store the collected data. Data can then be used for troubleshooting, as well as for analysis and trending reporting, so you can detect issues before they occur or trouble shoot errors as they are uncovered .

These simple scripts are outlined below:

				
					#CONTROLLING BATCH SCRIPT
param ( [string] $server_name1 = $server_name,
        [int]    $server_id1 = $server_id,
        [int]    $client1 = $client_id,
        [string] $logfile1 = $logfile,
        [string] $conn1 = $conn
       )
#############
##Logical
#############
$path=”\$server_name1logicalDisk(_total)*”
write-host “the path is ” $path
$p = Get-counter $path ##”\$SQLServerlogicalDisk(_total)*”
foreach ( $prop in $p.countersamples)
{  write-host $prop.path
if ( $prop.path -like “*avg. disk read queue length*”)
    { [float] $LogicalAvgDiskReadQueue = $prop.cookedvalue  }
if ( $prop.path -like “*avg. disk write queue length*”)
    { [float] $LogicalAvgDiskWriteQueue = $prop.cookedvalue  }
}
#############
## Memory
#############
$PagesSec = (
     get-counter –computername $server_name1 -Counter “Memorypages/sec”  –SampleInterval 1 –MaxSamples 5   |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$CommittedBytes = (
     get-counter –computername $server_name1 -Counter “MemoryCommitted Bytes”  –SampleInterval 1 –MaxSamples 5   |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$totmem=Get-WmiObject win32_operatingsystem –computername $server_name1 |
                   Foreach {“{0:N2}” -f ($_.TotalVisibleMemorySize)}
$totfree=Get-WmiObject win32_operatingsystem –computername $server_name1 |
                   Foreach {“{0:N2}” -f ($_.FreePhysicalMemory)}
#############
## Physical
#############
 $p = Get-counter $path
foreach ( $prop in $p.countersamples)
{
if ( $prop.path -like “*disk reads/sec*”)
    { [float]$PhysicalAvgDiskBytesRead = $prop.cookedvalue }
if ( $prop.path -like “*disk writes/sec*”)
    { $PhysicalAvgDiskBytesWrites = $prop.cookedvalue }
if ( $prop.path -like “*avg. disk sec/read*”)
     {write-host “sec/read”
     [float] $PhysicalAvgDiskSecRead = $prop.cookedvalue  }
if (  $prop.path -like “*avg. disk sec/write*”)
    { write-host “sec/write “
    [float] $PhysicalAvgDiskSecWrite = $prop.cookedvalue  }
}
$PagingUsage= (
     get-counter –computername $server_name1 -Counter  “Paging File(_Total)% Usage”  –SampleInterval 1 –MaxSamples 5 |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$ProcessorQueueLength= (
     get-counter –computername $server_name1 -Counter  “SystemProcessor Queue Length”  –SampleInterval 1 –MaxSamples 5 |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$ProcessorTime = (
     get-counter –computername $server_name1 -Counter “Processor(_Total)% Processor Time” –SampleInterval 1 –MaxSamples 5 |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$ProcessorPrivelegedTime = (
     get-counter –computername $server_name1 -Counter “Processor(_Total)% Privileged Time” –SampleInterval 1 –MaxSamples 5 |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$ProcessTime = (
     get-counter –computername $server_name1 -Counter “Process(_Total)% Processor Time” –SampleInterval 1 –MaxSamples 5 |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
$ProcessPrivelegedTime = (
     get-counter –computername $server_name1 -Counter “Process(_Total)% Privileged Time” –SampleInterval 1 –MaxSamples 5 |
     select –ExpandProperty countersamples | select –ExpandProperty cookedvalue | Measure-Object -Average).average
##############################################################
##  Now load all the values into our sql server tables
##############################################################
     ##********************************************************************
     ## Set up the connection string
     ##********************************************************************
     $SqlConnection = New-Object System.Data.SqlClient.SqlConnection
     $SqlConnection.ConnectionString = $conn1
     $SqlCmd = New-Object System.Data.SqlClient.SqlCommand
     $SqlCmd.Connection = $SqlConnection
     ##********************************************************************
     ## Set up the stored procedure command
     ##********************************************************************
     $sqlcmd.CommandText = “spr_ps_update_r_sql_server_property“
     ##********************************************************************
     ## Set up the stored procedure parameters (input and output)
     ##********************************************************************
     $param1=$sqlcmd.Parameters.Add(“@i_server_name” , [System.Data.SqlDbType]::String)
     $param1.Value = $server_name1
     $param2=$sqlcmd.Parameters.Add(“@i_property” , [System.Data.SqlDbType]::String)
     $param2.Value = “TotalMemory“
     $param3=$sqlcmd.Parameters.Add(“@i_value” , [System.Data.SqlDbType]::Float)
     $param3.Value = $totmem
     $param4=$sqlcmd.Parameters.Add(“@i_user_id” , [System.Data.SqlDbType]::Int32)
     $param4.Value = 0
     $outParam1 = new-object System.Data.SqlClient.SqlParameter;
     $outParam1.ParameterName = “@o_success_flg“;
     $outParam1.Direction = [System.Data.ParameterDirection]’Output’;
     $outParam1.DbType = [System.Data.DbType]’Int32′;
     $SqlCmd.Parameters.Add($outParam1) >> $null;
     ##********************************************************************
     ## Run the stored procedure for each property gathered
     ##********************************************************************
     $SqlCmd.Connection = $SqlConnection
     $SqlCmd.CommandType = [System.Data.CommandType]::”StoredProcedure“
     $SqlConnection.Open()
     $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
     $SqlAdapter.SelectCommand = $SqlCmd
     $DataSet = New-Object System.Data.DataSet
     $SqlAdapter.Fill($DataSet)
     ## PagesPerSec
     $param2.Value = “PagesPerSec“
     $param3.Value = $PagesSec
     $SqlAdapter.Fill($DataSet)
     ##  TotalFree
     $param2.Value = “TotalFree“
     $param3.Value = $totfree
     $SqlAdapter.Fill($DataSet)
     ## LogicalAvgDiskReadQueue“
     $param2.Value = “LogicalAvgDiskReadQueue“
     $param3.Value = $LogicalAvgDiskReadQueue
     $SqlAdapter.Fill($DataSet)
     ## LogicalAvgDiskWriteQueue
     $param2.Value = “LogicalAvgDiskWriteQueue“
     $param3.Value =  $LogicalAvgDiskWriteQueue
     $SqlAdapter.Fill($DataSet)
     ## SQLServerAgentStatus
     $param2.Value = “SQLServerAgentStatus“
     $param3.Value =  $SQLServerAgentStatus
     $SqlAdapter.Fill($DataSet)
     ## PhysicalAvgDiskBytesRead
     $param2.Value = “PhysicalAvgDiskBytesRead“
     $param3.Value =  $PhysicalAvgDiskBytesRead
     $SqlAdapter.Fill($DataSet)
     ## PhysicalAvgDiskBytesWrites
     $param2.Value = “PhysicalAvgDiskBytesWrites“
     $param3.Value =  $PhysicalAvgDiskBytesWrites
     $SqlAdapter.Fill($DataSet)
     ## processor time
     $param2.Value = “processor time”
     $param3.Value = $ProcessorTime
     $SqlAdapter.Fill($DataSet)
     ## Process processor time
     $param2.Value = “process processor time”
     $param3.Value = $ProcessTime
     $SqlAdapter.Fill($DataSet)
     ## Processor privileged time
     $param2.Value = “Processor Privileged Time”
     $param3.Value = $ProcessorPrivelegedTime
     $SqlAdapter.Fill($DataSet)
     ## Process privileged time
     $param2.Value = “Process Privileged Time”
     $param3.Value = $ProcessPrivelegedTime
     $SqlAdapter.Fill($DataSet)
     ## Processor Queue Length
     $param2.Value = “Processor Queue Length”
     $param3.Value = $ProcessorQueueLength
     $SqlAdapter.Fill($DataSet)
     ## Paging File Usage
     $param2.Value = “Paging File Usage”
     $param3.Value = $PagingUsage
     $SqlAdapter.Fill($DataSet)
     ## Physical Avg Disk sec/read
     $param2.Value = “Physical Avg Disk sec/read”
     $param3.Value = $PhysicalAvgDiskSecRead
     $SqlAdapter.Fill($DataSet)
     ## Physical Avg Disk sec/write
     $param2.Value = “Physical Avg Disk sec/write”
     $param3.Value = $PhysicalAvgDiskSecWrite
     $SqlAdapter.Fill($DataSet)
     ## Committed Bytes
     $param2.Value = “Committed Bytes”
     $param3.Value = $CommittedBytes
     $SqlAdapter.Fill($DataSet)
     $SqlConnection.Close()
UPDATE SCRIPT
ALTER PROCEDURE [dbo].[spr_ps_update_r_sql_server_property]
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Purpose:       Insert property and value into r_sql_server_property.
               If a new property, also insert into lu_server_property.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
(
    @i_server_name      Varchar(80)
  , @i_property         Varchar(100)
  , @i_value            float
  ,             @i_user_id          integer
  , @o_success_flg      integer        OUTPUT
)
AS
BEGIN TRY
    DECLARE @CurrentDate       DateTime
          , @client_id         integer
          , @server_id         integer
          , @property_id       integer
          , @reccnt            integer
                SET @CurrentDate = GetDate()
    BEGIN
      –**************************************************
      —  DETERMINE IF LU_SERVER_PROPERTY RECORD EXISTS
      –**************************************************
      SELECT  @reccnt = count(*)
        FROM lu_server_property p
       WHERE upper(p.SERVER_PROPERTY_DESC) = upper(ltrim(rtrim(@i_property)))
      PRINT @reccnt
    IF ( @reccnt = 0 )
       BEGIN
          –*******************************************
          —  GET NEXT PROPERTY NUMBER
          –*******************************************
          SELECT @property_id = max(SERVER_PROPERTY_ID) + 1
            FROM LU_SERVER_PROPERTY
              if (@property_id is null )
                  BEGIN
                    SET @property_id = 11
                  END
          –************************************************
          —  INSERT NEW PROPERTY INTO LU_SERVER_PROPERTY
          –************************************************
          INSERT INTO LU_SERVER_PROPERTY
            (SERVER_PROPERTY_ID
            , SERVER_PROPERTY_DESC
            , ACTIVE_FLAG
            , LASTMOD
            , USER_ID )
            VALUES
            (@property_id,
             upper(@i_property),
             ‘Y’,
             @CurrentDate,
             @i_user_id)
        END
        –*******************************************
        —  GET THE PROPERTY ID
        –*******************************************
        SELECT  @property_id = p.SERVER_PROPERTY_ID
          FROM lu_server_property p
         WHERE upper(p.SERVER_PROPERTY_DESC) = upper(ltrim(rtrim(@i_property)))
          –*******************************************
          —  GET THE SERVER ID
          –*******************************************
          SELECT @server_id = SERVER_ID,
                 @client_id = CLIENT_ID
            FROM SERVER
           WHERE upper(SERVER_NAME) =   upper(@i_server_name)
           –*******************************************
          —  INSERT NEW SERVER PROPERTY RECORD
          –*******************************************
                                  INSERT INTO dbo.R_SQL_SERVER_PROPERTY
                                            (
                                             CLIENT_ID
                                            ,SERVER_ID
                                            ,JOB_ID
                                            ,LASTMOD
                                            ,PROPERTY_ID
                                            ,PROPERTY_VALUE
                                            )
                      VALUES      (    @client_id
                                     , @server_id
                                                                                                 , 9999
                                                                                                 , @CurrentDate
                                                                                                 , @property_id
                                                                                                 , @i_value
                                                                                   )
                END
                set @o_success_flg = 1
                RETURN @o_success_flg
END TRY
BEGIN CATCH
                DECLARE @msg  varchar(300)
                SET @msg = ‘Server ‘ + @@servername + ‘, Database ‘ + db_name() + ‘, Procedure spr_ps_update_r_sql_server_property ERROR: ‘ + ERROR_MESSAGE() + ‘; Number ‘ + cast(ERROR_NUMBER() as varchar(9)) + ‘, Line ‘ + cast(ERROR_LINE() as varchar(9))
                SET @o_success_flg = 0
                RETURN  @o_success_flg
                RAISERROR(@msg, 16, 1)
END CATCH
GATHERING STATS SCRIPT
{rtf1ansiansicpg1252deff0deflang1033{fonttbl{f0fswissfcharset0 Arial;}}
{*generator Msftedit 5.41.15.1515;}viewkind4uc1pardf0fs20 #************************************************************************par
#**  NAME:    UpdateServerProperties.ps1par
#**  COMMENTS:  Gather statistics on all servers and store resultspar
#************************************************************************par
#** Initialize Parameterspar
#************************************************************************par
 param (par
    [string] $user,par
    [string] $pass,par
    [string] $database,par
    [string] $SQLServer,par
    [string] $logfile,par
    [string] $dirpar
    )par
$outbuffer =  get-date | add-content $logfilepar
$outbuffer = “starting Execution of UpdateServerDisk.ps1” | add-content $logfilepar
$outbuffer = ” ” | add-content $logfilepar
##Add-PSSnapin SqlServerCmdletSnapin100par
##Add-PSSnapin SqlServerProviderSnapin100par
$outbuffer = ” Getting Server information” | add-content $logfilepar
##****************************************************************************par
## Get server table informationpar
##****************************************************************************par
$SqlConnection = New-Object System.Data.SqlClient.SqlConnectionpar
$conn=”Server =  $SQLServer ; Database = $database ; uid = $user ; pwd = $pass ; Integrated Security = SSPI “par
$SqlConnection.ConnectionString = $conn par
$SqlQuery1 = “SELECT server_id, server_name, client_id from server wherestatus_id = 1 “par
$SqlCmd = New-Object System.Data.SqlClient.SqlCommandpar
$SqlCmd.CommandText = $SqlQuery1par
$SqlCmd.Connection = $SqlConnectionpar
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapterpar
$SqlAdapter.SelectCommand = $SqlCmdpar
$DataSet2 = New-Object System.Data.DataSetpar
$srvrfound = $SqlAdapter.Fill($DataSet2) par
$SqlConnection.Close()par
##**************************************************par
##  Loop through each server in the server tablepar
##**************************************************par
$i = 0par
[INT] $jobcnt = 0par
$jobs = @()par
$getprops=$dir+”\GetServerProperties.ps1″par
Foreach ( $item in  $dataSet2.tables[0] | % {$_.server_id})par
par
    {par
        $server_id=$dataset2.tables[0].rows[$i][0]par
        $server_name=$dataset2.tables[0].rows[$i][1]par
        $client_id=$dataset2.tables[0].rows[$i][2]par
        $outbuffer = ” ” | add-content $logfilepar
        $outbuffer = “Server name: ” + $server_name + ” and the Server ID is ” + $server_id | add-content $logfilepar
        $i = $i + 1par
      par
$outbuffer =  $jobs += start-job –FilePath ($getprops) –ArgumentList$server_name,$server_id,$client_id,$logfile, $conn | add-content $logfile par
        $running = @(Get-Job | Where-Object { $_.JobStateInfo.State –eq ‘Running’ })par
        $outbuffer = “running count is “ + $running.count  | add-content $logfilepar
        $outbuffer =”job cnt is “ + $jobcnt | add-content $logfilepar
        $outbuffer = “submitted for “ + $server_name + ‘ ‘ + $server_id + ‘ ‘ + $client_id+ ‘ ‘ + $logfile + ‘ ‘ + $conn | add-content $logfilepar
        $jobcnt = $jobcnt + 1par
        while ( $jobcnt -GT 9  )par
        {par
        sleep 10par
        $outbuffer =  “SLEEPING !!!!!!!!!!!!!!! ” | add-content $logfilepar
        $running = @(Get-Job | Where-Object { $_.JobStateInfo.State –eq ‘Running’ }par
        $jobcnt = $running.count)par
        $outbuffer =  “job count is ” + $jobcnt | add-content $logfilepar
        }par
    }par
        while ( $jobcnt -GT 0  )par
        {par
        sleep 10par
        $outbuffer =  “SLEEPING UNTIL ALL JOBS ARE DONE “| add-content $logfilepar
        $running = @(Get-Job | Where-Object { $_.JobStateInfo.State –eq ‘Running’ }par
        $jobcnt = $running.count)par
        $outbuffer =  “job count is ” + $jobcnt| add-content $logfilepar
        }par
$outbuffer = ” ” | add-content $logfilepar
$outbuffer = “Successful completion of UpdateServerDisk.ps1 “ | add-content $logfilepar
$outbuffer =  get-date | add-content $logfilepar
}#
				
			
Scroll to Top