实际应用中,我们有时需要统计PostgreSQL 的CPU使用率和私有内存。
本文介绍在Windows上使用powershell统计PostgreSQL的方法,并将相关步骤整理为一个脚本。
方案的原理:
PostgreSQL 是多进程模型的数据库。它在运行时,会启动一个名为“pg_ctl”进程和若干个名为“postgres” 的进程。其中,进程pg_ctl是“祖先”进程,它表示数据库处于运行状态,占用的内存很少;其他所有工作进程的名称都是postgres。我们可以根据PostgreSQL的每个进程的CPU使用率和私有内存,计算出PostgreSQL的总CPU使用率和内存。
- 打开powershell 控制台,输入:
set-ExecutionPolicy UnRestricted
这表示允许运行powershell 脚本。
- 定义服务名。输入下面的内容:
$PgServiceName = "postgresql-9.6"
$pgService = Get-WmiObject -class win32_service -filter "name='$PgServiceName'"
这里,$PgServiceName 表示ISC 各应用组件使用的数据库服务名,$pgService 表示该服务对象。
我们可以通过 echo $pgService 查看服务信息。
- 获取PostgreSQL数据库安装目录下的bin 文件夹和data文件夹路径,前者中存放postgresql的工具,而后者中存放相关数据。输入下面的内容:
$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文件夹的路径,它们的信息如下图所示:
- 获取postgres.exe 程序的路径和过滤器。
$PostgresPath = $PgBinDir + "\postgres.exe"
$PostgresPattern = $PostgresPath -replace "\\", "\\"
$PostgresFilter = "(Name='postgres.exe' and ExecutablePath='" + $PostgresPattern + "')"
这里,$PostgresPath是 postgres.exe的文件路径;$PostgresFilter是过滤器,它是下文中我们获取进程ID的过滤条件,它们信息如下图所示:
- 计算逻辑CPU 的个数:
$SumNumberOfLogicalProcessors = $((get-wmiobject win32_processor).NumberOfLogicalProcessors | measure-object -sum).sum
查看如下:
- 获取所有系统中所有postgres.exe 进程的性能信息,进程ID(IDProcess),私有内存工作集(WorkingSetPrivate)的大小和CPU使用率(PercentProcessorTime,表示的是单个进程对单个逻辑CPU的使用率):
$pgPerfProcProgress = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process -filter "(name like 'postgres%')" -property IDProcess,WorkingSetPrivate,PercentProcessorTime
Get-WmiObject 是获取系统对象的命令,而Win32_PerfFormattedData_PerfProc_Process格式化数据类提供从监测运行的应用程序和系统进程的性能计数器预先计算的数据。这里我们无法直接获得ISC各组件数据库的性能信息。下文中我们会过滤出相关的数据。
- 获取PostgreSQL进程的ID:
$PgProcessIDList = $(Get-WmiObject -class win32_process -filter $PostgresFilter -property ProcessId | ForEach-Object { $_.processId })
- 统计PostgreSQL数据库的平均CPU使用率(%)和总私有内存(MB):
$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"