寫在前面 當管理多台Windows Server伺服器時(無論是DB、AD、WEB以及其他的應用伺服器),當出現性能或其他問題後,參閱性能計數器都是一個非常好的維度從而推測出問題可能出現的原因,再不濟也能縮小需要考慮的問題範圍,因此定期收集每一臺伺服器的計數器就會使得問題有據可循。並且收集到的數據也 ...
寫在前面
當管理多台Windows Server伺服器時(無論是DB、AD、WEB以及其他的應用伺服器),當出現性能或其他問題後,參閱性能計數器都是一個非常好的維度從而推測出問題可能出現的原因,再不濟也能縮小需要考慮的問題範圍,因此定期收集每一臺伺服器的計數器就會使得問題有據可循。並且收集到的數據也可以作為BaseLine,即使沒有出現問題也可以預先判斷一些問題。
之前看到網上的大多數收集性能計數器的文章都比較局限,一般是只收集單台伺服器,因此我分享一個多伺服器的寫法。
至於為什麼使用PowerShell,因為在微軟系產品來說像Python等腳本語言雖然有豐富的開源代碼沒有太好的對應介面,而PowerShell每一個微軟自己的產品都提供了大量的Cmdlet,調用起來甚是方便:-)
核心Cmdlet
獲取性能計數器的核心cmdlet就是Get-Counter了,該Get-Counter主要使用兩個參數,分別為要獲取的電腦名稱-ComputerName與性能計數器列表-Counter,這裡要註意的是,獲取性能計數器需要在被獲取伺服器有對應許可權(Performance Monitor Users組),我這裡的例子是使用域管理員帳號收集域內伺服器,因此不考慮許可權問題。
圖1.獲取到的遠程伺服器性能計數器
然後將獲取到的結果保存到變數中,如圖2所示。
圖2.將計數器結果保存到變數中
收集多台伺服器的多個計數器
將所需收集的伺服器以及所需收集的計數器保存到記事本內,方便隨時添加或減少伺服器或者計數器,記事本寫法如下:
圖3.計數器與伺服器配置
在PowerShell中使用Get-Content讀取配置文件內容,如下:
$currentPath=Split-Path ((Get-Variable MyInvocation -Scope 0).Value).MyCommand.Path
#讀取需要收集的性能計數器列表
$ServerNeedScan=get-content $currentPath\ServerNeedScan.txt
$ServerNeedScanArray=$ServerNeedScan.Split(",")
#讀取需要收集的性能計數器
$PerfCounter=get-content $currentPath\PerfmonCounter.txt
$PerfCounterArray=$PerfCounter.Split(",")
代碼1.讀取伺服器列表和計數器列表
現在由於收集的計數器中部分計數器是關於SQL Server的,而部分伺服器可能帶有實例名稱,而對於帶有SQL Server實例名稱的計數器需要把實例和及其名分開,然後把計數器名稱中實例名部分進行替換,代碼如下:
foreach ($fcomputer in $ServerNeedScanArray)
{
if($fcomputer.Trim() -eq "")
{
continue
}
#檢查是否為預設實例
$computer=""
if($fcomputer -like "*\*")
{
$instanceName=$fcomputer.Substring($fcomputer.IndexOf("\")+1,$fcomputer.Length-$fcomputer.IndexOf("\")-1)
$computer=$fcomputer.Substring(0,$fcomputer.IndexOf('\'))
}
else
{
$computer=$fcomputer
$instanceName=""
}
#遍歷所有計數器
$fullCounter=@()
foreach($counter in $PerfCounterArray)
{
$c=""
$c+="\"
$c+=$counter
$fullCounter+=$c
}
$NoDeaultInstanceName="MSSQL`$"
$NoDeaultInstanceName+=$instanceName
#如果是預設實例
if($instanceName -eq "")
{
$fullCounter | % {
if($_ -like "*network*")
{
$finalCounter+= $_.ToLower()
}
else
{
$finalCounter+= $_.ToLower().Replace("*","_total")
}
}
}
#如果是非預設實例
else
{
$a=$fullCounter | % {
if($_ -like "*network*")
{
$finalCounter+= $_.ToLower().Replace("sqlserver", $NoDeaultInstanceName)
}
else
{
$finalCounter+= $_.ToLower().Replace("*","_total").Replace("sqlserver", $NoDeaultInstanceName)
}
}
}
代碼2.替換SQL Server計數器中的非預設實例
將結果插入一臺SQL Server
上述情況就已經準備好了計數器和伺服器名稱,現在就可以將這些數據插入到一臺集中的SQL Server伺服器,代碼如下:
$a=(Get-Counter -ComputerName $computer -Counter $finalCounter).CounterSamples |Select-Object Path,CookedValue
$InsertSQL=""
$curentTime=Get-Date
foreach($PerformanceCounter in $a)
{
$realvalue=$PerformanceCounter.CookedValue
$InsertSQL+="INSERT INTO PerfCounter(instancename,event_timestamp,Counter,CounterValue)
VALUES(''"+$fcomputer+"'',''"+$curentTime+"'',''"+$PerformanceCounter.Path+"'',''"+$realvalue.ToString()+"'');"
}
$connectionString3="data source=伺服器IP;database=test;uid=perf_writer;pwd=123123;"
$conn2=new-object system.Data.SqlClient.SqlConnection($connectionString3)
$conn2.open()
$cmd2=$conn2.CreateCommand()
$cmd2.CommandText=$InsertSQL
$cmd2.ExecuteNonQuery()
$conn2.Close()
代碼3.讀取計數器後插入SQL Server
現在,讀取一臺伺服器並將計數器記錄到資料庫中的代碼就寫好了,並且已經可以靈活配置需要讀取的計數器和機器名。
多線程讀取
如果需要記錄計數器的伺服器比較多時,那麼迴圈遍歷每一臺伺服器就會花費比較長的時間,因此需要多線程來加快這一個速度,在PowerShell中,啟用多線程的cmdlet是start-job,我們首先需要將代碼2和代碼3的腳本封裝到一個script block中,並設置可傳入的參數,如代碼4。
$sb = [scriptblock]::Create('
param($instanceName,$NoDeaultInstanceName,$fullCounter,$fcomputer,$computer)
#這裡寫其他代碼
')
#開始非同步線程,並傳入參數
start-job -scriptblock $sb -Argument $instanceName,$NoDeaultInstanceName,$fullCounter,$fcomputer,$computer
代碼4.利用非同步線程讀取計數器數據並插入SQL Server
經過測試,PowerShell對同時可以併發的線程做了限制,這個限制很奇怪,我在每台伺服器上測試的結果並不相同,因此如果同時全部併發執行這些線程,某些線程會因為限制而不起作用,因此如果需要記錄性能計數器的伺服器比較多的話,會丟失一部分伺服器信息,我的解決辦法是限制同時併發的進程數量,如果進程數量超過規定數值,則等待1秒再次檢測,如果檢測通過再啟動新進程,代碼如代碼5所示。
While (@(Get-Job | Where { $_.State -eq "Running" }).Count -gt 5) {
Write-host "Waiting for background jobs..."
Start-Sleep -Seconds 1
}
代碼5.檢測處於“運行中”進程的數量是否大於5
定期執行腳本
現在,上面腳本就可以收集多台伺服器的性能計數器,並將結果保存到SQL Server了,現在只需要定期(比如2分鐘一次)執行該腳本即可。使用Windows計劃任務是定期執行PowerShell腳本推薦的方式,如圖4所示。
圖4.使用計劃任務2分鐘收集一次性能計數器信息
在圖4中,我們註意到使用了-NonInteractive參數,該參數用於在執行時,不彈出PowerShell視窗。
結果
現在,我們可以看到收集後的性能計數器信息,如圖5所示。
圖5.收集到的性能計數器信息
有了上述性能計數器信息,我們可以使用一些可視化工具分析這些信息,比如我將數據導入到ElasticSearch中,出幾張簡單的報表,如圖6所示。
圖6.使用這些性能計數器出簡單的報表
這些報表可以幫助我們直觀的看出一些問題,比如圖6中的forward record可以看到,某些實例大量缺少聚集索引,或者下麵的Top Lock Wait可以看到某些實例定期會產生大量的鎖阻塞,從而我們可以更容易提前發現問題,進行解決。
小結
定期收集一