☰
Linux部署PostgreSQL全流程:初始化配置、性能调优与备份恢复
2026/10/15 7:02:13 网站建设 项目流程

在 Linux 上部署 PostgreSQL,听起来像是敲几条命令就完事的事儿,可真到动手时你会发现,真正的坑基本不在“安装”这一步,而在初始化配置、访问控制、参数调优和后续的备份意识上。前阵子我把一套运行在旧测试机上的 PostgreSQL 迁到新的 4 核 8G 云主机,整个过程从选版本、换官方源、初始化数据目录,到调参数、验证连接、做备份恢复,全部重新来了一遍。这篇文章就是那次实测的记录。如果你正打算在 Linux 上部署 PostgreSQL,或者装完之后总觉得哪里不放心,可以拿我的流程当参照,能省下不少排查时间。

1. 动手前先想清楚两件事:版本选型和数据目录规划

很多人的第一个动作是yum install postgresql-server或者apt install postgresql,装上再说的确快,但代价往往过几天才显现。我吃过几次亏之后,现在部署前都会先花十分钟想两件事:系统软件源里的 PostgreSQL 版本够不够用,以及数据库的数据目录放在哪里才合理。

1.1 系统自带包不一定适合生产环境

先说版本。Linux 各发行版的默认软件源有一个共同特点:稳,但相对保守。我有一回在一台 CentOS 系的机器上执行yum install postgresql-server,装完一查版本,发现还停在较老的主版本,而业务代码里已经在用新版本才支持的分区语法,应用一启动就报语法错误。那次只能卸载重装,动静不小。后面我就学乖了:先用yum info postgresql-server或apt-cache policy postgresql看仓库里是什么版本,再决定走哪条路。

发行版家族默认源常见版本官方源可选版本我的建议
CentOS / RHEL 系通常为旧版 LTS当前所有正式版本生产环境优先用官方源指定主版本
Debian / Ubuntu 系随发行版配套版本当前所有正式版本根据业务兼容性选主版本

如果你只是本地学习,系统自带包完全够用;但如果是给业务用,我强烈建议直接到 PostgreSQL 官方软件源或官方二进制包来装,后面我也会给出实际命令。选版本时也别盲目追新,我的习惯是选社区已经比较成熟的主流版本带lts属性或者至少发布半年以上的版本,兼容性资料多,踩坑时好搜。

1.2 数据目录单独挂盘,别和系统盘抢空间

第二个容易被忽视的问题是数据目录。默认安装下,数据文件放在/var/lib/pgsql或/var/lib/postgresql这类路径,它们通常和/根分区在同一个逻辑卷或磁盘分区上。这个设计本身能用,但有个隐患:系统日志、软件包、临时文件会和数据库共享同一块磁盘空间。一旦系统盘被写满,数据库会拒绝写入,最尴尬的是你想清理空间时,发现满的正是根分区。

我这次部署时单独准备了一块数据盘,目录规划成/data/pgdata。步骤很简单:先挂载数据盘到/data,创建目录,并把属主交给数据库用户,例如chown postgres:postgres /data/pgdata,这样后续的 initdb 就能直接把数据目录指过去。把数据库和系统盘分开,遇到磁盘空间问题时处理起来会从容很多,日常备份也建议落到另一个挂载点,避免备份和源数据互相挤占。

2. 安装与初始化:从添加官方源到第一次启动验证

这个部分记录我实际执行的安装过程。官方源的好处是版本可选、升级路径清晰,而且不会因为发行版大版本升级导致数据库版本被迫跟着变。

2.1 官方源的添加方式:两家发行版家族的命令都给你

RHEL 系(CentOS、Rocky、AlmaLinux 类)先装仓库 RPM,再装指定版本的服务端。下面的命令以主版本 16 为例,你可以按需替换成 15、14 等版本号:

yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %distro_base)-x86_64/pgdg-redhat-repo-latest.noarch.rpm yum -y install postgresql16-server postgresql16-contrib

Debian 系(Debian、Ubuntu 类)需要先把签名密钥和源配置放进去,再执行安装:

sudo install -d /usr/share/postgresql-common/pgdg sudo wget -O /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc https://www.postgresql.org/media/keys/ACCC4CF8.asc echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list sudo apt update sudo apt install postgresql-16 postgresql-client-16

这里有个小细节:装完postgresql16-server后,RHEL 系会创建/usr/pgsql-16/bin和系统用户 postgres;Debian 系则默认创建/usr/lib/postgresql/16/bin。路径不同,后续操作时对应好自己的环境。

2.2 initdb 之前,先把用户、目录和权限准备好

官方 RPM 包安装完会自动创建数据库用户,也会自动初始化到默认数据目录/var/lib/pgsql/16/data。但如果你像我一样想把数据目录放到独立数据盘,就需要手动执行一次 initdb。我建议把这件事放在启动之前做,否则系统已经接管了默认数据目录,改起来麻烦。

