Skip to content

Postmaster 主进程(Postmaster / 监听与派生) ​

Postmaster 是 PostgreSQL 实例的第一个进程,也是所有其他进程的父进程。它负责监听连接请求、派生(fork)后端进程、拉起并监控所有辅助进程、接收信号并协调整个实例的启停。本节讲清它的职责边界、进程派生流程、postmaster.pid 文件与崩溃恢复的触发条件。

前置知识

阅读本节前,建议先了解:查询执行流程(Parser / Rewrite / Planner / Executor)

基础概念 ​

PostgreSQL 采用多进程(multi-process) 而非多线程模型。Postmaster 这个名字来自历史(早期 PostgreSQL 的后端进程本就是向客户端提供服务的进程,主进程是「postmaster」,即「管理后端的人」)。

术语英文名说明
PostmasterPostmaster / postgres 主进程实例的唯一监督进程,PID 1 之下的父进程
派生fork()Unix 系统调用,复制当前进程创建子进程
后端进程Backend Process每个客户端连接对应一个进程
辅助进程Auxiliary / Background Processcheckpointer、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 或系统服务管理命令。

进程树结构 ​

text
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
sql
-- 查看 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;
text
          start_time          | listen_addresses | port | max_connections | reserved
------------------------------+------------------+------+-----------------+----------
 2026-09-20 09:12:33.418275+08 | *                | 5432 |             200 |        3
(1 row)

连接建立与后端派生流程 ​

一个客户端从 TCP 连接到获得服务,经过以下步骤:

text
客户端 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 起)乘以连接数不可忽视。

