Postmaster 主进程(Postmaster / 监听与派生)
Postmaster 是 PostgreSQL 实例的第一个进程,也是所有其他进程的父进程。它负责监听连接请求、派生(fork)后端进程、拉起并监控所有辅助进程、接收信号并协调整个实例的启停。本节讲清它的职责边界、进程派生流程、postmaster.pid 文件与崩溃恢复的触发条件。
前置知识
阅读本节前,建议先了解:查询执行流程(Parser / Rewrite / Planner / Executor)
基础概念
PostgreSQL 采用多进程(multi-process) 而非多线程模型。Postmaster 这个名字来自历史(早期 PostgreSQL 的后端进程本就是向客户端提供服务的进程,主进程是「postmaster」,即「管理后端的人」)。
| 术语 | 英文名 | 说明 |
|---|---|---|
| Postmaster | Postmaster / postgres 主进程 | 实例的唯一监督进程,PID 1 之下的父进程 |
| 派生 | fork() | Unix 系统调用,复制当前进程创建子进程 |
| 后端进程 | Backend Process | 每个客户端连接对应一个进程 |
| 辅助进程 | Auxiliary / Background Process | checkpointer、bgwriter、walwriter 等 |
| 崩溃恢复 | Crash Recovery | 实例异常终止后重启时重放 WAL 的过程 |
| 快速关闭 | Fast Shutdown | 断开客户端、回滚事务、写检查点后退出 |
| 智能关闭 | Smart Shutdown | 等所有客户端主动断开后再退出 |
| 立即关闭 | Immediate Shutdown | 直接终止,下次启动走崩溃恢复 |
Postmaster 被杀掉等于整个实例宕掉
Postmaster 是所有进程的父进程。杀死它(kill -9 <postmaster_pid>)会导致所有子进程失去父进程并被一并终止,正在执行的事务全部中断,下次启动必须走崩溃恢复。绝对不要用 kill -9 关闭 PostgreSQL,正确方式是用 pg_ctl stop -m fast 或系统服务管理命令。
进程树结构
systemd / init (PID 1)
│
└── postgres (Postmaster, PID 1234) ← 实例的唯一入口
├── postgres: checkpointer ← 辅助进程
├── postgres: background writer
├── postgres: walwriter
├── postgres: autovacuum launcher
├── postgres: logical replication launcher
├── postgres: archiver (按需启动)
├── postgres: stats collector (PostgreSQL 15 之前才有)
├── postgres: postgres app_prod [local] idle ← 客户端后端
├── postgres: postgres app_prod 10.0.0.5(52341) SELECT
└── postgres: walsender repl_user 10.0.0.6(45222) streaming判断谁是 Postmaster:它是唯一没有前缀修饰的 postgres 进程。
核心机制
Postmaster 的四项核心职责
| 职责 | 具体行为 | 相关参数 |
|---|---|---|
| 监听连接 | 绑定 listen_addresses:port,接受 TCP / Unix Socket 连接 | listen_addresses、port、max_connections |
| 派生后端 | 为每个通过认证的连接 fork() 一个后端进程 | max_connections、superuser_reserved_connections |
| 主管辅助进程 | 启动并监控所有辅助进程,异常退出时重新拉起 | max_worker_processes、max_wal_senders |
| 处理信号与启停 | 响应 SIGHUP/SIGTERM/SIGQUIT,协调实例关闭 | pg_ctl -m |
-- 查看 Postmaster 的启动时间、监听配置与连接上限
SELECT pg_postmaster_start_time() AS start_time,
current_setting('listen_addresses') AS listen_addresses,
current_setting('port') AS port,
current_setting('max_connections') AS max_connections,
current_setting('superuser_reserved_connections') AS reserved; start_time | listen_addresses | port | max_connections | reserved
------------------------------+------------------+------+-----------------+----------
2026-09-20 09:12:33.418275+08 | * | 5432 | 200 | 3
(1 row)连接建立与后端派生流程
一个客户端从 TCP 连接到获得服务,经过以下步骤:
客户端 connect() 到 5432 端口
│
▼
┌────────────────────────────────────────────┐
│ 1. Postmaster 的监听套接字可读 │
│ 检查 max_connections 是否已满 │
│ 已满 → 拒绝并回 "FATAL: sorry, too many │
│ clients already" │
└────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 2. fork() 出一个新后端进程 │
│ 新进程继承监听套接字的文件描述符 │
│ 父进程(Postmaster)立即回到 accept 循环 │
└────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 3. 后端进程读 pg_hba.conf 做认证 │
│ (认证由后端自己完成,Postmaster 不参与) │
│ 失败 → 记录日志、关闭连接 │
└────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ 4. 认证通过:初始化 Backend 状态 │
│ 建立数据库连接、装载 catalog cache │
│ 进入「等待客户端消息」循环 │
└────────────────────────────────────────────┘关键点:fork() 是复制整个进程,不是「启动一个新程序」。这就是 PostgreSQL 需要连接池的根本原因——每个后端都要独立加载自己的 catalog cache 与工作内存,进程本身的内存开销(通常 5~10MB 起)乘以连接数不可忽视。
-- 看当前连接数与上限的对比(含保留给超级用户的 3 个)
SELECT count(*) FILTER (WHERE backend_type = 'client backend') AS client_conns,
current_setting('max_connections')::int AS max_conns,
current_setting('superuser_reserved_connections')::int AS reserved; client_conns | max_conns | reserved
--------------+-----------+----------
142 | 200 | 3
(1 row)-- 达到上限时的真实报错(可在测试环境用 psql 并发压测复现)
-- FATAL: sorry, too many clients already
-- DETAIL: ...为什么 Postmaster 不做认证
认证需要读 pg_hba.conf 并可能查询 pg_authid(要连接数据库),这些工作如果放在 Postmaster 里,一个慢认证(如 LDAP 超时)就会阻塞整个实例接受新连接。把认证下放到后端进程,Postmaster 只做「fork + 回 accept」,是保持高可用的关键设计。
postmaster.pid 文件
实例运行时会在 $PGDATA 下创建一个 postmaster.pid 文件,它是「实例状态」的权威记录。
# ── postmaster.pid 的六行内容 ──
cat "$PGDATA/postmaster.pid"1234 # 第 1 行:Postmaster PID
/data/pgdata # 第 2 行:数据目录
1758340353 # 第 3 行:启动时间(Unix 时间戳)
5432 # 第 4 行:端口
/tmp/.s.PGSQL.5432 # 第 5 行:Unix Socket 路径
localhost # 第 6 行:监听地址(可为空)| 行号 | 内容 | 用途 |
|---|---|---|
| 1 | Postmaster PID | pg_ctl stop 用它发信号;kill -0 用它判断进程是否存活 |
| 2 | 数据目录路径 | 防止把信号发给「相同 PID 的其他进程」 |
| 3 | 启动时间 | 与 PID 配合做二次校验(PID 会被复用) |
| 4 | 端口 | 便于运维脚本识别实例 |
| 5 | Socket 路径 | 客户端 psql -h /tmp 时定位 |
| 6 | 监听地址 | 最后写入的实际监听地址 |
# ── pg_ctl 的存活判断逻辑:读 PID → kill -0 探测 → 比对数据目录 ──
pg_ctl status -D "$PGDATA"pg_ctl: server is running (PID: 1234)
/usr/lib/postgresql/18/bin/postgres "-D" "/data/pgdata"实例异常退出留下的 postmaster.pid
如果 Postmaster 被 kill -9,postmaster.pid 不会被删除。此时 pg_ctl start 会报:
pg_ctl: another server might be running; trying to start server anyway
FATAL: lock file "postmaster.pid" already exists
HINT: Is another postmaster (PID 1234) running in data directory "/data/pgdata"?先确认 PID 1234 确实不存在(ps -p 1234),再删除 postmaster.pid 后启动。不要在进程还活着时删掉这个文件,那会导致同一个 PGDATA 上跑起两个实例,数据必定损坏。
辅助进程的启动与监控
Postmaster 在启动时会拉起固定的辅助进程,并在它们意外退出时自动重新拉起。例外是 checkpointer——它退出意味着实例必须重启(进入崩溃恢复)。
| 辅助进程 | 启动时机 | 意外退出后的行为 |
|---|---|---|
| checkpointer | 实例启动 | Postmaster 终止所有进程并进入崩溃恢复 |
| background writer | 实例启动 | 重新拉起 |
| walwriter | 实例启动 | 重新拉起 |
| autovacuum launcher | autovacuum = on 时 | 重新拉起 |
| archiver | archive_mode = on 时 | 重新拉起 |
| logical replication launcher | 存在逻辑复制槽时 | 重新拉起 |
| walsender | 收到复制连接时按需派生 | 不重建(连接断开即结束) |
| parallel worker | 并行查询时按需派生 | 随查询结束而结束 |
| stats collector | PostgreSQL 15 之前 | PG 15 起已移除 |
-- 一次看清所有进程的类型、PID 与各自的状态
SELECT pid,
backend_type,
usename,
state,
now() - backend_start AS uptime,
pg_size_pretty(pg_relation_size(0)) AS _ignore -- 占位,避免误用
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
ORDER BY backend_type, pid; pid | backend_type | usename | state | uptime
-------+-------------------------------+----------+--------+-------------
1236 | background writer | | | 3 days 04:21
1237 | checkpointer | | | 3 days 04:21
1238 | walwriter | | | 3 days 04:21
1239 | autovacuum launcher | | | 3 days 04:21
1240 | logical replication launcher | | | 3 days 04:21
1254 | archiver | | | 3 days 04:21
1301 | client backend | app_user | active | 00:00:01.22
1278 | client backend | app_user | idle | 00:12:04.51
(8 rows)backend_type 是 PostgreSQL 10 引入的字段,之前只能用 state 与用户名做粗略判断。
详细说明
启动与关闭的完整流程
启动流程:
# ── 第 1 步:pg_ctl 检查 postmaster.pid ──
pg_ctl status -D "$PGDATA"
# ── 第 2 步:启动实例(-l 指定日志文件) ──
pg_ctl start -D "$PGDATA" -l "$PGDATA/logs/server.log"
# ── 第 3 步:用 -w 等待「可以接受连接」再返回 ──
pg_ctl start -D "$PGDATA" -l "$PGDATA/logs/server.log" -w -t 60waiting for server to start.... done
server startedPostmaster 启动时依次做这些事:
| 步骤 | 动作 | 失败后果 |
|---|---|---|
| 1 | 读 postgresql.conf、postgresql.auto.conf | 配置错误直接退出 |
| 2 | 创建/校验 postmaster.pid,加锁 | 已有实例运行时拒绝启动 |
| 3 | 读 pg_control 判断上次是否正常关闭 | 非正常关闭 → 标记需要崩溃恢复 |
| 4 | 分配共享内存(shared_buffers、锁表等) | 内存不足则启动失败 |
| 5 | 创建 Unix Socket 与监听套接字 | 端口被占用则启动失败 |
| 6 | 拉起辅助进程 | — |
| 7 | 若需要,执行崩溃恢复(重放 WAL) | 失败则进入「恢复中」状态直至解决 |
| 8 | 写入就绪标记,开始接受连接 | — |
关闭流程:
# ── 智能关闭(smart):等所有客户端主动断开,最温和但可能永不完成 ──
pg_ctl stop -D "$PGDATA" -m smart
# ── 快速关闭(fast,默认推荐):断开客户端、回滚未提交事务、写检查点 ──
pg_ctl stop -D "$PGDATA" -m fast
# ── 立即关闭(immediate):等同 kill -9,下次启动必须走崩溃恢复 ──
pg_ctl stop -D "$PGDATA" -m immediate| 模式 | 新连接 | 当前事务 | 是否写检查点 | 下次启动 |
|---|---|---|---|---|
smart | 拒绝 | 等其自然结束 | 是 | 正常启动 |
fast | 拒绝 | 回滚后终止 | 是 | 正常启动 |
immediate | 拒绝 | 直接终止 | 否 | 崩溃恢复 |
# fast 关闭时的日志顺序
LOG: received fast shutdown request
LOG: aborting any active transactions
LOG: background worker "logical replication launcher" (PID 1240) exited with exit code 1
LOG: shutting down
LOG: checkpoint starting: shutdown immediate
LOG: checkpoint complete: wrote 812 buffers (5.0%); 0 WAL file(s) added, 0 removed, 1 recycled; write=0.041 s, sync=0.008 s, total=0.063 s
LOG: database system is shut down信号处理:运维与监控的接口
Postmaster 对信号的处理方式决定了它的行为,运维中常用到的信号:
| 信号 | 含义 | 效果 |
|---|---|---|
SIGHUP | 重新加载配置 | 重新读 postgresql.conf、pg_hba.conf,并转发给所有子进程 |
SIGTERM | 智能关闭 | 等同 pg_ctl stop -m smart |
SIGINT | 快速关闭 | 等同 pg_ctl stop -m fast |
SIGQUIT | 立即关闭 | 等同 pg_ctl stop -m immediate,不写检查点 |
SIGUSR1 | 内部通知 | 告知子进程「有新进程加入」或「配置文件已变更」 |
SIGUSR2 | 内部通知 | 告知子进程「需要重新读取后端列表」 |
SIGCHLD | 子进程状态变化 | Postmaster 感知子进程退出并决定是否重启 |
SIGABRT | 触发崩溃 | 用于测试崩溃恢复(PG 12+ 由 pg_signal_backend 权限控制) |
# ── 重载配置(最常用的操作):通知 Postmaster 重读配置文件 ──
kill -HUP "$(head -n 1 "$PGDATA/postmaster.pid")"
# ── 等价于 pg_ctl reload ──
pg_ctl reload -D "$PGDATA"-- 在数据库内重载配置(需要超级用户或 pg_signal_backend 权限)
SELECT pg_reload_conf(); pg_reload_conf
----------------
t
(1 row)-- 验证配置是否真的生效:看 source 是否为配置文件
SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE name IN ('shared_buffers', 'work_mem', 'log_min_duration_statement'); name | setting | source | pending_restart
----------------------------+---------+-------------------+-----------------
log_min_duration_statement | 1000 | configuration file | f
shared_buffers | 16384 | configuration file | t -- 需重启才生效
work_mem | 8192 | configuration file | f
(3 rows)pending_restart = t 是判断「是否改了需要重启的参数」的标准方法,必须在重启前确认。
SIGHUP 的边界
SIGHUP 只能重载 postmaster 上下文之外的参数。shared_buffers、wal_buffers、max_connections、huge_pages、shared_preload_libraries 这些属于 postmaster 上下文的参数,必须重启。想知道某个参数是否可以热加载,看 pg_settings.context:
SELECT name, context FROM pg_settings WHERE name IN ('shared_buffers', 'work_mem', 'listen_addresses');
-- shared_buffers | postmaster ← 必须重启
-- work_mem | user ← 会话内即可改
-- listen_addresses | sighup ← 重载即可崩溃恢复的触发与进度观察
只要实例上次是非正常关闭(SIGQUIT、断电、kill -9、OOM Killer),Postmaster 在启动时就会进入崩溃恢复。
判断依据是 $PGDATA/global/pg_control 中的状态标记。
# ── 查看控制文件中的集群状态 ──
pg_controldata "$PGDATA"pg_control version number: 1300
Catalog version number: 202506071
Database system identifier: 7423198560412381099
Database cluster state: in production ← 正常关闭会写这个状态
pg_control last modified: Mon 22 Sep 2026 10:15:33 AM CST
Latest checkpoint location: 0/1A2B3C80
Latest checkpoint's REDO location: 0/1A2B3C48Database cluster state 取值 | 含义 | 下次启动行为 |
|---|---|---|
shut down | 正常关闭完成 | 直接启动,无需恢复 |
in production | 运行中(或异常终止时的残留值) | 需崩溃恢复 |
in archive recovery | 归档恢复中 | 继续恢复 |
in crash recovery | 崩溃恢复进行中 | 继续恢复 |
# 崩溃恢复的日志(启动时)
LOG: database system was interrupted; last known up at 2026-09-22 10:14:02 CST
LOG: database system was not properly shut down; automatic recovery in progress
LOG: redo starts at 0/1A2B3C48
LOG: invalid record length at 0/1A2B3C80: wanted 24, got 0
LOG: redo done at 0/1A2B3C80 system usage: CPU: user: 0.02 s, system: 0.00 s, elapsed: 0.04 s
LOG: database system is ready to accept connections-- 恢复期间可以通过这个函数观察进度(返回 NULL 表示不在恢复中)
SELECT pg_is_in_recovery() AS in_recovery,
pg_last_wal_replay_lsn() AS replayed_lsn,
now() - pg_last_xact_replay_timestamp() AS delay; in_recovery | replayed_lsn | delay
-------------+--------------+-------
f | |
(1 row)恢复期间实例不接受写连接
崩溃恢复进行时,Postmaster 已经在监听端口,但只接受超级用户连接,且只能执行只读查询。普通应用连接会收到:
FATAL: the database system is in recovery mode这是设计行为,不是故障。监控系统应把这条错误与真正的宕机区分开。
监听地址与连接来源
Postmaster 的监听行为由 listen_addresses 决定,它属于 sighup 上下文(可重载,但部分场景需重启)。
# postgresql.conf
# 只监听本机(最安全)
listen_addresses = 'localhost'
port = 5432
# 监听所有网卡(需配合防火墙与 pg_hba.conf 严格控制)
# listen_addresses = '*'
# 监听多个具体地址
# listen_addresses = '10.0.0.10,127.0.0.1'
# Unix Socket 所在目录
unix_socket_directories = '/tmp,/var/run/postgresql'-- 查看当前的监听配置与所有客户端的来源地址
SELECT current_setting('listen_addresses') AS listen_addresses,
current_setting('unix_socket_directories') AS socket_dirs;
SELECT client_addr, client_port, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY client_addr, client_port
ORDER BY count DESC; listen_addresses | socket_dirs
------------------+--------------------------
localhost | /tmp,/var/run/postgresql
(1 row)
client_addr | client_port | count
-------------+-------------+-------
127.0.0.1 | 52341 | 8
10.0.0.15 | 41822 | 3
(2 rows)实战示例
示例 1:完整观察一次实例启停
# ── 第 1 步:确认实例当前状态 ──
pg_ctl status -D "$PGDATA"pg_ctl: server is running (PID: 1234)# ── 第 2 步:抓取进程树,确认 Postmaster 与辅助进程 ──
ps -o pid,ppid,stat,etime,command -p "$(head -n 1 "$PGDATA/postmaster.pid")"
ps --ppid "$(head -n 1 "$PGDATA/postmaster.pid")" -o pid,stat,command 2>/dev/null || \
pgrep -P "$(head -n 1 "$PGDATA/postmaster.pid")" | xargs ps -o pid,stat,command -p PID PPID STAT ELAPSED COMMAND
1234 1 Ssl 3-04:21:07 /usr/lib/postgresql/18/bin/postgres -D /data/pgdata
PID STAT COMMAND
1236 Ss postgres: background writer
1237 Ss postgres: checkpointer
1238 Ss postgres: walwriter
1239 Ss postgres: autovacuum launcher
1240 Ss postgres: logical replication launcher
1254 Ss postgres: archiver注意 Postmaster 的 PPID 是 1——它被 systemd 或 init 收养,与启动它的 shell 已无父子关系(启动脚本退出后不会带走实例)。
# ── 第 3 步:快速关闭并观察日志 ──
pg_ctl stop -D "$PGDATA" -m fast -wwaiting for server to shut down.... done
server stopped# ── 第 4 步:确认 postmaster.pid 已被清理 ──
ls -l "$PGDATA/postmaster.pid" 2>&1 || echo "postmaster.pid removed (expected)"ls: cannot access '/data/pgdata/postmaster.pid': No such file or directory
postmaster.pid removed (expected)示例 2:用 pg_controldata 判断上次是否正常关闭
# ── 在实例已停止的状态下执行 ──
pg_controldata -D "$PGDATA" | grep -E 'cluster state|TimeLineID|Latest checkpoint'Database cluster state: shut down
TimeLineID: 1
Latest checkpoint location: 0/1A2B3C80
Latest checkpoint's REDO location: 0/1A2B3C48
Latest checkpoint's TimeLineID: 1Database cluster state: shut down 说明上次是 smart 或 fast 关闭,重启不需要恢复。若显示 in production,说明上次异常终止,重启会走崩溃恢复。
# ── 模拟异常终止,观察状态变化(测试环境) ──
pg_ctl start -D "$PGDATA" -l "$PGDATA/logs/server.log" -w
kill -QUIT "$(head -n 1 "$PGDATA/postmaster.pid")" # SIGQUIT = 立即关闭
sleep 2
pg_controldata -D "$PGDATA" | grep 'cluster state'Database cluster state: in production# ── 再次启动,日志中会出现崩溃恢复 ──
pg_ctl start -D "$PGDATA" -l "$PGDATA/logs/server.log" -w
grep -m3 -E 'not properly shut down|automatic recovery|ready to accept' "$PGDATA/logs/server.log"LOG: database system was not properly shut down; automatic recovery in progress
LOG: redo starts at 0/1A2B3C48
LOG: database system is ready to accept connections示例 3:连接数打满的定位与处理
-- ── 第 1 步:统计各状态连接数,找出「占着连接不干活」的会话 ──
SELECT state,
count(*) AS conns,
round(100.0 * count(*) / sum(count(*)) OVER (), 1) AS pct
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY state
ORDER BY conns DESC; state | conns | pct
---------------------+-------+------
idle | 118 | 83.1
active | 19 | 13.4
idle in transaction | 5 | 3.5-- ── 第 2 步:找出空闲超过 10 分钟的会话(典型的连接泄漏) ──
SELECT pid, usename, application_name, client_addr,
state,
now() - state_change AS idle_time,
left(query, 40) AS last_query
FROM pg_stat_activity
WHERE backend_type = 'client backend'
AND state = 'idle'
AND now() - state_change > interval '10 minutes'
ORDER BY idle_time DESC; pid | usename | application_name | client_addr | state | idle_time | last_query
-------+----------+------------------+-------------+-------+---------------+----------------
1278 | app_user | app-server-03 | 10.0.0.15 | idle | 00:42:18.221 | SELECT id FROM ...
1302 | app_user | app-server-03 | 10.0.0.15 | idle | 00:38:05.774 | SELECT id FROM ...
(2 rows)-- ── 第 3 步:终止泄漏的连接(谨慎,先与业务确认) ──
SELECT pg_terminate_backend(pid), pid, application_name
FROM pg_stat_activity
WHERE backend_type = 'client backend'
AND state = 'idle'
AND now() - state_change > interval '30 minutes'
AND pid <> pg_backend_pid(); pg_terminate_backend | pid | application_name
----------------------+------+------------------
t | 1278 | app-server-03
t | 1302 | app-server-03
(2 rows)正确的长期解法是在应用侧引入连接池(PgBouncer / pgpool-II),把数据库侧连接数压到 max_connections 的 50% 以下。
示例 4:查看辅助进程的重启历史
辅助进程异常退出时,Postmaster 会重新拉起它们,日志中会留下记录。
# ── 查找进程重启的日志(不同版本的措辞略有差异) ──
grep -E 'terminating any other active|exited with exit code|restarting' "$PGDATA/logs/server.log" | tail -n 10LOG: background worker "logical replication launcher" (PID 1240) exited with exit code 1
LOG: terminating any other active server processes
LOG: all server processes terminated; reinitializing如果是 checkpointer 退出,日志会是:
LOG: checkpointer process (PID 1237) was terminated by signal 11: Segmentation fault
LOG: terminating any other active server processes
FATAL: the database system is shutting down
LOG: database system was interrupted; last known up at 2026-09-22 10:14:02 CST这类日志说明发生了段错误(Segmentation Fault)——通常指向 PostgreSQL 本身的 Bug、被 pthread 冲突影响的扩展,或硬件问题。必须立刻上报并收集 core 文件,不能简单重启了事。
注意事项
- 绝不用
kill -9关闭实例:会跳过检查点,下次启动必须走崩溃恢复,耗时随未刷盘的脏页量增长。 postmaster.pid不能手工乱删:只有在确认 PID 对应的进程确实不存在时才能删除,否则会导致同一 PGDATA 双实例写坏数据。max_connections不是越大越好:每个后端进程独立占用内存与 CPU 调度份额,超过 300 通常反而降低吞吐(连接切换开销上升)。用连接池。superuser_reserved_connections要留足:默认 3 个。当连接打满时,只有超级用户能连进去执行pg_terminate_backend自救。SIGHUP之后必须验证参数是否生效:用pg_settings.source与pending_restart双重确认,不要只看命令返回值。listen_addresses = '*'必须配合防火墙:PostgreSQL 默认端口公开在互联网上被扫描是常见事故来源。Database cluster state: in production不代表实例在跑:它只表示「上次没有正常关闭」,必须结合pg_ctl status判断。- 恢复期间不要反复重启:崩溃恢复会从头再来一次,反复重启会让恢复时间线性累加。让它跑完。
- 辅助进程频繁重启要查根因:Postmaster 有重启限制(短时间内失败过多次会直接终止实例),日志里会出现
XXX process ... exited with exit code N; terminating any other active server processes。 SIGUSR1/SIGUSR2是内部协议:不要手工发送,会破坏后端进程的状态一致性。
# 不推荐:用 kill -9 关闭实例
# kill -9 "$(head -n 1 "$PGDATA/postmaster.pid")" # 不推荐,会触发崩溃恢复-- 不推荐:把 max_connections 调到 5000 来「解决」连接不足
-- ALTER SYSTEM SET max_connections = 5000; -- 不推荐,会 OOM 且调度开销巨大
-- 推荐:用 PgBouncer 等连接池,数据库侧保持 100~300最佳实践
- 用
systemd或pg_ctl管理实例,不要手工起停:systemd的Restart=on-failure能在实例崩溃后自动拉起并保留日志。 - 启动时加
-w -t 60:确保脚本拿到的「启动成功」是真的可以接受连接,而不是「进程已 fork」。 - 关闭一律用
-m fast:平衡了数据安全与停机时间;只有在已经数据损坏且必须实验时才用immediate。 - 给
postmaster.pid所在目录留足权限:只有postgres用户可写,防止被其他用户篡改导致启动失败。 - 监控
pg_postmaster_start_time():通过now() - pg_postmaster_start_time()得到实例运行时长,突然归零就是重启告警。 - 生产环境用连接池把
max_connections控制在 100~300:这是控制内存总量与调度开销最有效的手段。 - 为超级用户保留连接通道:
superuser_reserved_connections至少设 5,并保证运维工具使用超级用户连接以应对连接打满。 listen_addresses默认只留localhost:需要远程访问时再显式打开,并同时收紧pg_hba.conf与防火墙。- 把
pg_controldata纳入巡检脚本:每次实例启动前记录Database cluster state,异常终止可被立刻发现。 - 崩溃恢复失败时不要反复重启:先收集日志、检查
pg_wal/完整性,必要时联系支持,避免在坏盘上反复写入造成二次损坏。
下一节
继续学习:Backend 后端进程