mkdir -p /data/pgdata chown postgres:postgres /data/pgdata su - postgres -c "/usr/pgsql-16/bin/initdb -D /data/pgdata -E UTF8 --locale=en_US.UTF-8"

有几个参数值得展开。-E UTF8指定数据库默认编码为 UTF-8,现在几乎所有的业务场景都应该用这个编码;--locale决定排序和比较规则,如果在性能和通用性之间找平衡,可以选C.UTF-8,它比完整本地化规则更快,也不太容易出现不同环境排序结果不一致的问题。

2.3 第一次用 systemctl 启动,顺便验证基本状态

systemctl start postgresql-16 systemctl enable postgresql-16

启动完后我会立刻做两个基础验证。先看端口和进程:

ss -lntp | grep 5432 /usr/pgsql-16/bin/pg_isready -h 127.0.0.1 -p 5432

再切到 postgres 用户执行一次 SQL:

su - postgres psql -c "select version();"

这一步能同时验证服务进程、本地 socket、认证链路是否正常。之后再去改配置做远程连接,就有了可对比的基准状态。如果这里就报错,优先去/var/lib/pgsql/16/data/log或/var/log/postgresql下面看日志,错误信息一般都会直接告诉你原因。

3. 三个核心配置:内存、并发和检查点

默认配置能跑,但离“适合这台机器”还差得远。我调整配置时最常用的就三块:内存参数、并发连接数、检查点与 WAL 行为。这一节写成“先理解为什么,再上手改”的顺序,方便你真正看懂。

3.1 内存参数怎么推

先给出一张我在 8G 内存机器上的实际取值表:

参数建议值依据
shared_buffers2GB物理内存的 25% 左右,用于共享缓存页
effective_cache_size6GB总内存的 50%~75%,供查询计划器估算操作系统缓存
work_mem32MB配合连接数控制总内存占用

shared_buffers 是 PostgreSQL 自己的共享缓冲池,所有进程读取数据块都要经过它。取值一般在物理内存的 15% 到 25%,调得过高会挤压系统缓存,并给内核共享内存带来压力。effective_cache_size 不是真实分配的内存,它只是告诉计划器“操作系统大概还能缓存多少数据”,让它更敢用索引,数值可以给得宽松一些。

work_mem 要单独提醒一下:这不是全局一次性分配,而是每一个排序、Join 操作都可能申请这么多内存。它的实际占用上限需要用并发操作数去乘。比如 work_mem 设成 32MB,同时有 20 个复杂查询在做排序,理论上就可能吃掉 640MB,这还不算 shared_buffers。所以 work_mem 不能拍脑袋往大调,我在第 5 节用 pgbench 实测时就验证过这个代价。

3.2 并发连接数与连接池的取舍

max_connections 默认一般是 100,对小业务够用。但很多应用喜欢用短连接频繁连库,如果直接把 max_connections 调到 1000,数据库每次连接都要 fork 一个进程,内存和调度开销不小。我的经验是:数据库端 max_connections 保持 100~200,应用侧用连接池来复用连接,系统负载比单纯调大连接数要稳得多。

改完连接数后记得重新算内存预算。一个简单的估算方式:shared_buffers + 每个连接占用内存 × max_connections + work_mem × 预估并发操作数。把这几项加在一起,仍然不超过物理内存的 70% 左右,才算比较健康。超过的话,要么降并发,要么引入连接池,不要硬扛。

3.3 让检查点更平滑,避免周期性“卡顿”

数据库每过一段时间会把脏缓存页刷到磁盘,这个动作叫检查点。如果脏页刷得太集中,磁盘 IO 会出现一个明显的峰值,业务表现就是“每隔一阵子卡一下”。我在配置文件里一般这样调整:

checkpoint_timeout = 15min max_wal_size = 4GB min_wal_size = 1GB

WAL 文件就是数据库的“预写日志”,类似记账流水:事务提交先写流水,再改账本,这样崩溃后能重放恢复。把 max_wal_size 设大一些,可以减少触发检查点的频率,让脏页分散落盘。但也不要无脑设到 10GB 以上,否则磁盘空间和恢复时间都会被拖累。这个参数和业务写入量强相关,我的做法是从 15min 和 4GB 起步,观察一段时间系统 IO 峰值后再微调。

4. 访问控制实战:pg_hba.conf 的本地与远程配置

如果只在本机用 psql 连库,访问控制基本不用动。但绝大多数部署都绕不开“远程连接”这一步,而远程连接出问题的概率相当高。我对每个新部署都会从头理一遍这层逻辑。

4.1 先分清两层控制:监听地址和认证规则