sql
-- 看当前连接数与上限的对比(含保留给超级用户的 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;
text
 client_conns | max_conns | reserved
--------------+-----------+----------
          142 |       200 |        3
(1 row)
sql
-- 达到上限时的真实报错(可在测试环境用 psql 并发压测复现)
-- FATAL:  sorry, too many clients already
-- DETAIL:  ...

为什么 Postmaster 不做认证

认证需要读 pg_hba.conf 并可能查询 pg_authid(要连接数据库),这些工作如果放在 Postmaster 里,一个慢认证(如 LDAP 超时)就会阻塞整个实例接受新连接。把认证下放到后端进程,Postmaster 只做「fork + 回 accept」,是保持高可用的关键设计。

postmaster.pid 文件 ​

实例运行时会在 $PGDATA 下创建一个 postmaster.pid 文件,它是「实例状态」的权威记录。

bash
# ── postmaster.pid 的六行内容 ──
cat "$PGDATA/postmaster.pid"
text
1234                                                  # 第 1 行:Postmaster PID
/data/pgdata                                          # 第 2 行:数据目录
1758340353                                            # 第 3 行:启动时间(Unix 时间戳)
5432                                                  # 第 4 行:端口
/tmp/.s.PGSQL.5432                                    # 第 5 行:Unix Socket 路径
localhost                                             # 第 6 行:监听地址(可为空)
行号内容用途
1Postmaster PIDpg_ctl stop 用它发信号;kill -0 用它判断进程是否存活
2数据目录路径防止把信号发给「相同 PID 的其他进程」
3启动时间与 PID 配合做二次校验(PID 会被复用)
4端口便于运维脚本识别实例
5Socket 路径客户端 psql -h /tmp 时定位
6监听地址最后写入的实际监听地址
bash
# ── pg_ctl 的存活判断逻辑:读 PID → kill -0 探测 → 比对数据目录 ──
pg_ctl status -D "$PGDATA"
text
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 会报:

text
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 launcherautovacuum = on 时重新拉起
archiverarchive_mode = on 时重新拉起
logical replication launcher存在逻辑复制槽时重新拉起
walsender收到复制连接时按需派生不重建(连接断开即结束)
parallel worker并行查询时按需派生随查询结束而结束
stats collectorPostgreSQL 15 之前PG 15 起已移除
sql
-- 一次看清所有进程的类型、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;
text
  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 与用户名做粗略判断。

详细说明 ​

启动与关闭的完整流程 ​

启动流程:

bash
# ── 第 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 60
text
waiting for server to start.... done
server started

Postmaster 启动时依次做这些事:

步骤动作失败后果
1读 postgresql.conf、postgresql.auto.conf配置错误直接退出
2创建/校验 postmaster.pid,加锁已有实例运行时拒绝启动
3读 pg_control 判断上次是否正常关闭非正常关闭 → 标记需要崩溃恢复
4分配共享内存(shared_buffers、锁表等)内存不足则启动失败
5创建 Unix Socket 与监听套接字端口被占用则启动失败
6拉起辅助进程—
7若需要,执行崩溃恢复(重放 WAL)失败则进入「恢复中」状态直至解决
8写入就绪标记,开始接受连接—

关闭流程:

bash
# ── 智能关闭(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拒绝直接终止否崩溃恢复
text
# 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 权限控制)
bash
# ── 重载配置(最常用的操作):通知 Postmaster 重读配置文件 ──
kill -HUP "$(head -n 1 "$PGDATA/postmaster.pid")"

# ── 等价于 pg_ctl reload ──
pg_ctl reload -D "$PGDATA"
sql
-- 在数据库内重载配置(需要超级用户或 pg_signal_backend 权限)
SELECT pg_reload_conf();
text
 pg_reload_conf
----------------
 t
(1 row)
sql
-- 验证配置是否真的生效:看 source 是否为配置文件
SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE name IN ('shared_buffers', 'work_mem', 'log_min_duration_statement');
text
            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:

sql
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 中的状态标记。

bash
# ── 查看控制文件中的集群状态 ──
pg_controldata "$PGDATA"
text
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/1A2B3C48
Database cluster state 取值含义下次启动行为
shut down正常关闭完成直接启动,无需恢复
in production运行中(或异常终止时的残留值)需崩溃恢复
in archive recovery归档恢复中继续恢复
in crash recovery崩溃恢复进行中继续恢复
text
# 崩溃恢复的日志(启动时)
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
sql
-- 恢复期间可以通过这个函数观察进度(返回 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;
text
 in_recovery | replayed_lsn | delay
-------------+--------------+-------
 f           |              |
(1 row)

恢复期间实例不接受写连接

崩溃恢复进行时,Postmaster 已经在监听端口,但只接受超级用户连接,且只能执行只读查询。普通应用连接会收到:

text
FATAL:  the database system is in recovery mode

这是设计行为,不是故障。监控系统应把这条错误与真正的宕机区分开。

监听地址与连接来源 ​

Postmaster 的监听行为由 listen_addresses 决定,它属于 sighup 上下文(可重载,但部分场景需重启)。

ini
# 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'
sql
-- 查看当前的监听配置与所有客户端的来源地址
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;
text
 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:完整观察一次实例启停 ​

bash
# ── 第 1 步:确认实例当前状态 ──
pg_ctl status -D "$PGDATA"
text
pg_ctl: server is running (PID: 1234)
bash
# ── 第 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
text
  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 已无父子关系(启动脚本退出后不会带走实例)。

bash
# ── 第 3 步:快速关闭并观察日志 ──
pg_ctl stop -D "$PGDATA" -m fast -w
text
waiting for server to shut down.... done
server stopped
bash
# ── 第 4 步:确认 postmaster.pid 已被清理 ──
ls -l "$PGDATA/postmaster.pid" 2>&1 || echo "postmaster.pid removed (expected)"
text
ls: cannot access '/data/pgdata/postmaster.pid': No such file or directory
postmaster.pid removed (expected)

示例 2:用 pg_controldata 判断上次是否正常关闭 ​

bash
# ── 在实例已停止的状态下执行 ──
pg_controldata -D "$PGDATA" | grep -E 'cluster state|TimeLineID|Latest checkpoint'
text
Database cluster state:               shut down
TimeLineID:                           1
Latest checkpoint location:           0/1A2B3C80
Latest checkpoint's REDO location:    0/1A2B3C48
Latest checkpoint's TimeLineID:       1

Database cluster state: shut down 说明上次是 smart 或 fast 关闭,重启不需要恢复。若显示 in production,说明上次异常终止,重启会走崩溃恢复。

bash
# ── 模拟异常终止,观察状态变化(测试环境) ──
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'
text
Database cluster state:               in production
bash
# ── 再次启动,日志中会出现崩溃恢复 ──
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"
text
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:连接数打满的定位与处理 ​

sql
-- ── 第 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;
text
        state        | conns |  pct
---------------------+-------+------
 idle                |   118 | 83.1
 active              |    19 | 13.4
 idle in transaction |     5 |  3.5
sql
-- ── 第 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;
text
  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)
sql
-- ── 第 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();
text
 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 会重新拉起它们,日志中会留下记录。

bash
# ── 查找进程重启的日志(不同版本的措辞略有差异) ──
grep -E 'terminating any other active|exited with exit code|restarting' "$PGDATA/logs/server.log" | tail -n 10
text
LOG:  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 退出,日志会是:

text
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 是内部协议:不要手工发送,会破坏后端进程的状态一致性。
bash
# 不推荐:用 kill -9 关闭实例
# kill -9 "$(head -n 1 "$PGDATA/postmaster.pid")"   # 不推荐,会触发崩溃恢复
sql
-- 不推荐:把 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 后端进程

参考链接 ​