Skip to content

PostgreSQL 进程监控:使用标准 Unix 工具

概述

PostgreSQL 数据库服务器在运行时会创建多个进程来处理不同的任务。通过标准的 Unix 工具(主要是 ps 命令),我们可以有效地监控这些进程的状态,了解数据库的运行情况。这对于数据库管理员进行故障排查、性能优化和系统监控至关重要。

PostgreSQL 进程架构

使用 ps 命令监控 PostgreSQL 进程

基本命令格式

bash
# 查看所有 PostgreSQL 相关进程
ps auxww | grep ^postgres

# 或者使用更精确的过滤
ps -ef | grep postgres | grep -v grep

为什么使用 auxww 参数?

  • a:显示所有用户的进程
  • u:以用户友好的格式显示
  • x:显示没有控制终端的进程
  • ww:不限制输出宽度,显示完整的命令行 :::

完整示例分析

让我们看一个实际的输出示例:

bash
$ ps auxww | grep ^postgres
postgres  15551  0.0  0.1  57536  7132 pts/0    S    18:02   0:00 postgres -i
postgres  15554  0.0  0.0  57536  1184 ?        Ss   18:02   0:00 postgres: background writer
postgres  15555  0.0  0.0  57536   916 ?        Ss   18:02   0:00 postgres: checkpointer
postgres  15556  0.0  0.0  57536   916 ?        Ss   18:02   0:00 postgres: walwriter
postgres  15557  0.0  0.0  58504  2244 ?        Ss   18:02   0:00 postgres: autovacuum launcher
postgres  15582  0.0  0.0  58772  3080 ?        Ss   18:04   0:00 postgres: joe runbug 127.0.0.1 idle
postgres  15606  0.0  0.0  58772  3052 ?        Ss   18:07   0:00 postgres: tgl regression [local] SELECT waiting
postgres  15610  0.0  0.0  58772  3056 ?        Ss   18:07   0:00 postgres: tgl regression [local] idle in transaction

进程类型详细分析

进程类型PID描述作用
主进程15551postgres -i负责启动和管理所有其他进程
后台写入器15554background writer定期将脏页写入磁盘,减少检查点压力
检查点进程15555checkpointer执行检查点操作,确保数据持久性
WAL 写入器15556walwriter将预写日志(WAL)缓冲区写入磁盘
自动清理15557autovacuum launcher启动自动清理作业,维护表统计信息
客户端连接15582joe runbug 127.0.0.1 idle处理用户 joe 的连接,当前空闲
客户端连接15606tgl regression [local] SELECT waiting执行 SELECT,等待锁
客户端连接15610tgl regression [local] idle in transaction事务中空闲状态

客户端连接进程格式解析

命令行格式

PostgreSQL 为每个客户端连接进程使用以下格式:

postgres: user database host activity

格式组件说明

活动状态指示器

状态含义示例场景
idle空闲,等待客户端命令连接建立后,等待 SQL 语句
idle in transaction事务中空闲执行 BEGIN 后,等待下一条 SQL
SELECT正在执行 SELECT 查询复杂查询正在处理中
INSERT正在执行 INSERT 操作数据插入操作进行中
UPDATE正在执行 UPDATE 操作数据更新操作进行中
DELETE正在执行 DELETE 操作数据删除操作进行中
waiting等待锁释放被其他事务阻塞

实际业务场景示例

场景 1:正常的应用连接

bash
# 应用服务器连接
postgres: webapp myapp 192.168.1.100 idle

解释:

  • 用户:webapp(应用程序用户)
  • 数据库:myapp(应用数据库)
  • 主机:192.168.1.100(应用服务器 IP)
  • 状态:idle(等待下一个查询)

场景 2:报表查询

bash
# 长时间运行的报表查询
postgres: analyst reporting 192.168.1.50 SELECT

解释:

  • 用户:analyst(分析师用户)
  • 数据库:reporting(报表数据库)
  • 主机:192.168.1.50(分析师工作站)
  • 状态:SELECT(正在执行复杂查询)

场景 3:锁等待问题

bash
# 两个相互竞争的事务
postgres: user1 mydb 127.0.0.1 UPDATE waiting
postgres: user2 mydb 127.0.0.1 idle in transaction

问题分析:

  • user1 正在执行 UPDATE 操作,但被阻塞(waiting)
  • user2 在事务中处于空闲状态,可能持有锁
  • 需要检查锁依赖关系

集群名称显示

配置集群名称

sql
-- 查看当前集群名称
SHOW cluster_name;

-- 设置集群名称(需要重启)
ALTER SYSTEM SET cluster_name = 'production_db';
SELECT pg_reload_conf();

带集群名称的进程显示

bash
$ psql -c 'SHOW cluster_name'
 cluster_name
--------------
 server1
(1 row)

