☰
CentOS 7.9 安装 PostgreSQL 9.6 完整避坑指南
2026/10/22 11:37:57 网站建设 项目流程

不到万不得已,没人愿意在一台新机器上装一个已经停止维护的数据库。但我上个月刚接了一个这样的活儿:某老客户的业务系统跑在 PostgreSQL 9.6 上,服务器是 CentOS 7.9,因为业务代码里依赖了 9.6 的某些老行为,加上线上数据文件格式就是 9.6 的,整体升级不是他们现阶段能接受的。我只能在一台全新 CentOS 7.9 上把 PostgreSQL 9.6 完整复刻出来,再把旧数据导进去。这篇文章就把整套流程写清楚,尤其是那些光看官方文档根本注意不到的坑。

先说结论:装 PostgreSQL 9.6 这个操作本身不难,难的是你大概率会在源选择、初始化、启动这几个环节被卡住。我把实际踩过的坑和排查思路都整理在下面了。

1. 装之前先确认:CentOS 7 自带的 PostgreSQL 不是 9.6,别等装完才发现

1.1 系统源里的“惊喜”版本

很多人拿到一台 CentOS 7.9,第一反应就是yum install postgresql-server。这个命令一定能装成功,但装出来的是 PostgreSQL9.2.24,不是 9.6。CentOS 7 自带 base 仓库里维护的 PostgreSQL 版本就是 9.2.x,9.6 从来就没有进过 CentOS 官方源。

我当时第一次在 7.9 上装数据库时也吃过这个亏,装完psql --version一看版本直接愣住。所以这里请大家务必记住:只要标题是“CentOS 7.9 安装 PostgreSQL 9.6”,就不要碰系统默认的 postgresql 相关包。先看一眼当前源里有什么:

yum list available postgresql* yum info postgresql-server

结果会很清楚:postgresql-server.x86_64 9.2.24。版本号明明白白,除非你就要 9.2,否则别装。

1.2 什么情况下才需要 9.6,什么情况下不建议

先帮大家分个类,别盲目跟风装老版本。如果你是新项目、新环境,没有任何历史包袱,请直接安装仍在维护的 PostgreSQL 14、15 或更高版本。9.6 早在 2021 年 11 月就停止支持了,不再有安全补丁,也不再有 bug 修复,长期用它等于把一个有已知漏洞的数据库暴露在业务里。

需要装 9.6 的典型场景是下面这几类:

  • 老系统替换服务器:原机器跑的是 9.6,应用代码里用到了pg_stat_activity某些字段、plpgsql 锁行为等新版本已经变化的东西,整个升级回归成本太高。
  • 旧备份文件恢复:pg_dump导出的 9.6 归档文件(-Fc格式)固然可以用新版本恢复,但结构、权限、扩展版本可能触发一系列兼容问题,迁移窗口又很短,不如原地复刻一个 9.6 实例。
  • 第三方系统强约束:某些商业系统对数据库版本有硬性要求,比如只认证 9.6,你在实施阶段没得选。

装之前务必把“这是临时过渡方案”这个判断明确告诉业务方。我是直接跟客户写明:9.6 只作为过渡期兼容环境,后续还是要排期升级高版本。

1.3 动手前的环境自检清单

确认环境不会给自己挖坑,我习惯先执行一轮基础检查:

cat /etc/redhat-release uname -m free -g getenforce systemctl status firewalld

重点是架构(x86_64 还是 aarch64)、内存大小、SELinux 状态和防火墙状态。后面每一步排查都跟这几个信息相关。内存如果只有 1G,9.6 默认参数跑起来会很吃力,建议先想好要不要调shared_buffers。

2. 配置 PGDG 软件源:官方源才是 9.6 的真正来源,附带依赖坑提示

2.1 为什么必须用 PGDG 源

PostgreSQL 官方有一个面向 Red Hat 系发行版的 YUM 仓库,叫 PGDG(PostgreSQL Global Development Group 的软件源),里面维护了从 9.6 到 16 的所有历史版本 rpm 包。CentOS 7 完全兼容这些 rhel 7 的包,所以装 9.6 的唯一正确来源就是 PGDG。