PostgreSQL 对外连接有两层开关。postgresql.conf里的listen_addresses决定数据库进程监听在哪些地址上;pg_hba.conf则按客户端来源 IP、数据库名、用户名匹配认证方式。很多人远程连不上,第一反应是防火墙,实际上前面这两层没配好的情况更常见。

我把监听地址设置成:

listen_addresses = '*'

如果机器有多个网卡,也可以写成具体 IP,例如'192.168.1.10',这样更安全。需要注意的是,listen_addresses属于需要重启实例才能完全生效的参数,而pg_hba.conf修改后只需要reload就能生效。

4.2 pg_hba.conf 里到底写哪种认证方式

新版 PostgreSQL 默认推荐scram-sha-256,这是目前密码认证里比较安全的方案。下面是我常用的一组规则:

规则含义我的选择
local all postgres peer本机 postgres 用户通过 unix socket 登录保留,管理员维护用
host all all 0.0.0.0/0 scram-sha-256任意远端主机用密码登录不建议直接放公网
host all app_user 10.0.0.0/8 scram-sha-256内网指定网段访问生产环境推荐

实际生产环境不要写0.0.0.0/0加密码登录,至少限制到办公网或业务网段;如果有云安全组,还要在云控制台同步限制来源 IP。修改 pg_hba.conf 后执行:

su - postgres -c "psql -c 'select pg_reload_conf();'"

不重启实例,认证规则就能生效,这也是生产环境比较常用的操作。

4.3 创建业务账号,别让应用用超级用户跑

用超级用户 postgres 跑业务是很危险的习惯。部署时我会创建独立业务账号,只给最小权限。一个实操例子:

CREATE USER app_user WITH PASSWORD '一个足够复杂的密码'; CREATE DATABASE app_db OWNER app_user; GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;

这样应用连app_db时用的是app_user,即使密码泄露,也不至于把整库的管理权限暴露出去。后续需要给报表账号或者只读账号,再单独授权就好,别直接复用 app_user 的权限边界。

4.4 防火墙、SELinux 与最终连通验证

最后才是防火墙层。RHEL 系常配的是 firewalld:

firewall-cmd --permanent --add-service=postgresql firewall-cmd --reload

如果你的发行版默认开了 SELinux,还要确认数据库端口策略是否放行:

setsebool -P httpd_can_network_connect_db on

不过我更建议先用getenforce看看状态,再决定要不要动 SELinux,别把安全增强功能一刀切关掉。配置完,我会从另一台机器上执行:

psql -h 数据库IP -U app_user -d app_db

能正常连接并查询,远程访问这关才算真正过了。如果你本机连得上、远程连不上,又确认安全组和防火墙都放行了,那八成就是监听地址的问题,可以回到 4.1 再检查一遍。

5. 用 pgbench 做一次上线前的性能快照

配置改完之后,光凭感觉说“好像变快了”不行。我用 pgbench 做压力测试来验证,它是 PostgreSQL 自带的基准工具,安装 contrib 包时一般都会带上。

5.1 安装与初始化测试数据

RHEL 系在安装postgresql16-contrib时通常已经带了 pgbench,Debian 系需要单独确认postgresql-contrib是否安装。先用它初始化一个测试库:

pgbench -i -s 50 testdb

-s 50表示规模因子 50,生成的测试数据大概有 500 万条记录的量级,对于一台 8G 内存的机器来说规模适中。如果机器内存很小,可以把 50 改成 10,测试时间会缩短一些。

5.2 跑一轮默认配置,再调整后跑一轮

初始化完成后,先用默认配置跑 30 秒:

pgbench -c 16 -j 4 -T 30 -P 5 testdb

这里的-c 16是并发客户端数,-j 4是线程数,-T 30是测试时长,-P 5表示每 5 秒打印一次进度。我在这台 4 核 8G 的机器上,默认配置测出来大约是每秒三千多个事务。然后我重启数据库,把第 3 节里的参数改到 postgresql.conf,再跑同一份命令,TPS 有明显提升。

这不代表所有机器都会得到同样的比例,但它至少证明两件事:一是参数调整方向是对的,二是以后每次改完参数,都可以用同一条 pgbench 命令做横向对比,效果很直观。

5.3 观察哪些指标值得参考

pgbench 输出里我重点看 TPS 和延迟分布,另外配合top看 CPU 和内存。比如 CPU 打满,瓶颈可能不在数据库参数,而在硬件本身;内存 swap 增长明显,则要回头检查 shared_buffers 和 work_mem 是否给太高。做完这轮实测,我会把测试库删掉,避免残留脏数据影响后续排查:

DROP DATABASE testdb;

6. 备份与恢复:只做过一次还原,才叫有备份

部署完成后,我认为最重要的不是写多少配置,而是把备份恢复整个流程跑通。没有恢复验证过的备份,只能叫“备份文件”,不能叫“备份方案”。

