pg_ctl 启停服务
pg_ctl 是 PostgreSQL 自带的命令行工具,用于初始化、启动、停止、重启与查看服务状态。它是所有发行版封装(systemd、pg_ctlcluster)的底层实现,也是容器与源码编译环境下唯一可用的启动方式。本节详解各子命令、常用参数与三种停止模式的差异。
前置知识
阅读本节前,建议先了解:初始化数据库集群(initdb)
基础概念
pg_ctl:随 PostgreSQL 一起安装的辅助控制程序,位于$PGBIN(如/usr/lib/postgresql/18/bin/pg_ctl)- 数据目录(Data Directory):
-D参数指向的集群目录;也可用环境变量PGDATA代替 - 服务进程(Server Process):由
postgres主进程管理的进程组;pg_ctl本身不是守护进程,它执行完动作就退出 - 只加载配置(Reload):重新读取
postgresql.conf与pg_hba.conf,不中断现有连接。等价于给主进程发SIGHUP - 重启(Restart):终止所有连接与后端进程,再重新启动服务;会短暂中断业务
- 停止模式(Shutdown Mode):
smart/fast/immediate三种,决定对正在执行的连接与事务的处置方式
| 子命令 | 作用 | 是否影响数据 | 典型场景 |
|---|---|---|---|
init | 调用 initdb 初始化集群 | 创建数据 | 新建集群 |
start | 启动服务 | 无 | 日常启动 |
stop | 停止服务 | 取决于模式 | 维护、关机 |
restart | 重启服务 | 无 | 改需要重启的参数 |
reload | 重载配置 | 无 | 改 pg_hba.conf |
status | 查看运行状态 | 无 | 排障、健康检查 |
promote | 备库提升为主库 | 无 | 主备切换 |
logrotate | 轮转日志文件 | 无 | 配合 logging_collector |
kill | 向指定进程发信号 | 取决于信号 | 极端情况 |
版本说明
pg_ctl 的参数在各大版本间非常稳定。PostgreSQL 18 中新增/强化的是与 --data-checksums 相关的能力,以及 start 时对 -w(等待就绪)的默认行为优化——18 中 -w 仍是默认,但等待逻辑更精确。
语法与用法
通用语法
pg_ctl [子命令] [通用选项] [子命令专属选项]通用选项
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-D, --pgdata=DIR | 路径 | $PGDATA | 数据目录;几乎每条命令都需要 |
-l, --log=FILE | 路径 | 无 | 把服务端 stdout/stderr 重定向到文件 |
-s, --silent | flag | 关闭 | 只打印错误,不打印普通提示 |
-t, --timeout=SEC | 整数 | 60 | 等待操作完成的最长秒数 |
-w, --wait | flag | 开启 | 等待操作真正完成(推荐保持) |
-W, --no-wait | flag | —— | 不等待,命令立即返回 |
-o, --options=OPTS | 字符串 | 无 | 传给 postgres 的启动参数,必须整体加引号 |
-p, --pgdata(旧写法) | 路径 | 无 | 历史遗留,不建议使用 |
-V, --version | flag | —— | 打印版本 |
-?, --help | flag | —— | 打印帮助 |
-o 参数必须整体加引号
-o 后面是「一串要原样传给 postgres 进程的参数」,shell 会按空格拆分,所以必须写成 -o "-c shared_buffers=4GB -p 5433" 这种整体引用的形式,否则 -c 会被 pg_ctl 自己解析而报错。
子命令一览
# 初始化集群(封装 initdb,等价于直接调用 initdb)
pg_ctl -D /var/lib/postgresql/18/data init -o "-E UTF8 --locale=C.UTF-8"
# 启动服务,日志输出到指定文件
pg_ctl -D /var/lib/postgresql/18/data -l /var/log/postgresql/pg18.log start
# 查看状态
pg_ctl -D /var/lib/postgresql/18/data status
# 只重载配置(不中断连接)
pg_ctl -D /var/lib/postgresql/18/data reload
# 快速停止(默认模式)
pg_ctl -D /var/lib/postgresql/18/data stop -m fast
# 重启
pg_ctl -D /var/lib/postgresql/18/data -l /var/log/postgresql/pg18.log restart
# 备库提升为主库(流复制场景)
pg_ctl -D /var/lib/postgresql/18/data promote
# 手动轮转日志(需开启 logging_collector)
pg_ctl -D /var/lib/postgresql/18/data logrotate详细说明
start 子命令
start 会在后台派生出 postgres 主进程,然后(默认 -w)等待服务真正可以接受连接后才返回。
# 最简启动:必须指定 -l,否则服务端日志会被丢弃
pg_ctl -D /var/lib/postgresql/18/data -l /var/log/postgresql/pg18.log start
# 启动时临时覆盖配置(不改文件,仅本次生效)
pg_ctl -D /var/lib/postgresql/18/data \
-o "-c shared_buffers=2GB -c max_connections=200 -p 5433" \
-l /var/log/postgresql/pg18.log start
# 启动但不等待(适合脚本中自行轮询 pg_isready 的场景)
pg_ctl -D /var/lib/postgresql/18/data -W startwaiting for server to start.... done
server started为什么一定要写 -l
不指定 -l 时,pg_ctl 会把服务端输出继承到当前终端;一旦终端关闭,这些输出就彻底丢失。服务端的关键启动错误(如配置语法错误、端口占用)恰恰只出现在这里。生产环境必须指定日志文件。
stop 子命令与三种停止模式
这是 pg_ctl 最需要理解的部分。三种模式对应不同的信号,行为差异巨大:
# smart:等待所有客户端主动断开后才关闭,可能永远不停
pg_ctl -D /var/lib/postgresql/18/data stop -m smart
# fast:立刻断开所有连接,回滚未提交事务,然后干净关闭(默认且推荐)
pg_ctl -D /var/lib/postgresql/18/data stop -m fast
# immediate:不给任何清理机会,等价于断电;重启后必须做崩溃恢复
pg_ctl -D /var/lib/postgresql/18/data stop -m immediate| 模式 | 对应信号 | 对现有连接 | 对未提交事务 | 是否需要崩溃恢复 | 适用场景 |
|---|---|---|---|---|---|
smart | SIGTERM | 等待客户端主动断开 | 允许提交完成 | 否 | 计划内维护、业务已确认停机 |
fast | SIGINT | 立即断开所有连接 | 全部回滚 | 否 | 日常停机的默认选择 |
immediate | SIGQUIT | 直接杀死进程 | 全部回滚 | 是 | fast 卡死、进程无响应时 |
# smart 模式的典型输出:等待中
waiting for server to shut down....
# fast 模式的典型输出:很快完成
waiting for server to shut down.... done
server stopped
# immediate 模式的典型输出:立即返回
waiting for server to shut down.... done
server stoppedimmediate 不是「更快」,而是「更危险」
immediate 模式下 PostgreSQL 不写关闭检查点,也不保证数据文件已刷盘,下次启动必须重放 WAL 做崩溃恢复。它只应在 fast 长时间无法结束时作为最后手段使用。恢复期间数据库可能拒绝连接或性能极差。
reload 与 restart 的取舍
# 只重载配置:现有连接不中断,新连接按新规则处理
pg_ctl -D /var/lib/postgresql/18/data reload
# 完整重启:所有连接断开,服务不可用数秒
pg_ctl -D /var/lib/postgresql/18/data -l /var/log/postgresql/pg18.log restart -m fast| 参数类型 | 生效方式 | 示例 | 命令 |
|---|---|---|---|
| 认证规则 | reload 即可 | pg_hba.conf、pg_ident.conf 全部内容 | reload |
| 可动态修改的运行参数 | reload 即可 | log_min_duration_statement、work_mem | reload |
| 需要重启的参数 | 必须 restart | shared_buffers、max_connections、wal_level、port | restart |
| 已废弃的参数 | —— | —— | 检查日志中的警告 |
实践中不必死记清单:发送 reload 后,PostgreSQL 会在日志中对无法动态生效的参数打印提示。
LOG: received SIGHUP, reloading configuration files
LOG: parameter "shared_buffers" cannot be changed without restarting the server
LOG: configuration file "/var/lib/postgresql/18/data/postgresql.conf" reloadedstatus 与 kill 子命令
# 查看状态:运行中返回 0,未运行返回 3,数据目录无效返回 4
pg_ctl -D /var/lib/postgresql/18/data statuspg_ctl: server is running (PID: 24318)
/usr/lib/postgresql/18/bin/postgres "-D" "/var/lib/postgresql/18/data"# 未运行时
pg_ctl: no server running# kill:向指定 PID 或指定类型的进程发送信号
pg_ctl -D /var/lib/postgresql/18/data kill -s HUP 24318 # 指定 PID
pg_ctl -D /var/lib/postgresql/18/data kill -s INT -1 # 向所有后端进程发信号
# 也可直接给 postgres 主进程发信号(效果等价于 reload)
kill -HUP "$(head -1 /var/lib/postgresql/18/data/postmaster.pid)"| 信号 | 等价操作 | 说明 |
|---|---|---|
SIGHUP | pg_ctl reload | 重载配置 |
SIGINT | pg_ctl stop -m fast | 快速关闭 |
SIGTERM | pg_ctl stop -m smart | 智能关闭 |
SIGQUIT | pg_ctl stop -m immediate | 立即关闭(危险) |
SIGUSR1 | —— | 切换日志文件 |
SIGUSR2 | —— | 切换 WAL 段 |
实战示例
场景一:源码编译环境下的标准启停脚本
#!/usr/bin/env bash
# pg18.sh:源码编译环境下管理单实例的运维脚本
set -euo pipefail
PGBIN=/usr/local/pgsql-18/bin
PGDATA=/var/lib/pgsql/18/data
PGLOG=/var/log/pgsql/pg18.log
case "${1:-}" in
start)
# -w 等待就绪,确保返回后立即可连接
sudo -u postgres "${PGBIN}/pg_ctl" -D "${PGDATA}" -l "${PGLOG}" start
;;
stop)
# 默认用 fast,避免 smart 卡住导致关机流程挂起
sudo -u postgres "${PGBIN}/pg_ctl" -D "${PGDATA}" stop -m fast
;;
restart)
sudo -u postgres "${PGBIN}/pg_ctl" -D "${PGDATA}" -l "${PGLOG}" restart -m fast
;;
reload)
sudo -u postgres "${PGBIN}/pg_ctl" -D "${PGDATA}" reload
;;
status)
sudo -u postgres "${PGBIN}/pg_ctl" -D "${PGDATA}" status
;;
*)
echo "usage: $0 {start|stop|restart|reload|status}" >&2
exit 1
;;
esac$ ./pg18.sh start
waiting for server to start.... done
server started
$ ./pg18.sh status
pg_ctl: server is running (PID: 31127)
/usr/local/pgsql-18/bin/postgres "-D" "/var/lib/pgsql/18/data"
$ ./pg18.sh stop
waiting for server to shut down.... done
server stopped场景二:在容器中使用 pg_ctl
官方 Docker 镜像内部不使用 systemd,docker exec 后的标准做法就是 pg_ctl:
# 容器内查看状态(注意:容器内用 postgres 用户身份)
docker exec -u postgres pg18 pg_ctl -D /var/lib/postgresql/18/docker status
# 重载配置(改完挂载的 pg_hba.conf 后)
docker exec -u postgres pg18 pg_ctl -D /var/lib/postgresql/18/docker reload
# 优雅停止时不建议用 pg_ctl stop:应让 Docker 发 SIGTERM
docker stop pg18 # 正确:Docker 向主进程发 SIGTERM,等价 fast
# docker exec pg18 pg_ctl stop -m fast # 可行,但绕过了 Docker 的生命周期管理容器环境下优先用 docker stop
容器的生命周期由运行时管理。用 pg_ctl stop 从内部停止会导致容器立即退出并被 --restart 策略重启,容易造成「停了又起」的困惑。让 Docker 发信号是更符合容器语义的做法。
场景三:排障——服务启动失败
# 1. 先看状态
pg_ctl -D /var/lib/postgresql/18/data status
# 2. 不带 -l 启动,把错误直接打到终端(临时排障技巧)
pg_ctl -D /var/lib/postgresql/18/data startpg_ctl: another server might be running; trying to start server anyway
waiting for server to start....2026-09-21 10:31:02.114 CST [31202] FATAL: could not create lock file "postmaster.pid": Permission denied
2026-09-21 10:31:02.114 CST [31202] LOG: database system is shut down
stopped waiting
pg_ctl: could not start server
Examine the log output.# 3. 典型原因排查清单
# 数据目录属主不对
ls -ld /var/lib/postgresql/18/data
sudo chown -R postgres:postgres /var/lib/postgresql/18/data
sudo chmod 700 /var/lib/postgresql/18/data
# postmaster.pid 残留且进程已不存在
sudo -u postgres cat /var/lib/postgresql/18/data/postmaster.pid | head -1
ps -p "$(sudo -u postgres head -1 /var/lib/postgresql/18/data/postmaster.pid)" || \
sudo -u postgres rm -f /var/lib/postgresql/18/data/postmaster.pid
# 端口被占用
sudo lsof -i :5432
# 配置语法错误(此时错误只在日志里)
tail -50 /var/log/postgresql/pg18.log场景四:让 pg_ctl 在系统启动时自动运行(无 systemd 环境)
# /etc/init.d/postgresql-18(SysV 风格,适用于无 systemd 的精简系统)
#!/bin/sh
# chkconfig: 2345 98 02
# description: PostgreSQL 18 (source build)
PGBIN=/usr/local/pgsql-18/bin
PGDATA=/var/lib/pgsql/18/data
PGLOG=/var/log/pgsql/pg18.log
case "$1" in
start)
su - postgres -c "$PGBIN/pg_ctl -D $PGDATA -l $PGLOG -w start"
;;
stop)
su - postgres -c "$PGBIN/pg_ctl -D $PGDATA -m fast -w stop"
;;
reload)
su - postgres -c "$PGBIN/pg_ctl -D $PGDATA reload"
;;
status)
su - postgres -c "$PGBIN/pg_ctl -D $PGDATA status"
;;
esac
exit $?sudo chmod +x /etc/init.d/postgresql-18
# 注册到各运行级别并启动
sudo chkconfig --add postgresql-18 # RHEL 系
# sudo update-rc.d postgresql-18 defaults # Debian 系注意事项
pg_ctl不是守护进程,它执行完动作即退出;服务是否存活要看status或pg_isready,而不是pg_ctl进程- 忘记
-l会让启动期的关键错误信息随终端消失,是最常见的「启不起来又查不到原因」 stop不带-m时使用默认模式,Debian 系的默认是fast,但显式写出-m fast更安全清晰smart模式会等待所有客户端断开,带连接池或长事务的系统上可能长时间不停,关机流程会被卡住immediate之后第一次启动会做崩溃恢复,期间可能拒绝连接;不要在自动化脚本中使用-o参数必须整体加引号,且其中指定的配置会覆盖配置文件,容易造成「改了配置却不生效」的困惑restart是「stop + start」的组合,并不是原子操作;若 stop 阶段失败,服务会一直处于停止状态- 数据目录权限必须是
700,属主必须是运行用户,否则pg_ctl会以权限错误启动失败 postmaster.pid残留时pg_ctl会拒绝启动;清理前务必确认原进程确实已不存在,否则会启动出两个实例-w等待默认超时为 60 秒;大shared_buffers或需重放大量 WAL 时启动可能超时,可用-t调整- 容器内不要用
pg_ctl stop代替docker stop,否则会与容器的重启策略冲突
最佳实践
- 把
pg_ctl调用统一封装成脚本(含PGBIN/PGDATA/PGLOG变量),避免每条命令手工敲长路径 - 始终显式写出
-m fast,让停机行为在所有环境完全一致 - 生产环境停止务必走业务确认 +
pg_ctl -m fast,把immediate只保留给故障应急 - 用
reload代替restart处理pg_hba.conf与动态参数,减少不必要的中断 - 启动前用
pg_isready或pg_ctl status做前置判断,避免重复启动产生第二个实例 - 日志文件路径使用绝对路径,并纳入
logrotate管理,防止单文件无限增长 - 优先使用系统的 systemd 单元来托管服务,只在没有 systemd 或用容器时才直接用
pg_ctl - 在自动化脚本中配合
set -euo pipefail与显式退出码判断,让启停失败能被上游感知 - 关机脚本中使用
-m fast -t 60,避免系统关机因数据库不响应而超时 - 在监控中采集
pg_ctl status返回码或pg_isready结果,作为数据库存活的独立判据
下一节
继续学习:systemd 服务管理