安装仓库 rpm 的方式很直接,用的也是官方通用地址:

yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

如果这个链接因为时间推移失效,就去download.postgresql.org/pub/repos/yum/reporpms/目录下找EL-7-x86_64对应的最新包,规则是一样的。装完以后,/etc/yum.repos.d/下会出现pgdg-redhat.repo之类的配置文件。

2.2 仓库启用逻辑:别让 yum 装错版本

我见过不少人在装完 PGDG 源后直接执行yum install postgresql96-server,结果提示找不到包,或者更离谱的是装成了系统源的 9.2。原因出在 repo 文件的启用状态上。

打开/etc/yum.repos.d/pgdg-redhat.repo看,里面通常有一整段,比如[pgdg96]、[pgdg14]、[pgdg15],每个段落的enable值可能是不一样的。你需要的那个pgdg96段不一定是默认启用状态。稳妥的做法是安装时显式指定仓库:

yum --enablerepo=pgdg96 install -y postgresql96-server postgresql96

如果你希望之后所有 PG 相关操作都默认走这个源,也可以把 repo 文件里其他版本的enable=0,把pgdg96的enable=1。不过我个人更推荐每次安装都带--enablerepo参数,这样不会影响其他源的默认状态。

2.3 GPG key 没导入的报错

第一次通过 PGDG 源安装时,大概率你会碰到这个提示:

Public key for postgresql96-9.6.24-1PGDG.rhel7.x86_64.rpm is not installed

这不是源有问题,而是仓库 rpm 自带的 GPG key 还没有被 rpm 数据库收录。解决办法是手动导入:

rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-PGDG*

然后重新执行安装命令。这里提醒一句:绝对不要用--nogpgcheck跳过校验。数据库这种核心组件,包完整性校验是完全不能省的。

2.4 依赖缺失问题

PGDG 仓库里的 9.6 依赖一般比较老,但在 CentOS 7.9 上通常都能顺利解决,因为 7.9 的库版本本身不算新。我实际操作中偶尔会遇到perl相关模块缺失的情况,这种多半是系统太“瘦”了。此时先补一个 EPEL 源再重试:

yum install -y epel-release yum --enablerepo=pgdg96 install -y postgresql96-server postgresql96

如果还报依赖错误,就老老实实看缺哪个包、从哪个源能装。千万不要用rpm -ivh --nodeps这种暴力方式绕过去,否则后面initdb或者服务启动时会在莫名其妙的地方崩掉,排查成本高得多。

3. 安装主程序和数据库初始化:目录结构跟旧版本不一样,别找错文件

3.1 安装并确认版本

仓库配置好以后,安装就是一条命令的事:

yum --enablerepo=pgdg96 install -y postgresql96-server postgresql96

装完先验证版本,不要直接开始初始化:

/usr/pgsql-9.6/bin/postgres --version

正常会输出postgres (PostgreSQL) 9.6.24之类的结果。你注意看路径,9.6 的程序目录是/usr/pgsql-9.6,不是/usr/bin。这也是很多教程没强调的地方:9.6 版本的程序、工具都集中在带版本号的目录下,psql、pg_dump、initdb都在/usr/pgsql-9.6/bin/下。

3.2 数据目录为什么在 /var/lib/pgsql/9.6/data

CentOS 下安装 PostgreSQL,无论哪个版本,数据目录默认都在/var/lib/pgsql/下,但 9.6 的路径里面多了一层版本号:

  • 程序目录:/usr/pgsql-9.6/bin/
  • 数据目录:/var/lib/pgsql/9.6/data/
  • 配置文件:/var/lib/pgsql/9.6/data/postgresql.conf
  • 认证配置:/var/lib/pgsql/9.6/data/pg_hba.conf

这意味着你千万不要去/etc/postgresql/或者/var/lib/pgsql/data/找配置文件,这是新手最容易卡住的地方。我见过有人在/var/lib/pgsql/data找半天,最后才发现真正的数据目录多了个版本号。

3.3 初始化命令:为什么是 postgresql96-setup

初始化数据库这一步,9.6 提供的是一个跟 systemd 紧密配合的脚本:

/usr/pgsql-9.6/bin/postgresql96-setup initdb

执行后如果一切正常,末尾会看到类似Success. You can now start the database server的提示。这个脚本做了什么?它会检查数据目录是否存在、目录属主是否为空、必要时创建postgres系统用户,并且把初始化参数按 systemd unit 文件的预期来设置。所以我建议直接用脚本,而不是自己手动initdb。

有人可能会问:我能不能自己指定字符集?可以,但没必要折腾。CentOS 7 默认 locale 一般是en_US.UTF-8,初始化出来的数据库编码就是 UTF8,足够满足绝大多数场景。如果你明确需要指定:

su - postgres -c "/usr/pgsql-9.6/bin/initdb -D /var/lib/pgsql/9.6/data --encoding=UTF8 --locale=en_US.UTF-8"

自己手动 initdb 不是不行,但要注意后面 systemd 的PGDATA环境变量必须跟实际路径一致。用官方脚本就完全不用操心这个对齐问题。

3.4 数据目录属主问题

初始化脚本一般会把/var/lib/pgsql/9.6/data的属主搞对,但如果你是手动创建的父目录,或者从别的机器拷贝了数据目录过来,这一步很容易翻车。检查一下:

ls -ld /var/lib/pgsql/9.6/data

属主应该是postgres:postgres。如果不是,启动时 9.6 会直接报could not open directory之类的权限错误。手动修正:

chown -R postgres:postgres /var/lib/pgsql/9.6

这条命令要在 root 下执行,并且最好在 initdb 之前就处理好。

4. 启动服务与开机自启:服务名是 postgresql-9.6,以及一次启动失败的完整排查

4.1 服务名容易打错

9.6 的 systemd 服务名是postgresql-9.6,注意中间有版本号,不是postgresql96,更不是postgresql。正确的启动方式:

systemctl start postgresql-9.6 systemctl enable postgresql-9.6 systemctl status postgresql-9.6

如果你执行systemctl start postgresql-9.6时提示找不到服务,大概率是第二步初始化(initdb)没有执行成功,systemd 找不到对应的配置。此时回到上一节,把初始化脚本重新跑一遍。

4.2 一次典型的启动失败排查链路

我在这台 7.9 上安装时,第一次systemctl start就翻车了。systemctl status postgresql-9.6显示inactive (dead),没有任何有效信息。这里分享我的完整排错思路,比直接告诉你怎么解决更实用。

第一步,看 systemd 日志:

journalctl -u postgresql-9.6 -n 100 --no-pager

如果日志里什么都没有,或者只有模糊的 kill 信息,第二步就要看 PostgreSQL 自己的日志:

ls -lt /var/lib/pgsql/9.6/data/pg_log/

9.6 默认会在数据目录下开pg_log文件夹存放运行日志,启动失败的具体原因都在这里。

第三步,如果还看不出来,看 SELinux 审计日志:

ausearch -m avc -ts recent

我这次碰到的实际问题:journal 里只有一句Unit postgresql-9.6.service entered failed state,但pg_log里根本没有日志文件,后来用ausearch才发现是 SELinux 拦截了进程对数据目录的访问。SELinux 默认策略下,PostgreSQL 数据目录的标签如果不对,启动时进程会被直接拒绝。

修复方式也很直接:

restorecon -Rv /var/lib/pgsql/9.6

如果你的数据目录是从别处拷贝过来的,这个命令基本每次都能救你。这个坑非常隐蔽,网上教程很少提到,因为大多数博客是在 SELinux 关闭的环境下写的。

4.3 常见启动失败原因速查表

把这几年遇到过的启动问题整理成一张表,方便大家对着排查:

报错关键字原因处理方式
data directory ... does not exist没有 initdb 或 PGDATA 路径不对检查环境变量,执行 postgresql96-setup initdb
could not create shared memory segment共享内存参数问题检查/dev/shm大小,调整shared_buffers
Permission denied且 AVC 有记录SELinux 拦截restorecon -Rv /var/lib/pgsql/9.6
postmaster.pid相关报错上次非正常关闭残留确认无进程后删除postmaster.pid
No usable systemd notify support系统目录权限问题检查/var/lib/pgsql 属主
could not bind to 127.0.0.1端口被占用ss -lntp查看端口占用