$ ps aux | grep server1
postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: server1: background writer
postgres   27094  0.0  0.0  30096  1024 ?        Ss   11:34   0:00 postgres: server1: checkpointer
postgres   27095  0.0  0.0  30096  1024 ?        Ss   11:34   0:00 postgres: server1: walwriter

集群名称的作用 在多数据库集群环境中,集群名称帮助管理员快速识别进程属于哪个 PostgreSQL 实例,特别是在同一台服务器运行多个 PostgreSQL 实例时非常有用。 :::

锁等待检测与分析

识别锁等待

当看到进程状态为 waiting 时,表示该进程正在等待锁:

bash
postgres: tgl regression [local] SELECT waiting

查找锁阻塞关系

sql
-- 查看当前锁状态
SELECT
    blocked_locks.pid AS blocked_pid,
    blocked_activity.usename AS blocked_user,
    blocking_locks.pid AS blocking_pid,
    blocking_activity.usename AS blocking_user,
    blocked_activity.query AS blocked_statement,
    blocking_activity.query AS current_statement_in_blocking_process
FROM pg_catalog.pg_locks blocked_locks
    JOIN pg_catalog.pg_stat_activity blocked_activity
        ON blocked_activity.pid = blocked_locks.pid
    JOIN pg_catalog.pg_locks blocking_locks
        ON blocking_locks.locktype = blocked_locks.locktype
        AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database
        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
        AND blocking_locks.pid != blocked_locks.pid
    JOIN pg_catalog.pg_stat_activity blocking_activity
        ON blocking_activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

锁等待处理流程

进程标题更新配置

update_process_title 参数

sql
-- 查看当前设置
SHOW update_process_title;

-- 关闭进程标题更新(可以提高性能)
ALTER SYSTEM SET update_process_title = off;
SELECT pg_reload_conf();

性能考虑

平台开启 update_process_title关闭 update_process_title
Linux轻微性能影响无影响,但失去实时状态
Solaris明显性能影响显著性能提升
BSD 系统中等性能影响性能提升

关闭进程标题更新的影响

  • 优点:减少系统调用开销,可能提升性能
  • 缺点:失去实时的活动状态指示,难以监控当前执行的操作 :::

平台特殊注意事项

Solaris 系统

在 Solaris 系统上使用 ps 命令需要特殊处理:

bash
# 错误的用法(不要使用)
/bin/ps -ef | grep postgres

# 正确的用法
/usr/ucb/ps auxww | grep postgres

