Skip to content

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 仍是默认,但等待逻辑更精确。

语法与用法 ​

通用语法 ​

bash
pg_ctl [子命令] [通用选项] [子命令专属选项]

通用选项 ​

参数类型默认值说明
-D, --pgdata=DIR路径$PGDATA数据目录;几乎每条命令都需要
-l, --log=FILE路径无把服务端 stdout/stderr 重定向到文件
-s, --silentflag关闭只打印错误,不打印普通提示
-t, --timeout=SEC整数60等待操作完成的最长秒数
-w, --waitflag开启等待操作真正完成(推荐保持)
-W, --no-waitflag——不等待,命令立即返回
-o, --options=OPTS字符串无传给 postgres 的启动参数,必须整体加引号
-p, --pgdata(旧写法)路径无历史遗留,不建议使用
-V, --versionflag——打印版本
-?, --helpflag——打印帮助

-o 参数必须整体加引号

-o 后面是「一串要原样传给 postgres 进程的参数」,shell 会按空格拆分,所以必须写成 -o "-c shared_buffers=4GB -p 5433" 这种整体引用的形式,否则 -c 会被 pg_ctl 自己解析而报错。

子命令一览 ​

bash
# 初始化集群(封装 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)等待服务真正可以接受连接后才返回。

bash
# 最简启动:必须指定 -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 start
text
waiting for server to start.... done
server started

为什么一定要写 -l

不指定 -l 时,pg_ctl 会把服务端输出继承到当前终端;一旦终端关闭,这些输出就彻底丢失。服务端的关键启动错误(如配置语法错误、端口占用)恰恰只出现在这里。生产环境必须指定日志文件。

stop 子命令与三种停止模式 ​

这是 pg_ctl 最需要理解的部分。三种模式对应不同的信号,行为差异巨大:

bash
# 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
模式对应信号对现有连接对未提交事务是否需要崩溃恢复适用场景
smartSIGTERM等待客户端主动断开允许提交完成否计划内维护、业务已确认停机
fastSIGINT立即断开所有连接全部回滚否日常停机的默认选择
immediateSIGQUIT直接杀死进程全部回滚是fast 卡死、进程无响应时
text
# 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 stopped

immediate 不是「更快」,而是「更危险」

immediate 模式下 PostgreSQL 不写关闭检查点,也不保证数据文件已刷盘,下次启动必须重放 WAL 做崩溃恢复。它只应在 fast 长时间无法结束时作为最后手段使用。恢复期间数据库可能拒绝连接或性能极差。

reload 与 restart 的取舍 ​

bash
# 只重载配置:现有连接不中断,新连接按新规则处理
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_memreload
需要重启的参数必须 restartshared_buffers、max_connections、wal_level、portrestart
已废弃的参数————检查日志中的警告

实践中不必死记清单:发送 reload 后,PostgreSQL 会在日志中对无法动态生效的参数打印提示。

text
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" reloaded

status 与 kill 子命令 ​

bash
# 查看状态:运行中返回 0,未运行返回 3,数据目录无效返回 4
pg_ctl -D /var/lib/postgresql/18/data status
text
pg_ctl: server is running (PID: 24318)
/usr/lib/postgresql/18/bin/postgres "-D" "/var/lib/postgresql/18/data"
bash
# 未运行时
pg_ctl: no server running
bash
# 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)"
信号等价操作说明
SIGHUPpg_ctl reload重载配置
SIGINTpg_ctl stop -m fast快速关闭
SIGTERMpg_ctl stop -m smart智能关闭
SIGQUITpg_ctl stop -m immediate立即关闭(危险)
SIGUSR1——切换日志文件
SIGUSR2——切换 WAL 段

实战示例 ​

场景一:源码编译环境下的标准启停脚本 ​

bash
#!/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
text
$ ./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:

bash
# 容器内查看状态(注意:容器内用 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 发信号是更符合容器语义的做法。

场景三:排障——服务启动失败 ​

bash
# 1. 先看状态
pg_ctl -D /var/lib/postgresql/18/data status

# 2. 不带 -l 启动,把错误直接打到终端(临时排障技巧)
pg_ctl -D /var/lib/postgresql/18/data start
text
pg_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.
bash
# 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 环境) ​

ini
# /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 $?
bash
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 服务管理

参考链接 ​