4.4 启动成功后的验证动作

服务起来后,别急着高兴,先做两个验证。一个是看端口:

ss -lntp | grep 5432

另一个是用pg_isready探活:

/usr/pgsql-9.6/bin/pg_isready -h 127.0.0.1 -p 5432

返回accepting connections才算真正 OK。systemctl enable postgresql-9.6设置完开机自启后,建议直接重启一次服务器验证服务能自动拉起。这一步很多人偷懒不做,等到客户那边断电重启才发现服务没起来。

5. 安全配置与远程访问:密码、监听、防火墙、SELinux 一次说清楚

5.1 设置 postgres 用户密码

数据库实例刚初始化完,本地通过peer认证可以免密登录:

sudo -u postgres psql

登录后要立刻设置密码:

ALTER USER postgres PASSWORD '你的强密码';

这里有一个 9.6 级别的关键认知:9.6 只支持 md5 认证方式,不支持scram-sha-256。scram-sha-256是 10 版本才引入的。所以写pg_hba.conf时认证方法只能写md5。很多从高版本转回来的人在这里会踩坑,写了scram-sha-256导致连接永远失败。

5.2 创建业务账号和数据库

生产环境不建议直接用postgres超级用户跑业务。创建一个独立账号,把权限控制在业务库内:

CREATE ROLE app_user LOGIN PASSWORD 'app_pass'; CREATE DATABASE app_db OWNER app_user ENCODING 'UTF8';

如果业务项目用了扩展,也要提前在目标库里创建:

\c app_db CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS pg_trgm;

扩展是按库存储的,不是全局的。在postgres库里创建完,换到业务库还要再建一次。这也是个容易忘记的细节。

5.3 配置 pg_hba.conf:顺序很重要

编辑/var/lib/pgsql/9.6/data/pg_hba.conf,这是远程连接成败的关键文件。默认配置里,本地连接是peer,远程 host 连接是ident。ident认证对大多数应用来说不是你要的,所以必须改成md5。

我常用的配置片段:

# 保留本地超级用户 peer 认证 local all postgres peer # 本机回环访问用 md5 host all all 127.0.0.1/32 md5 # 内网网段访问用 md5 host all all 192.168.10.0/24 md5

pg_hba.conf的匹配规则是从上到下匹配第一条,所以更宽松的规则要放到更下面,更精确的规则放上面。比如你只让内网访问,就不要写all,直接把网段写死。假如你想限制某个应用账号只能连某个库,可以写:

host app_db app_user 192.168.10.5/32 md5

5.4 修改 postgresql.conf 开启监听

默认情况下 PostgreSQL 只监听localhost,要对外提供服务必须修改:

listen_addresses = '*' port = 5432

postgresql.conf同样在数据目录下:/var/lib/pgsql/9.6/data/postgresql.conf。修改后有两种生效方式:

systemctl reload postgresql-9.6

reload不需要重启,对已有连接影响最小。如果你改了listen_addresses以外的核心参数,比如shared_buffers,那就要systemctl restart postgresql-9.6才能完整生效。这里我建议第一次修改时直接重启,避免 reload 后配置生效不完整的错觉。

5.5 防火墙放行

CentOS 7 默认使用 firewalld。放行 PostgreSQL 有两种方式,第一种是按服务名:

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

第二种是按端口更直观:

firewall-cmd --permanent --add-port=5432/tcp firewall-cmd --reload

检查放行结果:

firewall-cmd --list-all

注意,如果服务器上跑的是自定义 iptables 服务而非 firewalld,那还要单独处理 iptables 规则。用systemctl status firewalld看下防火墙用的哪个,别改了半天没生效才发现。

5.6 SELinux:默认端口没事,改端口就会卡你

CentOS 7 默认 SELinux 是 enforcing 状态。PostgreSQL 的默认端口 5432 在 SELinux 策略里属于postgresql_port_t,不需要额外放行。但如果你把端口改成 5444 之类的自定义端口,SELinux 会直接拦截远程连接,而且本地psql可能一切正常,从外部连就是超时。

