PostgreSQL 13 正式发布之后,我一直在犹豫要不要把生产环境的数据库从老版本迁过来。2021 年初终于下定决心做一轮基础环境升级,选定的方式就是源码安装,而不是直接用发行版自带的 yum 或 apt。原因很简单:我需要在一台服务器上同时管理多个实例,还要按业务场景裁剪编译参数,包管理器给不了这种灵活度。这篇文章记录的就是 2021 年 3 月这次 PostgreSQL 13 源码安装的完整过程,包括依赖准备、configure 参数取舍、initdb 初始化和排障实录。如果你也想搞明白源码安装背后每一步的原因,或者正准备动手编译一次,这篇文章可以直接拿来当参考。
1. 为什么选源码安装:先想清楚再动手
1.1 包管理与源码编译的取舍
很多人一听源码安装就觉得麻烦,其实要先问自己一个问题:你到底需要什么?如果只是本地开发跑一下,包管理器一条命令装完完全够用。但一旦涉及下面这些场景,源码安装的优势就出来了:
- 版本精度要求高。发行版仓库里的 PostgreSQL 经常滞后一到两个大版本,而且小版本更新节奏也不可控。我要用 13.x 的特定小版本时,包管理器给不了。
- 多实例共存。同一台机器上要跑两个以上不同版本的 PG,只有源码安装能保证每个版本完全隔离,互不干扰。
- 编译参数定制。比如我要用
--with-openssl开启 SSL 支持、--with-perl做 pl/perl 开发、调整--with-segsize这类底层的页面段大小,这些只能通过源码编译阶段注入。 - 目录布局自主可控。生产环境里我习惯把程序和数据彻底分开,程序放
/usr/local/pgsql13,数据放独立的/var/lib/pgdata,包管理器装出来的布局很难完全按我的规范来。
| 对比项 | 包管理器安装 | 源码安装 |
|---|---|---|
| 安装速度 | 快,几分钟 | 较慢,编译时间取决于机器性能 |
| 版本控制 | 受仓库限制 | 完全自定义 |
| 参数定制 | 基本固定 | configure 阶段全可控 |
| 多版本共存 | 较麻烦 | 天然支持 |
| 维护成本 | 低,yum/apt 直接管理 | 需要自己管理升级和 service 文件 |
当然,源码安装的代价也很实在:编译要花时间,依赖要手动补齐,升级要自己处理。所以做这个决定之前,先把需求列清楚,别为了“显得专业”而盲目编译。
1.2 环境确认:系统信息与编译工具链
这次安装的目标机器是一台 CentOS 7.9 服务器,内核版本比较老,但 PostgreSQL 13 对操作系统的要求并不苛刻,Linux 3.x 内核以上都能跑。动手之前先确认三件事:系统版本、可用内存和磁盘空间。
cat /etc/centos-release uname -r free -h df -h /usr/local /var/lib编译 PostgreSQL 13 对内存和磁盘的要求不高,2G 内存、10G 可用磁盘就足够了,但如果你要make -j并行编译,内存建议至少 4G,否则容易把机器压到 swap。磁盘方面,源码解压后约 1GB 左右,编译中间文件会再占一部分,我一般至少预留 5GB 保险。
工具链检查也不能省。PostgreSQL 13 对编译器版本有硬性要求:GCC 6 以上,因为代码用到了 C99 标准的一部分特性。CentOS 7 自带的是 GCC 4.8.5,直接编译会报错,必须先把 devtoolset 升级上去。这一步很多人踩坑,我后面会专门讲。
2. 编译安装核心流程:每一步都要知道为什么
2.1 依赖准备:缺一个库都过不了
源码安装最烦的就是依赖,但也是有规律可循的。PostgreSQL 13 编译需要以下几类依赖,我按照必装和可选分开列:
必装依赖:
- gcc、make:编译器和构建工具
- readline-devel:命令行交互支持,没有它 psql 进去你会发现编辑功能像残废一样
- zlib-devel:压缩支持,pg_dump 备份压缩和 WAL 日志的某些功能依赖它
可选依赖(按需安装):
- openssl-devel:启用 SSL 连接,生产环境强烈建议装
- libxml2-devel、libxslt-devel:XML 和 XSLT 相关功能,用不到可以不装
- perl-devel、python3-devel:pl/perl、pl/python 存储过程支持
- bison、flex:生成 SQL 语法分析器,PostgreSQL 14 之后才不再依赖 bison,13 必须要装
CentOS 7 上我执行的安装命令是这样的:
yum install -y gcc make readline-devel zlib-devel openssl-devel yum install -y bison flex yum install -y perl-ExtUtils-Embed python3-devel注意一个问题:如果直接跑,bison 装出来可能是 2.7 版本,PostgreSQL 13 要求 bison 2.3 以上就够,所以这块问题不大。真正麻烦的是 GCC 太老,我单独处理:
yum install -y centos-release-scl yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++ scl enable devtoolset-7 bash gcc --version执行scl enable后当前 shell 就切到了 GCC 7,gcc --version显示 7.x 才算通过。这里有个细节:scl enable只在当前终端会话生效,如果你关掉终端再开,还得重新执行。我建议直接把编译那一步和这个命令放在同一个脚本里,避免后面忘了环境导致编译失败。
2.2 下载源码与校验:别跳过这一步
PostgreSQL 官方源码包可以从官网下载,国内也有镜像站。我下载的是 13.2 版本,文件名是postgresql-13.2.tar.gz。下载完成之后一定先做校验,这是我吃过亏才养成的习惯:有一次下载文件不完整,configure 阶段报了各种莫名其妙的错,最后才发现是包损坏。
wget https://ftp.postgresql.org/pub/source/v13.2/postgresql-13.2.tar.gz sha256sum postgresql-13.2.tar.gz校验值去官网对照,确认一致再继续。如果你用的是公司内部源或镜像,同样要校验,只是对照的校验值来源不同。这一步花不了 30 秒,但能帮你省掉后面几个小时的排查时间。
解压并进入目录:
tar -zxvf postgresql-13.2.tar.gz cd postgresql-13.22.3 configure 参数详解:定制从这里开始
configure 是整个源码安装的灵魂,它的作用是检查系统环境、生成 Makefile,并把你要启用的功能标记进去。我这次用的完整参数是:
./configure \ --prefix=/usr/local/pgsql13 \ --with-openssl \ --with-perl \ --with-python \ --with-libxml \ --with-systemd逐个解释这些参数背后的原因:
--prefix=/usr/local/pgsql13:指定安装目录。编译安装默认是/usr/local/pgsql,但我要在机器上跑两个版本,所以把版本号写进目录名做区分。目录名定下来之后,后面的 PATH、systemd 服务文件都会跟它绑定,提前想清楚很关键。
--with-openssl:启用 SSL 支持,数据库层面对外的加密连接依赖这个选项。生产环境不管现在用不用得到,建议都加上,后面想开 SSL 就不用重编译了。
--with-perl和--with-python:启用存储过程语言支持。即使暂时不用 pl/perl 和 pl/python,编译进去的成本很低,后面业务提需求时你直接能用。
--with-libxml:XML 数据处理功能,PostgreSQL 的 XML 类型和 xpath 查询依赖它。
--with-systemd:这个参数很实用,它让 PostgreSQL 原生支持 systemd 通知机制。配合 systemd 管理服务时,start/stop 状态同步会更准确,不会出现“进程其实挂了但 systemctl 还显示 active”的尴尬情况。
还有个参数需要单独说明,--with-pgport=5432。如果你默认端口就是 5432 可以不指定,但多实例场景下我习惯第二个实例用 5433,编译时就把默认端口定好,后面 initdb 和 psql 都不用每次加-p参数了。
configure 执行完后,记得看一眼输出末尾有没有configure: error之类的信息,常见的错误就是某个依赖库找不到。比如readline library not found,就是 readline-devel 没装好。这一步没报错不代表万事大吉,但它至少表明基础环境是通的。
2.4 编译与安装:耐心等待别乱来
configure 通过之后就是编译,这一步没什么技术含量,但有几个细节值得说。
make -j4-j4表示用 4 个线程并行编译,可以显著缩短时间。线程数怎么选?我一般是看 CPU 核数,比核数少一个左右比较稳妥。比如 8 核机器用-j7,如果同时还有其他服务在跑,适度调低到-j4,避免编译把机器卡死。实测在 4 核的虚拟机里,不用-j编译 PostgreSQL 13 大概要 15 分钟,开了-j4能压到 6 分钟以内,差距非常明显。
编译过程如果中途报错,别急着重复make -j。先看报错信息,90% 的情况是两种:提示缺头文件(某个 -devel 包没装)、提示编译器版本不够(GCC 太老)。针对性地处理完,再重新编译。这里有个小技巧:make失败后的重跑,一般不需要make clean,直接再make会从断点继续。但如果 configure 之后改动过参数,才需要make distclean全部重来。
编译完成后执行安装:
make install安装过程很快,把二进制文件拷贝到/usr/local/pgsql13/bin和共享文件到对应目录。安装完可以先看看关键的几个文件是否完整:
ls /usr/local/pgsql13/bin/正常情况下应该能看到postgres、initdb、pg_ctl、psql这些核心工具。看到它们就说明编译安装的主流程已经走通了。这一步还有一个易忽略的辅助安装:make install默认不装一些附加模块,比如contrib目录下的扩展,如果你需要某些扩展,记得回到源码目录再用make contrib install补上。常见的高性能扩展pg_stat_statements就在 contrib 里,缺了它后面做性能诊断会很疼。
3. 初始化数据库与运行配置:装好不代表能跑
3.1 创建专用用户与数据目录
二进制装好了,但 PostgreSQL 有一个硬性安全要求:禁止用 root 用户运行数据库服务。这跟 MySQL 还不太一样,PG 的设计理念就是“数据库操作必须用独立用户”,避免权限混乱。所以第一步是创建系统用户:
useradd -m postgres然后规划数据目录。我习惯把数据放在独立分区,这样系统盘挂了数据还在,备份和扩容也方便:
mkdir -p /var/lib/pgdata chown -R postgres:postgres /var/lib/pgdata chmod 700 /var/lib/pgdatachmod 700很关键,PG 对数据目录权限敏感,如果目录权限是 755 或者其他用户可读的状态,initdb 会拒绝执行。这个安全机制在 MySQL 那边用--initialize-insecure可能碰不到,但 PG 是强制的,目录只能属于 postgres 用户且权限只能是 700。
3.2 initdb 初始化:字符集和区域设置
初始化数据库实例用的是initdb,这一步的任务是创建基础系统库(template1、postgres)、生成配置文件和控制文件。我用的是:
su - postgres /usr/local/pgsql13/bin/initdb -D /var/lib/pgdata \ -E UTF8 \ --locale=en_US.UTF-8 \ -U postgres \ --pwfile=/tmp/pgpass_init逐项拆解:
-D /var/lib/pgdata:指定数据目录,必须和前面创建的目录一致,目录属主也必须是 postgres 用户。
-E UTF8:指定数据库默认字符集。国内环境基本都选 UTF8,避免中文字符乱码。这个参数写进 template1,后面建库如果不指定,默认就用 UTF8。
--locale=en_US.UTF-8:区域设置。这里有个常见的坑:如果系统没装 en_US.UTF-8 locale,initdb 会直接报错。装系统语言包可以用localedef -i en_US -f UTF-8 en_US.UTF-8解决。另外,如果你确定所有业务都是中文环境,也可以直接--locale=zh_CN.UTF-8,但要先确认系统有这个 locale。
-U postgres:指定超级用户名。默认是当前系统用户名,因为我用su - postgres执行的 initdb,默认超级用户就是 postgres,写出来更明确。
--pwfile=/tmp/pgpass_init:从文件读取超级用户密码,避免密码出现在 shell 历史里。文件内容就是一行密码文本,用完后删掉就行。注意超级用户密码迟早要设的,不在这里设,后面也要ALTER USER补上。
初始化成功后输出末尾会出现“Success. You can now start the database server”的字样,看到这句话说明基础数据库已经建好了。这时候如果不报错也别急着启动,还要改两个配置文件。
3.3 调整 postgresql.conf 与 pg_hba.conf
初始化生成的配置文件在数据目录下,两个核心文件是postgresql.conf和pg_hba.conf。先看连接相关的配置:
vi /var/lib/pgdata/postgresql.conf需要改的参数,按重要程度排:
listen_addresses = 'localhost' # 默认就够,仅本机访问 port = 5432 # 跟上文 --with-pgport 保持一致 max_connections = 100 # 默认 100,小业务够用 shared_buffers = 1GB # 典型经验值是物理内存的 1/4 min_wal_size = 1GB max_wal_size = 4GBshared_buffers这个参数很多人一上来就调得很大,其实是误区。它不是越大越好,因为 PG 的共享缓冲和操作系统页缓存是两套体系,设得太大反而容易造成双缓存,白白浪费内存。我的经验值:总内存 16GB 的机器设 4GB 是安全的,超过物理内存 1/3 就要小心了。云服务器如果只有 2G 内存,建议先保持默认 128MB,别折腾。
然后是pg_hba.conf,这个文件控制访问权限,默认配置非常保守,只有本地 Unix Socket 连接。如果你像我一样需要远程连接,就要加一行:
host all all 0.0.0.0/0 scram-sha-256这里要特别提醒:0.0.0.0/0表示允许所有地址访问,如果是生产环境且没有专门的防火墙管控,这个配置极其危险。我实际部署时会先限制到业务网段,比如10.0.0.0/8,等确认没问题再放开,或者干脆只留内网连接。密码认证方式建议用scram-sha-256,不要用老的md5,13 版本已经默认支持 SCRAM 认证,安全性更高。
配置改完,记得用pg_ctl reload或重启实例让参数生效。注意:shared_buffers、max_connections这类参数改了必须重启才能生效,listen_addresses也一样。而pg_hba.conf支持热加载,pg_ctl reload就够了。
3.4 启动实例与注册 systemd 服务
第一次启动直接用pg_ctl就可以了:
/usr/local/pgsql13/bin/pg_ctl -D /var/lib/pgdata -l /var/lib/pgdata/server.log start-l指定日志文件,这是排查问题的重要入口。启动成功后验证一下:
/usr/local/pgsql13/bin/psql -U postgres -d postgres能正常进入 psql 交互界面,说明实例通了。不过在su - postgres环境下,psql 走的是 Unix Socket 连接,默认认证是trust(即不用密码),所以能直接进来是正常的。
要让 PostgreSQL 开机自启并纳入系统体系管理,我建议写 systemd 服务文件。虽然手动运行没问题,但服务器一旦重启,手动启动的进程不会自动恢复,这是个隐患。我的服务配置文件/etc/systemd/system/postgresql-13.service内容如下:
[Unit] Description=PostgreSQL 13 database server After=network.target [Service] Type=notify User=postgres Group=postgres ExecStart=/usr/local/pgsql13/bin/postgres -D /var/lib/pgdata ExecReload=/bin/kill -HUP $MAINPID KillMode=mixed KillSignal=SIGINT TimeoutSec=300 [Install] WantedBy=multi-user.target关键点有两个。第一,Type=notify对应的是 configure 阶段我加的--with-systemd,postgres 启动完成会主动向 systemd 发送通知,服务状态判断更准确。第二,ExecStart直接指向 postgres 可执行文件,而不是 pg_ctl,因为 pg_ctl 的启动模式和 notify 类型在 systemd 下有兼容性问题。这个坑我一开始没注意,用 pg_ctl 写进 service,结果 systemctl status 永远显示 activating,查了半天,换成直接 exec postgres 就好了。
注册并启动:
systemctl daemon-reload systemctl enable postgresql-13 systemctl start postgresql-13 systemctl status postgresql-13看到active (running)就干净了。这里有个验证的小习惯:systemctl status之后我还会再跑一次 psql 连接做双确认,毕竟 status 显示 active 也不能保证端口真的在听。
4. 常见问题与排查技巧实录
4.1 编译阶段的高频报错
源码安装过程中,90% 的问题集中在编译阶段。我实际遇到过的报错和解决办法整理如下:
报错:configure: error: readline library not found
这个最简单,就是 readline-devel 没装。PostgreSQL 的 psql 依赖 readline 做行编辑,没有它 configure 直接拦下。解决:
yum install -y readline-devel然后重跑 configure。这里有个经验:报错信息明确提示了什么,就先补什么,不要盲目卸载重装,浪费时间。
报错:cc: error: unrecognized command line option -std=gnu99
这个基本就是 GCC 太老的锅。PostgreSQL 12 之前对 GCC 要求不高,13 开始要求 GCC 6+,CentOS 7 自带 4.8.5 一定会踩。要么用 devtoolset 升级 GCC,要么换个支持新 GCC 的系统,我前面已经给出了 devtoolset-7 的安装命令,不再赘述。
报错:make: bison: Command not found
bison 没装,或者装了但不在当前 PATH。检查:
which bison yum install -y bison注意,如果你用的是 devtoolset 的 GCC,bison 一定要装在同一环境或系统全局路径下,否则编译时 PATH 混乱,一会儿能找到一会儿找不到,很难受。
4.2 初始化阶段报错
报错:initdb: error: could not create directory "/var/lib/pgdata": Permission denied
就是目录属主或权限不对。检查命令:
ls -ld /var/lib/pgdata如果不是postgres postgres且权限 700,直接用 root 改:
chown -R postgres:postgres /var/lib/pgdata chmod 700 /var/lib/pgdata这个报错还有一个变体:你明明用了su - postgres,但还是 Permission denied。原因多半是目录上层路径的某个中间目录,比如/var/lib的属主权限有问题,PG 对整条路径都要可进入。新用户常忽略这点。
报错:initdb: error: could not find suitable encoding "UTF8"
系统没有 UTF8 locale,或者 locale 相关的环境变量没设置。可以先跑locale -a查看可用 locale,如果没有 en_US.UTF-8:
localedef -i en_US -f UTF-8 en_US.UTF-8设置完之后重新 initdb 即可。
4.3 连接阶段的报错
报错:psql: FATAL: role "root" does not exist
这个最经典。你在 root 用户下执行 psql,但数据库里没有 root 这个账号。解决要么用-U postgres指定超级用户,要么用su - postgres切换到 postgres 系统用户再连。我一般习惯直接psql -U postgres,这样不管是哪个系统用户执行都能连。
报错:psql: could not connect to server: No such file or directory
两个可能:一是实例没启动,二是 Socket 文件路径不对。默认 Socket 在/tmp下,如果你的临时目录被系统清理过,Socket 没了,但进程还在。这种情况用 TCP 连接试试:
psql -h 127.0.0.1 -p 5432 -U postgres能连上就说明问题是 Socket 路径,可以改 postgresql.conf 里的unix_socket_directories来固定路径。
报错:FATAL: password authentication failed for user "postgres"
这个一般是密码错了,或者 pg_hba.conf 里的认证方式不匹配。先看 pg_hba.conf 当前生效的是哪种认证方式,再用ALTER USER postgres PASSWORD '...'重置密码。注意改了 pg_hba.conf 要pg_ctl reload才生效。
4.4 问题排查速查表
| 问题现象 | 最常见原因 | 排查命令 | 解决思路 |
|---|---|---|---|
| configure 报 readline 缺失 | 缺 readline-devel | rpm -qa | grep readline | yum 安装后重跑 configure |
| 编译报 GNU99 错误 | GCC 版本太老 | gcc --version | devtoolset 升级 GCC |
| initdb 权限错误 | 目录属主/权限不对 | ls -ld /var/lib/pgdata | chown postgres + chmod 700 |
| initdb 编码错误 | 无对应 locale | locale -a | localedef 生成 locale |
| psql 连不上 | 实例未启动 | pg_ctl status / ss -lntp | 启动实例并检查日志 |
| password authentication failed | 密码或认证方式不对 | cat pg_hba.conf | 重置密码或调整认证 |
排查的通用思路:先看日志,再改配置。PostgreSQL 的 server log 里几乎会记录所有的连接失败、认证失败原因,比你瞎猜快得多。日志默认输出到 stderr,用 pg_ctl 启动时-l指定的文件就是它,我记得有一次排查远程连接不上的问题,就是因为 listen_addresses 改错了没重启,日志一眼就看出来了。
最后再分享一点个人心得
这次源码安装整个过程下来,我的感受是:源码安装本身不复杂,复杂的是理解每一个参数在决定什么。如果你只是照抄命令,装完遇到问题还是会一头雾水。我建议先把 configure 参数根据自己需求想清楚,再动手编译,别为了图省事全默认,也别盲目堆参数。另外,生产环境装之前一定要先在测试机上完整跑一遍,把踩过的坑和系统版本记录下来,这样真正上线时就从容很多。如果你打算在同一台机器上再跑第二个 PG 13 实例,只需要把数据目录换一个,端口改成 5433,systemd 服务文件复制一份改改就行,二进制是可以共用一份的。这次记录算是这套系列笔记的第一篇,后面实例化、备份、性能调优的坑,我会陆续整理出来。