【问题标题】:How Can I Make My Powershell Get-WMIObject Script Faster Cycling Through Servers如何使我的 Powershell Get-WMIObject 脚本更快地在服务器中循环
【发布时间】:2013-04-18 03:04:32
【问题描述】:

我正在编写一个脚本,该脚本检查服务器是否 ping,然后检查所有 SQL Server 服务状态并将其存储在一个表中。它还适用于 SQL 实例。但是,我必须检查 70 台服务器,并且运行需要一分钟多的时间。我查看了 AsJob 参数,当我将它添加到所有“Get_WMIObject xxService”命令时,它开始为每个服务返回错误信息。即它开始为所有人返回“运行”状态,所以我怀疑它只是重复之前的数据捕获。尽管它确实只运行了 20 秒。有人可以看看下面并建议我哪里出错了或者我可以做些什么来使服务器服务信息的检索异步?

我认为将所有服务器的服务状态存储在哈希表中并一次性更新 SQL 数据表可能会更好,但事实证明它相当复杂。我觉得我的代码需要彻底重新考虑才能异步运行,但我正在努力避免这种情况!

提前致谢

<#
Tests all Servers for Ping and SQL Server Service Status
#>

# Return the Ping Status
Function GetStatusCode 
{  
    Param([int] $StatusCode)   
    switch($StatusCode) 
    { 
        0         {"Success"} 
        11001   {"Buffer Too Small"} 
        11002   {"Destination Net Unreachable"} 
        11003   {"Destination Host Unreachable"} 
        11004   {"Destination Protocol Unreachable"} 
        11005   {"Destination Port Unreachable"} 
        11006   {"No Resources"} 
        11007   {"Bad Option"} 
        11008   {"Hardware Error"} 
        11009   {"Packet Too Big"} 
        11010   {"Request Timed Out"} 
        11011   {"Bad Request"} 
        11012   {"Bad Route"} 
        11013   {"TimeToLive Expired Transit"} 
        11014   {"TimeToLive Expired Reassembly"} 
        11015   {"Parameter Problem"} 
        11016   {"Source Quench"} 
        11017   {"Option Too Big"} 
        11018   {"Bad Destination"} 
        11032   {"Negotiating IPSEC"} 
        11050   {"General Failure"} 
        default {"Failed"} 
    } 
} 

# Format the Server Up-time
Function GetUpTime 
{ 
    param([string] $LastBootTime) 
    $Uptime = (Get-Date) - [System.Management.ManagementDateTimeconverter]::ToDateTime($LastBootTime) 
    "Days: $($Uptime.Days); Hours: $($Uptime.Hours); Minutes: $($Uptime.Minutes); Seconds: $($Uptime.Seconds)"  
} 

#Main Body
# Populate Table MyDB.dbo.tbl_ServerPingTest

$conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=MyServer; Initial Catalog=MyDB; Integrated Security=SSPI")
$conn.Open()

$cmd = $conn.CreateCommand()
$cmd.CommandText ="DELETE FROM MyDB.dbo.tbl_ServerPingTest
INSERT INTO MyDB.dbo.tbl_ServerPingTest (ServerName, InstanceName)
SELECT ServerName, InstanceName FROM MyDB.dbo.tbl_servers
WHERE ServerCategory <> 'DECOMMED'"
$cmd.ExecuteNonQuery()

$cmd2 = $conn.CreateCommand()
$cmd2.CommandText = "SELECT * FROM MyDB.dbo.tbl_ServerPingTest"
$da = New-Object System.Data.SqlClient.SqlDataAdapter #($cmd2)
$da.SelectCommand = $cmd2
$dt = New-Object System.Data.DataTable
$da.Fill($dt) | Out-Null