这种情况要给新端口加标签:

semanage port -a -t postgresql_port_t -p tcp 5444

如果系统没有semanage,先装:

yum install -y policycoreutils-python

判断是不是 SELinux 的问题,用ausearch -m avc -ts recent,命中postgresql_port_t相关的记录基本就是它。

5.7 远程连接验证

配置完成后,从客户端执行:

psql -h 服务器IP -p 5432 -U app_user -d app_db

如果连不上,按这个顺序排查:

  1. ss -lntp | grep 5432,确认 postgres 进程真的监听在0.0.0.0:5432或具体网卡上。
  2. telnet 服务器IP 5432,确认 TCP 层是否可达。
  3. ausearch -m avc -ts recent,确认 SELinux 有没有拦截。
  4. 再次确认pg_hba.conf里 host 记录的顺序和认证方式。

大部分远程连接失败,最后都能落到pg_hba.conf写错或者防火墙没放行这两个原因上。

6. 9.6 的老版本脾气:数据备份、后续升级路径与运维提醒

6.1 备份习惯必须比新版本更严

9.6 已经停止维护,这意味着它不会有安全补丁。越是这样,备份就越重要。最基本的逻辑备份命令:

/usr/pgsql-9.6/bin/pg_dump -U app_user -F c -d app_db -f /backup/app_db.dump

-F c是自定义格式,配合pg_restore恢复时非常灵活。我习惯再加一个定时任务:

0 2 * * * /usr/pgsql-9.6/bin/pg_dump -U app_user -F c app_db > /backup/app_$(date +\%F).dump

除了逻辑备份,物理备份也别忽略。最简单可靠的办法是停库后打包数据目录:

systemctl stop postgresql-9.6 tar -czf /backup/pgdata_$(date +%F).tar.gz /var/lib/pgsql/9.6/data systemctl start postgresql-9.6

对于 9.6 这种老版本,我建议逻辑备份和物理备份同时保留,恢复时多一条路。

6.2 升级路线:最好别一把梭

9.6 的数据不能一直趴在老版本上,迟早要升级。升级路线我先劝一句:不要在生产环境直接尝试跨多个大版本的pg_upgrade。9.6 到 14 以上跨度太大,二进制文件格式和系统表结构变化都不小,虽然工具理论上支持,但出问题的概率和排查成本都很高。

稳妥路线是:

  1. pg_dump全量备份数据。
  2. 在新服务器上安装目标版本(如 14 或 15),初始化全新实例。
  3. pg_restore恢复数据。
  4. 业务侧做完整回归测试。
  5. 切换连接串。

这条路线看起来多花一点时间,实际上最省心。唯一要注意的是老库里的某些扩展,比如uuid-ossp在新版本里可能已经被并入pgcrypto或其他模块,恢复时要提前处理。

6.3 老版本环境的一些基础参数建议

安装和启动都搞定后,我根据经验还会顺手做几件事:

  • 把postgresql.conf里的timezone显式设为Asia/Shanghai,避免系统时区变更影响业务。
  • 根据内存调整shared_buffers,一般取物理内存的 25% 左右,别直接跑默认值,9.6 默认配置对 4G 以上内存的机器来说太保守。
  • 如果并发连接多,建议引入连接池(比如常见的 PgBouncer),9.6 的max_connections默认只有 100,扛不住大量短连接。
  • 把log_line_prefix调成带时间戳的格式,方便排查问题:
log_line_prefix = '%m [%p] %q%u@%d '

这些调整虽然不能改变 9.6 老版本的事实,但能让它在一台新机器上跑得稳定些。

最后再分享一点个人体会:搞定这台 9.6 之后,我让同事把整个操作流程和踩坑记录整理成了内部文档,第二台同配置服务器半小时就装完了。老版本就是这样,第一台总是最痛苦的,但只要你把源、目录结构、SELinux 这几个关键点摸透,后面就是无脑复制。整体上来讲,9.6 能不用就别用,但既然业务需要,那就把每一步做扎实,同时把升级计划排上日程,别让业务一直趴在停止维护的数据库上。

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

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

立即咨询