Solaris 注意事项

  1. 必须使用 /usr/ucb/ps 而不是 /bin/ps
  2. 必须使用两个 w 标志(ww
  3. 启动 postgres 命令时的显示必须比每个服务器进程的 ps 状态显示更短
  4. 如果不满足以上三个条件,ps 输出将显示原始的 postgres 命令行而不是有用的状态信息 :::

监控脚本示例

基础监控脚本

bash
#!/bin/bash
# postgresql_monitor.sh - PostgreSQL 进程监控脚本

echo "=== PostgreSQL 进程监控报告 ===="
echo "时间: $(date)"
echo

echo "=== 主要进程状态 ==="
ps auxww | grep "postgres:" | grep -v grep | while read line; do
    # 提取进程信息
    pid=$(echo $line | awk '{print $2}')
    cpu=$(echo $line | awk '{print $3}')
    mem=$(echo $line | awk '{print $4}')
    process_info=$(echo $line | awk '{for(i=11;i<=NF;i++) printf "%s ", $i; print ""}')

    echo "PID: $pid | CPU: $cpu% | MEM: $mem% | $process_info"
done

echo
echo "=== 活跃连接统计 ==="
ps auxww | grep "postgres:" | grep -v "background\|checkpointer\|walwriter\|autovacuum" | grep -v grep | wc -l | awk '{print "活跃连接数: " $1}'

echo
echo "=== 等待锁的进程 ==="
ps auxww | grep "postgres:" | grep "waiting" | grep -v grep | while read line; do
    pid=$(echo $line | awk '{print $2}')
    process_info=$(echo $line | awk '{for(i=11;i<=NF;i++) printf "%s ", $i; print ""}')
    echo "⚠️  PID: $pid - $process_info"
done

echo
echo "=== 长时间运行的事务 ==="
ps auxww | grep "postgres:" | grep "idle in transaction" | grep -v grep | while read line; do
    pid=$(echo $line | awk '{print $2}')
    start_time=$(echo $line | awk '{print $9}')
    process_info=$(echo $line | awk '{for(i=11;i<=NF;i++) printf "%s ", $i; print ""}')
    echo "⏰ PID: $pid | 开始时间: $start_time - $process_info"
done

高级监控脚本

bash
#!/bin/bash
# advanced_pg_monitor.sh - 高级 PostgreSQL 监控

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 阈值设置
MAX_CONNECTIONS=100
HIGH_CPU_THRESHOLD=80
HIGH_MEM_THRESHOLD=80

echo -e "${BLUE}=== PostgreSQL 高级监控报告 ===${NC}"
echo "时间: $(date)"
echo

# 检查服务状态
if pgrep postgres > /dev/null; then
    echo -e "${GREEN}✅ PostgreSQL 服务运行中${NC}"
else
    echo -e "${RED}❌ PostgreSQL 服务未运行${NC}"
    exit 1
fi

# 主进程信息
echo -e "\n${BLUE}=== 主进程信息 ===${NC}"
main_process=$(ps auxww | grep "postgres.*-" | grep -v grep | head -1)
if [ -n "$main_process" ]; then
    echo "$main_process"
else
    echo -e "${RED}未找到主进程${NC}"
fi

# 后台进程统计
echo -e "\n${BLUE}=== 后台进程统计 ===${NC}"
background_processes=("background writer" "checkpointer" "walwriter" "autovacuum launcher")
for process in "${background_processes[@]}"; do
    count=$(ps auxww | grep "postgres:" | grep "$process" | grep -v grep | wc -l)
    if [ $count -gt 0 ]; then
        echo -e "${GREEN}✅ $process: $count${NC}"
    else
        echo -e "${YELLOW}⚠️  $process: $count${NC}"
    fi
done

# 客户端连接分析
echo -e "\n${BLUE}=== 客户端连接分析 ===${NC}"
total_connections=$(ps auxww | grep "postgres:" | grep -E "(idle|SELECT|INSERT|UPDATE|DELETE)" | grep -v grep | wc -l)
echo "总连接数: $total_connections"

if [ $total_connections -gt $MAX_CONNECTIONS ]; then
    echo -e "${RED}⚠️  连接数超过阈值 ($MAX_CONNECTIONS)${NC}"
fi

# 按状态分组
echo -e "\n${YELLOW}按状态分组:${NC}"
for status in "idle" "SELECT" "INSERT" "UPDATE" "DELETE" "waiting" "idle in transaction"; do
    count=$(ps auxww | grep "postgres:" | grep "$status" | grep -v grep | wc -l)
    if [ $count -gt 0 ]; then
        if [ "$status" = "waiting" ]; then
            echo -e "  ${RED}$status: $count${NC}"
        elif [ "$status" = "idle in transaction" ]; then
            echo -e "  ${YELLOW}$status: $count${NC}"
        else
            echo -e "  ${GREEN}$status: $count${NC}"
        fi
    fi
done

# 资源使用警告
echo -e "\n${BLUE}=== 资源使用警告 ===${NC}"
ps auxww | grep "postgres:" | grep -v grep | while read line; do
    cpu=$(echo $line | awk '{print $3}' | cut -d. -f1)
    mem=$(echo $line | awk '{print $4}' | cut -d. -f1)
    pid=$(echo $line | awk '{print $2}')
    process_info=$(echo $line | awk '{for(i=11;i<=NF;i++) printf "%s ", $i; print ""}')

    if [ $cpu -gt $HIGH_CPU_THRESHOLD ]; then
        echo -e "${RED}🔥 高CPU使用: PID $pid (${cpu}%) - $process_info${NC}"
    fi

    if [ $mem -gt $HIGH_MEM_THRESHOLD ]; then
        echo -e "${RED}💾 高内存使用: PID $pid (${mem}%) - $process_info${NC}"
    fi
done

故障排查指南

常见问题与解决方案

1. 进程数量异常

bash
# 检查进程数量
ps auxww | grep postgres | grep -v grep | wc -l

# 如果进程数量过多,检查连接池配置
# 查看 max_connections 设置
psql -c "SHOW max_connections;"

2. 锁等待过多

bash
# 查找等待锁的进程
ps auxww | grep "waiting" | grep postgres

# 在数据库中查看锁信息
psql -c "SELECT * FROM pg_locks WHERE NOT granted;"

3. 长时间运行的事务

bash
# 找出长时间运行的事务
ps auxww | grep "idle in transaction" | grep postgres

# 在数据库中查看事务时间
psql -c "SELECT pid, now() - pg_stat_activity.query_start AS duration, query
         FROM pg_stat_activity
         WHERE state = 'idle in transaction'
         AND now() - pg_stat_activity.query_start > interval '5 minutes';"

最佳实践

监控最佳实践

  1. 定期监控: 建立定期的进程监控机制,及时发现异常
  2. 设置告警: 对关键指标设置阈值告警
  3. 记录历史: 保存监控历史数据,便于问题追踪
  4. 自动化处理: 对常见问题建立自动化处理机制
  5. 文档化: 记录常见问题的处理方法 :::

通过这些监控技术和工具,数据库管理员可以有效地监控 PostgreSQL 数据库的运行状态,及时发现并解决性能问题,确保数据库系统的稳定运行。