# Cycle through Server and Instances and retrieve information
Foreach($row in $dt.rows) 
{ 
    $ServerName = $row.ServerName
    $InstanceName = $row.InstanceName

    $pingStatus = Get-WmiObject -Query "Select * from win32_PingStatus where Address='$ServerName'" 

    $Uptime = $null 
    $SQLServerStatus = $null 
    $SQLAgentStatus = $null 

    # Enter the Loop if a Server is Pingable
    if($pingStatus.StatusCode -eq 0) 
    { 
        # Trap needed for server where Access is Denied causes the SQL Job to fail
        trap {continue}
        $OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName -ErrorAction SilentlyContinue -ErrorVariable wmiResults
        $Uptime = GetUptime( $OperatingSystem.LastBootUpTime ) 

        if ($wmiResults -ne $null)
          {
            $tmperr = "Uptime Info Could Not be Obtained"
            $Uptime = $null
          }
        else
          {
            $tmperr = ""

            filter SvcFilter {
                if ($_.StartMode -eq "Disabled") {$_.StartMode }
                else {$_.State}
                }

            $props="Name","StartMode","State"

            if ($InstanceName -eq 'DEFAULT') 
                {
                    $SQLServerStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLSERVER'" -computer $ServerName | SvcFilter
                    $SQLAgentStatus = Get-WMIObject win32_service -property $props -filter "name='SQLSERVERAGENT'" -computer $ServerName | SvcFilter
                    $RSAgentStatus = Get-WMIObject win32_service -property $props -filter "name='ReportServer'" -computer $ServerName | SvcFilter
                }
                else
                {
                    $NamedInstanceSQLService = "MSSQL$" + $InstanceName
                    $NamedInstanceAgentService = "SQLAgent$" + $InstanceName
                    $NamedInstanceRSService = "ReportServer$" + $InstanceName
                    $SQLServerStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceSQLService} | SvcFilter
                    $SQLAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceAgentService} | SvcFilter
                    $RSAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceRSService} | SvcFilter
                }

            $ASAgentStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLServerOLAPService'" -computer $ServerName | SvcFilter

          }
    } 

    $IPAddress = $pingStatus.IPV4Address
    $PingTest = GetStatusCode( $pingStatus.StatusCode ) 
    $ErrMSG = $tmperr

    # Update Table MyDB.dbo.tbl_ServerPingTest with all retreived information 
    $updateRow = $dt.Select("ServerName = '$ServerName' AND InstanceName = '$InstanceName'")
    $updateRow[0].IPAddress = $IPAddress
    $updateRow[0].PingTest = $PingTest
    $updateRow[0].ErrMSG = $ErrMSG
    $updateRow[0].Uptime = $Uptime
    $updateRow[0].SQLServerStatus = $SQLServerStatus
    $updateRow[0].SQLAgentStatus = $SQLAgentStatus   
    $updateRow[0].RSAgentStatus = $RSAgentStatus 
    $updateRow[0].ASAgentStatus = $ASAgentStatus 

    $cmdUpd = $conn.CreateCommand()
    $cmdUpd.CommandText = "UPDATE MyDB.dbo.tbl_ServerPingTest
    SET IPAddress = @IPAddress, PingTest = @PingTest, ErrMSG = @ErrMSG, Uptime = @Uptime, SQLServerStatus = @SQLServerStatus, SQLAgentStatus = @SQLAgentStatus, RSAgentStatus = @RSAgentStatus, ASAgentStatus = @ASAgentStatus
    WHERE ServerName = @ServerName AND InstanceName = @InstanceName"

    # Add parameters to pass values to the UPDATE statement
    $cmdUpd.Parameters.Add("@ServerName", "nvarchar", 50, "ServerName") | Out-Null
    $cmdUpd.Parameters["@ServerName"].SourceVersion = "Original"
    $cmdUpd.Parameters.Add("@InstanceName", "nvarchar", 50, "InstanceName") | Out-Null
    $cmdUpd.Parameters["@InstanceName"].SourceVersion = "Original"
    $cmdUpd.Parameters.Add("@IPAddress", "nvarchar", 50, "IPAddress") | Out-Null
    $cmdUpd.Parameters["@IPAddress"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@PingTest", "nvarchar", 50, "PingTest") | Out-Null
    $cmdUpd.Parameters["@PingTest"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@ErrMSG", "nvarchar", 50, "ErrMSG") | Out-Null
    $cmdUpd.Parameters["@ErrMSG"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@Uptime", "nvarchar", 50, "Uptime") | Out-Null
    $cmdUpd.Parameters["@Uptime"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@SQLServerStatus", "nvarchar", 50, "SQLServerStatus") | Out-Null
    $cmdUpd.Parameters["@SQLServerStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@SQLAgentStatus", "nvarchar", 50, "SQLAgentStatus") | Out-Null
    $cmdUpd.Parameters["@SQLAgentStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@RSAgentStatus", "nvarchar", 50, "RSAgentStatus") | Out-Null
    $cmdUpd.Parameters["@RSAgentStatus"].SourceVersion = "Current"
    $cmdUpd.Parameters.Add("@ASAgentStatus", "nvarchar", 50, "ASAgentStatus") | Out-Null
    $cmdUpd.Parameters["@ASAgentStatus"].SourceVersion = "Current"

    # Set the UpdateCommand property
    $da.UpdateCommand = $cmdUpd

    # Update the database
    $RowsUpdated = $da.Update($dt)


} 

$conn.Close()

我尝试将 -AsJob 参数添加到每个 Get-WMIObject 行

$SQLServerStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceSQLService} -AsJob | SvcFilter

但我没有在代码中的任何地方使用 Receive-Job。我注意到每个变量都会返回“运行”状态,即使它是“停止”或“禁用”。所以我猜测 Job 的结果没有被正确捕获,它实际上是返回之前 Get-WMIObject 调用的输出(其中大部分将处于“运行”状态)。

【问题讨论】:

    标签: sql sql-server performance sql-server-2008 powershell


    【解决方案1】:

    get-wmiobject 有一个 -asjob 参数,但 where-object 没有。

    此外,当您使用 -asjob 时,返回值是一个“作业对象”,而不是由 get-wmiobject 调用返回的对象。

    一旦在远程服务器上完成,您可以使用receive-job cmdlet 从每个作业中获取实际对象(您可以使用wait-jobget-job 来帮助了解何时完成)。

    我建议您发送get-help about_Remote_Jobs 以了解有关如何构建代码的背景知识。

    【讨论】:

      【解决方案2】:

      如果你的 get-wmiObject wants a shorter timeoutget-wmiCustom function 可能会有所帮助。

      【讨论】:

        【解决方案3】:

        我设法通过使用 Start-Job 重新编写来做到这一点,并将其从 80 秒减少到 20 秒。如果有人对如何提高效率有任何进一步的建议,我将不胜感激。我正在考虑一次性而不是一个一个地检索所有 WMIObjects?任何人都可以建议如何?

        #Main Body
        # Populate Table MyDB.dbo.tbl_ServerPingTest
        
        $conn = New-Object System.Data.SqlClient.SqlConnection("Data Source=MyServer; Initial Catalog=MyDB; Integrated Security=SSPI")
        $conn.Open()
        
        $cmd = $conn.CreateCommand()
        $cmd.CommandText ="DELETE FROM MyDB.dbo.tbl_ServerPingTest
        INSERT INTO MyDB.dbo.tbl_ServerPingTest (ServerName, InstanceName)
        SELECT ServerName, InstanceName FROM MyDB.dbo.tbl_servers
        WHERE ServerCategory <> 'DECOMMED'"
        $cmd.ExecuteNonQuery()
        
        $cmd2 = $conn.CreateCommand()
        $cmd2.CommandText = "SELECT * FROM MyDB.dbo.tbl_ServerPingTest"
        $da = New-Object System.Data.SqlClient.SqlDataAdapter #($cmd2)
        $da.SelectCommand = $cmd2
        $dt = New-Object System.Data.DataTable
        $da.Fill($dt) | Out-Null
        
        # Create list
        $serverlist = @()
        
        $sb = {
            param ([string] $ServerName, [string] $InstanceName)
        
            # Return the Ping Status
            Function GetStatusCode 
            {  
                Param([int] $StatusCode)   
                switch($StatusCode) 
                { 
                    0         {"Success"} 
                    11001   {"Buffer Too Small"} 
                    11002   {"Destination Net Unreachable"} 
                    11003   {"Destination Host Unreachable"} 
                    11004   {"Destination Protocol Unreachable"} 
                    11005   {"Destination Port Unreachable"} 
                    11006   {"No Resources"} 
                    11007   {"Bad Option"} 
                    11008   {"Hardware Error"} 
                    11009   {"Packet Too Big"} 
                    11010   {"Request Timed Out"} 
                    11011   {"Bad Request"} 
                    11012   {"Bad Route"} 
                    11013   {"TimeToLive Expired Transit"} 
                    11014   {"TimeToLive Expired Reassembly"} 
                    11015   {"Parameter Problem"} 
                    11016   {"Source Quench"} 
                    11017   {"Option Too Big"} 
                    11018   {"Bad Destination"} 
                    11032   {"Negotiating IPSEC"} 
                    11050   {"General Failure"} 
                    default {"Failed"} 
                } 
            }
        
            # Format the Server Up-time
            Function GetUpTime 
            { 
                param([string] $LastBootTime) 
                $Uptime = (Get-Date) - [System.Management.ManagementDateTimeconverter]::ToDateTime($LastBootTime) 
                "Days: $($Uptime.Days); Hours: $($Uptime.Hours); Minutes: $($Uptime.Minutes); Seconds: $($Uptime.Seconds)"  
            } 
        
            try {
        
                # Fetch IP
                $pingStatus = Get-WmiObject -Query "Select * from win32_PingStatus where Address='$ServerName'"
        
                $Uptime = $null 
                $SQLServerStatus = $null 
                $SQLAgentStatus = $null 
        
                # Enter the Loop if a Server is Pingable
                if($pingStatus.StatusCode -eq 0) 
                { 
                    # Trap needed for server where Access is Denied causes the SQL Job to fail
                    trap {continue}
                    $OperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ServerName -ErrorAction SilentlyContinue -ErrorVariable wmiResults
                    $Uptime = GetUptime( $OperatingSystem.LastBootUpTime ) 
        
                    if ($wmiResults -ne $null)
                      {
                        $tmperr = "Uptime Info Could Not be Obtained"
                        $Uptime = $null
                      }
                    else
                      {
                        $tmperr = ""
        
                        filter SvcFilter {
                            if ($_.StartMode -eq "Disabled") {$_.StartMode }
                            else {$_.State}
                            }
        
                        $props="Name","StartMode","State"
        
                        if ($InstanceName -eq 'DEFAULT') 
                            {
                                #Write-Host $ServerName + '\' + $InstanceName 
                                $SQLServerStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLSERVER'" -computer $ServerName | SvcFilter
                                #Write-Host $SQLServerStatus
                                $SQLAgentStatus = Get-WMIObject win32_service -property $props -filter "name='SQLSERVERAGENT'" -computer $ServerName | SvcFilter
                                #Write-Host $SQLAgentStatus
                                $RSAgentStatus = Get-WMIObject win32_service -property $props -filter "name='ReportServer'" -computer $ServerName | SvcFilter
                                #Write-Host $RSAgentStatus
                            }
                            else
                            {
                                #Write-Host $ServerName + '\' + $InstanceName 
                                $NamedInstanceSQLService = "MSSQL$" + $InstanceName
                                $NamedInstanceAgentService = "SQLAgent$" + $InstanceName
                                $NamedInstanceRSService = "ReportServer$" + $InstanceName
                                $SQLServerStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceSQLService} | SvcFilter
                                $SQLAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceAgentService} | SvcFilter
                                $RSAgentStatus = Get-WMIObject win32_service -property $props -computer $ServerName | where {$_.name -eq $NamedInstanceRSService} | SvcFilter
                            }
        
                        $ASAgentStatus = Get-WMIObject win32_service -property $props -filter "name='MSSQLServerOLAPService'" -computer $ServerName | SvcFilter
        
                      }
                } 
        
                $IPAddress = $pingStatus.IPV4Address
                $PingTest = GetStatusCode( $pingStatus.StatusCode ) 
                $ErrMSG = $tmperr
        
                # Save info about server
                $serverInfo = New-Object -TypeName PSObject -Property @{
                ServerName = $ServerName
                InstanceName = $InstanceName
                IPAddress = $IPAddress
                PingTest = $PingTest
                ErrMSG = $ErrMSG
                Uptime = $Uptime
                SQLServerStatus = $SQLServerStatus
                SQLAgentStatus = $SQLAgentStatus   
                RSAgentStatus = $RSAgentStatus 
                ASAgentStatus = $ASAgentStatus 
        
                }
                return $serverInfo
            } catch {
                throw 'Failed to process server named {0}. The error was "{1}".' -f $ServerName, $_
            }
        }
        
        # Loop servers
        $max = 5
        $jobs = @()
        foreach($row in $dt.rows) {
            #$InstanceName = "DEFAULT"
            $server = $row.ServerName
            $InstanceName = $row.InstanceName
            $jobs += Start-Job -ScriptBlock $sb -ArgumentList @($server, $InstanceName) 
            $running = @($jobs | ? {$_.State -eq 'Running'})
        
            # Throttle jobs.
            while ($running.Count -ge $max) {
                $finished = Wait-Job -Job $jobs -Any
                $running = @($jobs | ? {$_.State -eq 'Running'})
            }
        }
        
        # Wait for remaining.
        Wait-Job -Job $jobs > $null
        
        # Check for failed jobs.
        $failed = @($jobs | ? {$_.State -eq 'Failed'})
        if ($failed.Count -gt 0) {
            $failed | % {
                $_.ChildJobs[0].JobStateInfo.Reason.Message
            }
        }
        
        # Collect job data.
        $jobs | % {
            $serverlist += $_ | Receive-Job | Select-Object ServerName,InstanceName,IPAddress,PingTest,ErrMSG,Uptime,SQLServerStatus,SQLAgentStatus,RSAgentStatus,ASAgentStatus
        }
        
        #$serverlist
        
        Foreach($row in $serverlist) 
        { 
            $ServerName = $row.ServerName
            $InstanceName = $row.InstanceName
        
            #$dt.Select("ServerName = '$ServerName' AND InstanceName = '$InstanceName'")
        
            $IPAddress = $row.IPAddress
            $PingTest = $row.PingTest
            $ErrMSG = $row.ErrMSG
            $Uptime = $row.Uptime
            $SQLServerStatus = $row.SQLServerStatus
            $SQLAgentStatus = $row.SQLAgentStatus   
            $RSAgentStatus = $row.RSAgentStatus 
            $ASAgentStatus = $row.ASAgentStatus 
        
        
           # Write-Host $ServerName
           # Write-Host $InstanceName
        
            $updateRow = $dt.Select("ServerName = '$ServerName' AND InstanceName = '$InstanceName'")
            $updateRow[0].IPAddress = $IPAddress
            $updateRow[0].PingTest = $PingTest
            $updateRow[0].ErrMSG = $ErrMSG
            $updateRow[0].Uptime = $Uptime
            $updateRow[0].SQLServerStatus = $SQLServerStatus
            $updateRow[0].SQLAgentStatus = $SQLAgentStatus   
            $updateRow[0].RSAgentStatus = $RSAgentStatus 
            $updateRow[0].ASAgentStatus = $ASAgentStatus 
        
            $cmdUpd = $conn.CreateCommand()
            $cmdUpd.CommandText = "UPDATE MyDB.dbo.tbl_ServerPingTest
            SET IPAddress = @IPAddress, PingTest = @PingTest, ErrMSG = @ErrMSG, Uptime = @Uptime, SQLServerStatus = @SQLServerStatus, SQLAgentStatus = @SQLAgentStatus, RSAgentStatus = @RSAgentStatus, ASAgentStatus = @ASAgentStatus
            WHERE ServerName = @ServerName AND InstanceName = @InstanceName"
        
            # Add parameters to pass values to the UPDATE statement
            $cmdUpd.Parameters.Add("@ServerName", "nvarchar", 50, "ServerName") | Out-Null
            $cmdUpd.Parameters["@ServerName"].SourceVersion = "Original"
            $cmdUpd.Parameters.Add("@InstanceName", "nvarchar", 50, "InstanceName") | Out-Null
            $cmdUpd.Parameters["@InstanceName"].SourceVersion = "Original"
            $cmdUpd.Parameters.Add("@IPAddress", "nvarchar", 50, "IPAddress") | Out-Null
            $cmdUpd.Parameters["@IPAddress"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@PingTest", "nvarchar", 50, "PingTest") | Out-Null
            $cmdUpd.Parameters["@PingTest"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@ErrMSG", "nvarchar", 50, "ErrMSG") | Out-Null
            $cmdUpd.Parameters["@ErrMSG"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@Uptime", "nvarchar", 50, "Uptime") | Out-Null
            $cmdUpd.Parameters["@Uptime"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@SQLServerStatus", "nvarchar", 50, "SQLServerStatus") | Out-Null
            $cmdUpd.Parameters["@SQLServerStatus"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@SQLAgentStatus", "nvarchar", 50, "SQLAgentStatus") | Out-Null
            $cmdUpd.Parameters["@SQLAgentStatus"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@RSAgentStatus", "nvarchar", 50, "RSAgentStatus") | Out-Null
            $cmdUpd.Parameters["@RSAgentStatus"].SourceVersion = "Current"
            $cmdUpd.Parameters.Add("@ASAgentStatus", "nvarchar", 50, "ASAgentStatus") | Out-Null
            $cmdUpd.Parameters["@ASAgentStatus"].SourceVersion = "Current"
        
            # Set the UpdateCommand property
            $da.UpdateCommand = $cmdUpd
        
            # Update the database
            $RowsUpdated = $da.Update($dt)
        
        }
        
        $conn.Close()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-01-06
          • 2014-11-10
          • 1970-01-01
          • 1970-01-01
          • 2011-05-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多