Appearance
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 | 描述 | 作用 |
---|---|---|---|
主进程 | 15551 | postgres -i | 负责启动和管理所有其他进程 |
后台写入器 | 15554 | background writer | 定期将脏页写入磁盘,减少检查点压力 |
检查点进程 | 15555 | checkpointer | 执行检查点操作,确保数据持久性 |
WAL 写入器 | 15556 | walwriter | 将预写日志(WAL)缓冲区写入磁盘 |
自动清理 | 15557 | autovacuum launcher | 启动自动清理作业,维护表统计信息 |
客户端连接 | 15582 | joe runbug 127.0.0.1 idle | 处理用户 joe 的连接,当前空闲 |
客户端连接 | 15606 | tgl regression [local] SELECT waiting | 执行 SELECT,等待锁 |
客户端连接 | 15610 | tgl 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 注意事项
- 必须使用
/usr/ucb/ps
而不是/bin/ps
- 必须使用两个
w
标志(ww
) - 启动 postgres 命令时的显示必须比每个服务器进程的 ps 状态显示更短
- 如果不满足以上三个条件,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';"
最佳实践
监控最佳实践
- 定期监控: 建立定期的进程监控机制,及时发现异常
- 设置告警: 对关键指标设置阈值告警
- 记录历史: 保存监控历史数据,便于问题追踪
- 自动化处理: 对常见问题建立自动化处理机制
- 文档化: 记录常见问题的处理方法 :::
通过这些监控技术和工具,数据库管理员可以有效地监控 PostgreSQL 数据库的运行状态,及时发现并解决性能问题,确保数据库系统的稳定运行。