6.1 逻辑备份 pg_dump 与定时脚本

对中小型业务,我最常用的还是pg_dump。它把数据按 SQL 或自定义格式导出,恢复灵活,跨版本迁移也能用。一个常用命令组合:

pg_dump -h 127.0.0.1 -U app_user -Fc -d app_db -f /backup/app_db_$(date +%F).dump

-Fc是自定义压缩格式,之后用pg_restore能按需恢复。恢复时先建一个空库再灌入:

createdb -U postgres app_db_restore pg_restore -U postgres -d app_db_restore /backup/app_db_2025-01-01.dump

恢复完成后我一般检查三样东西:表数量对不对、记录数大致对不对、业务最近写入的关键行是否都在。

6.2 物理备份 pg_basebackup 适合整库全量

如果需要更完整的物理备份,可以用pg_basebackup生成数据目录的物理副本:

pg_basebackup -h 127.0.0.1 -U backup_user -D /backup/base_backup -X stream -P

执行前要求wal_level至少是replica,并且要创建一个具备复制权限的专用账号,比如:

CREATE USER backup_user REPLICATION LOGIN PASSWORD 'backup_password';

物理备份的恢复场景是:服务器直接挂了,想快速拉起整套实例。它比逻辑备份更接近即时恢复,但跨大版本恢复不如逻辑备份灵活。所以我的做法是两者都做:每日逻辑备份用于误操作回滚,每周物理备份用于灾难恢复。

6.3 定期在测试环境演练恢复

我总跟人说,备份方案值不值钱,完全取决于恢复演练。至少一个季度做一次:找一台新机器,把最近的备份恢复出来,模拟一次“原库不可用”的切换。这个过程中你会发现自己之前没注意的坑,比如备份文件落在同一块盘、恢复时权限不对、字符集不一致。这次部署结束后,我也顺手写了一个简单脚本,每周自动把所有表记录数对比一次,异常就能早点发现。

7. 这次部署里我实际踩过的坑:从现象到根因

7.1 远程登录一直超时,根因居然不是防火墙

第一次配置完远程访问,我用另一台机器执行psql -h 数据库IP -U app_user -d app_db,结果卡了很久,最后报连接超时。当时第一反应是防火墙没放行,跑来跑去查了一圈,firewalld 状态正常,安全组也放行了 5432,但还是超时。

后来静下心按顺序排查,发现问题其实在postgresql.conf。我装的是官方 RPM,数据目录在自定义路径,配置也是从模板复制过来的,模板里listen_addresses默认是localhost。更麻烦的是,我改的配置文件并没有被当前实例真正读取。确认方法很简单:

ss -lntp | grep 5432

输出只有127.0.0.1:5432,说明监听地址不对。修改listen_addresses并重启实例后,连接马上就通了。这个坑告诉我:遇到连接问题,先按“监听 → pg_hba → 防火墙 → SELinux”的顺序层层验证,不要一上来就动防火墙。也是从这次开始,我养成了用ss -lntp验证监听地址的习惯。

7.2 shared_buffers 调太大,实例启动失败

还有一次我把 shared_buffers 从 2GB 调到 6GB,想着一台 8G 内存的机器应该顶得住。结果systemctl start postgresql-16半天没起来,查日志才发现,内核共享内存参数扛不住这么大的共享缓冲池。

原因是 PostgreSQL 的共享内存分配依赖系统内核参数kernel.shmmax(单个共享内存段上限)。当 shared_buffers 设置远超内核允许范围时,进程直接报错。解决方式有两种:一是调低 shared_buffers 到合理范围;二是同步调整kernel.shmmax和kernel.shmall,用sysctl.conf持久化。经过这次教训,我现在的原则是:shared_buffers 一般不超过物理内存的 25%,调大之后必须用 pgbench 或实际业务流量验证,而不是凭感觉图“越大越快”。

7.3 留给下次部署的检查清单

  • pg_hba.conf 改完后用pg_reload_conf()重载,不需要重启实例
  • postgresql.conf 里listen_addresses改完要用ss -lntp验证实际监听
  • 数据目录是否和系统盘分开
  • 备份文件是否和源数据落在同一个磁盘
  • 业务账号是否有足够但非超管的权限
  • 连接数、work_mem 是否和内存预算匹配
  • 远程测试要真实地从其他机器发起,而不是本机自连

最后分享一点我个人的运维体会。部署 PostgreSQL 这件事,装完并不代表结束,更多是在给后续的日常运维打基础。每次部署我都尽量把“基本配置、访问控制、性能快照、备份恢复”这四样一次做完,后面接手维护的人会省很多心。如果你也打算在 Linux 上自建 PostgreSQL,建议照着上面的流程走一遍,尤其是备份恢复那一节,千万别偷懒。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询