实际应用中,我们有时需要统计PostgreSQL 的CPU使用率和私有内存。
本文介绍在Windows上使用powershell统计PostgreSQL的方法,并将相关步骤整理为一个脚本。
方案的原理:
PostgreSQL 是多进程模型的数据库。它在运行时,会启动一个名为“pg_ctl”进程和若干个名为“postgres” 的进程。其中,进程pg_ctl是“祖先”进程,它表示数据库处于运行状态,占用的内存很少;其他所有工作进程的名称都是postgres。我们可以根据PostgreSQL的每个进程的CPU使用率和私有内存,计算出PostgreSQL的总CPU使用率和内存。
set-ExecutionPolicy UnRestricted
这表示允许运行powershell 脚本。
$PgServiceName = "postgresql-9.6"
$pgService = Get-WmiObject -class win32_service -filter "name='$PgServiceName'"
这里,$PgServiceName 表示ISC 各应用组件使用的数据库服务名,$pgService 表示该服务对象。
我们可以通过 echo $pgService 查看服务信息。
$PgPathName = (Get-WmiObject -class win32_service -filter "name='$PgServiceName'").pathname
$PgHomeTemp = $PgPathName -replace "\\bin\\pg_ctl\.exe.*", ""
$PgHome = $PgHomeTemp.TrimStart("""")
$PgBinDir = $PgHome + "\bin"
$PgDataDirTemp = $PgPathName -replace ".*"" -D """, ""
$PgDataDir = $PgDataDirTemp.TrimEnd("""")
这里,$PgBinDir即为bin文件夹的路径,$PgDataDir即为data文件夹的路径,它们的信息如下图所示:
$PostgresPath = $PgBinDir + "\postgres.exe"
$PostgresPattern = $PostgresPath -replace "\\", "\\"
$PostgresFilter = "(Name='postgres.exe' and ExecutablePath='" + $PostgresPattern + "')"
这里,$PostgresPath是 postgres.exe的文件路径;$PostgresFilter是过滤器,它是下文中我们获取进程ID的过滤条件,它们信息如下图所示:
$SumNumberOfLogicalProcessors = $((get-wmiobject win32_processor).NumberOfLogicalProcessors | measure-object -sum).sum
查看如下:
$pgPerfProcProgress = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process -filter "(name like 'postgres%')" -property IDProcess,WorkingSetPrivate,PercentProcessorTime
Get-WmiObject 是获取系统对象的命令,而Win32_PerfFormattedData_PerfProc_Process格式化数据类提供从监测运行的应用程序和系统进程的性能计数器预先计算的数据。这里我们无法直接获得ISC各组件数据库的性能信息。下文中我们会过滤出相关的数据。
$PgProcessIDList = $(Get-WmiObject -class win32_process -filter $PostgresFilter -property ProcessId | ForEach-Object { $_.processId })
$SumProperties = ($pgPerfProcProgress| Where-Object {($PgProcessIDList -contains $_.IDProcess)} | measure-object -property PercentProcessorTime,WorkingSetPrivate -sum | ForEach-Object {$_.sum}) #对每一个统计对象,取它的 Sum 属性
$PgPercentProcessorTime = $SumProperties[0] / $SumNumberOfLogicalProcessors
$PgWorkingSetPrivate = $SumProperties[1]/1048576
在$SumProperties = ($pgPerfProcProgress| Where-Object {($PgProcessIDList -contains $_.IDProcess)} | measure-object -property PercentProcessorTime,WorkingSetPrivate -sum | ForEach-Object {$_.sum})
这条命令中,我们从所有postgres.exe 进程的性能信息中,根据进程ID过滤出了属于ISC各组件PostgreSQL数据库的部分,然后分别对CPU使用率和私有内存集求和,得到结果。
查看如下:
而在
$PgPercentProcessorTime = $SumProperties[0] / $SumNumberOfLogicalProcessors
中,$SumProperties[0]表示各进程对各自的逻辑CPU使用率的累加和,而 $SumNumberOfLogicalProcessors是总的逻辑CPU个数,由此,我们得到了CPU的平均使用率。
$PgWorkingSetPrivate = $SumProperties[1]/1048576
$SumProperties[1]单位为字节,而$PgWorkingSetPrivate的单位是MB。
如图,这台服务器上,ISC各组件PostgreSQL数据库的平均CPU使用率是0%,私有内存集大小是18MB。
笔者把上述代码整理为了powershell 脚本,它生成报告pg_cpu_mem.csv。代码如下:
set-ExecutionPolicy UnRestricted -force
$DebugPreference = "inquire"
$CurrentDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$PgServiceName = "postgresql-9.6"
# judge whether these services exist
$pgService = Get-WmiObject -class win32_service -filter "name='$PgServiceName'"
# find home directory of PostgreSQL
# $PgHome: home directory of PostgreSQL
# $PgBinDir: $PgHome/bin directory of PostgreSQL
# $PgDataDir: data of PostgreSQL
if ($pgService) {
$PgPathName = (Get-WmiObject -class win32_service -filter "name='$PgServiceName'").pathname
$PgHomeTemp = $PgPathName -replace "\\bin\\pg_ctl\.exe.*", ""
$PgHome = $PgHomeTemp.TrimStart("""")
$PgBinDir = $PgHome + "\bin"
$PgDataDirTemp = $PgPathName -replace ".*"" -D """, ""
$PgDataDir = $PgDataDirTemp.TrimEnd("""")
$PostgresPath = $PgBinDir + "\postgres.exe"
$PostgresPattern = $PostgresPath -replace "\\", "\\"
$PostgresFilter = "(Name='postgres.exe' and ExecutablePath='" + $PostgresPattern + "')"
}
# Sum Of Logical Processors
# win32_processor: See https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-processor
$SumNumberOfLogicalProcessors = $((get-wmiobject win32_processor).NumberOfLogicalProcessors | measure-object -sum).sum
# Performance Data of processes whose name starts with "postgres"
# Win32_PerfFormattedData_PerfProc_Process: See https://msdn.microsoft.com/zh-cn/windows/desktop/aa394277#MainContent
# This will cost several minutes
$PgPerfProcProgress = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process -filter "(name like 'postgres%')" -property IDProcess,WorkingSetPrivate,PercentProcessorTime
# get two values:
# 1. PgPercentProcessorTime. The Percentage of how much Processor that PostgreSQL used
# 2. PgWorkingSetPrivate. The private working memory set of PostgreSQL
if ($pgService) {
$PgProcessIDList = $(Get-WmiObject -class win32_process -filter $PostgresFilter -property ProcessId | ForEach-Object { $_.processId })
$SumProperties = ($PgPerfProcProgress| Where-Object {($PgProcessIDList -contains $_.IDProcess)} | measure-object -property PercentProcessorTime,WorkingSetPrivate -sum | ForEach-Object {$_.sum})
$PgPercentProcessorTime = $SumProperties[0] / $SumNumberOfLogicalProcessors
$PgWorkingSetPrivate = $SumProperties[1]/1048576
}
$Content = @(
'"pg的CPU使用率 %","pg的专用内存集 MB"'
"$PgPercentProcessorTime, $PgWorkingSetPrivate"
)
$Content | ForEach-Object { Add-Content -Path $CurrentDir\pg_cpu_mem.csv -Value $_ }
Read-Host -Prompt "Press Enter to continue"