1. 环境准备与依赖安装
1.1 为什么要用源码安装
这两年接触到不少生产环境的部署需求,很多团队在选型时直接拎一个二进制包或系统包管理器装完就跑,确实省事,但到了真正需要调优、加插件、定制特性的时候,就有种被卡住脖子的感觉了。PostgreSQL 13 源码安装这条路,看着多花了两三个小时,实际上是把后续的主动权攥在自己手里:比如编译期就启用--with-icu支持国际化的排序规则、--with-llvm开启 JIT 加速能力,或者把默认的页面大小、块大小按业务存储特性重新编一遍,这种深度定制的空间包管理器很难给你。
我的建议是,如果你的服务器要做到线上支撑、长期演进,哪怕是一次性的内部系统,也值得走上源码编译这条路。这不是为了显得“专业”,而是源码方式能让你对整个软件的构成和依赖关系有更清晰的掌握,出去排查问题时心里更有底。而且 PostgreSQL 13 作为当时比较新的版本线,官方文档也建议源码方式用于具备一定维护能力的团队,社区里大量高可用方案和扩展插件也需要对应版本头文件和编译接口。
1.2 操作系统层面需要摸清的底细
我在实际操作中主要用的发行版是 CentOS 7.x 和 Rocky Linux 8.x,这两个系统在编译 PostgreSQL 13 时思路大体一致,但依赖包的命名和版本略有差异。建议拿到新机器后,先确认三件事:操作系统版本、CPU 架构、可用内存,因为这三项直接决定你要装哪些依赖包,以及后面的编译参数怎么选。
cat /etc/redhat-release uname -m free -hPostgreSQL 13 源码编译对系统的要求其实不苛刻,内存 2GB 以上就足够完成make全量构建,但如果要并行编译(-j参数),建议 4GB 以上更稳。磁盘方面,源码包加编译产物大约需要 2GB 左右临时空间,别把临时目录塞得太满。
需要额外提醒的是:PostgreSQL 源码编译会用到 GNU make,并且明确要求gmake而不是系统里可能默认存在的 BSD make。CentOS/RHEL 系列默认安装的是 GNU make,直接执行make没问题,但如果你在一台有一定历史的系统上操作,建议先用make --version确认一下,避免后续出现诡异报错。
1.3 依赖包清单与安装过程
这是最容易卡壳的第一关,很多编译中途失败都是因为缺了某个开发库。PostgreSQL 13 编译的基础依赖包括编译器、系统工具、开发头文件、以及几个可选特性的支持库。
yum install -y gcc gcc-c++ make readline-devel zlib-develreadline-devel 和 zlib-devel 基本上可以说是编译 PostgreSQL 的刚需:readline 是为了命令行历史记录和编辑功能,zlib 则是数据压缩能力的基础,比如 pg_dump 的压缩导出就依赖它。
如果你需要更完整的扩展能力,还有几个包建议一起装上,它们的名字容易漏:
yum install -y libxml2-devel libxslt-devel openssl-devellibxml2-devel和libxslt-devel提供 XML 处理能力,配套pgxmlerrors等模块要用;openssl-devel是为了 SSL 加密连接的支持。现在信息安全要求越来越高,数据库链接不加密基本是过不了评审的,所以 SSL 支持我建议默认就带上,不要等后面被安全扫描出问题再加。
另外有些场景需要依赖 ICU 库来实现更完整的 Unicode 排序规则支持:
yum install -y icu libicu-develICU 这个库在 PostgreSQL 12 之后已经被官方支持用作默认的排序规则库,如果你的业务需要处理多语言排序、Emoji 这类特殊字符,--with-icu非常值得打开。
我见过不少人在这一步图省事只装了前三个基础包,后面编译到contrib模块时才冒出来一堆报错,来回折腾半天。多花两分钟把依赖一次装齐,是过来人的经验。
2. 源码获取与编译参数设计
2.1 下载源码包与完整性校验
下载源码时,我建议不要直接用浏览器保存,而是用 wget 拉取,这样可以通过 md5 或 sha256 做一遍完整性校验。PostgreSQL 官方会把源码包和对应的校验值文件放在同一个目录下,稳妥的做法是把包和校验文件都拉下来一起比对。
wget https://ftp.postgresql.org/pub/source/v13.3/postgresql-13.3.tar.gz wget https://ftp.postgresql.org/pub/source/v13.3/postgresql-13.3.tar.gz.md5 md5sum -c postgresql-13.3.tar.gz.md5为什么选 13.3 而不是修改基调比较大的 13.0?其实到了 13.x 系列,小版本主要是修复 bug 和安全性漏洞,直接用稳定版是最省心的。这里插一句,官网的下载目录并没有固定版本,会持续更新,所以下载前先去官网确认一下最新的小版本号。
我把源码包放在/usr/local/src目录,这个目录专门存放需要自己编译的第三方源码,方便日后追溯版本和补丁。如果你的团队有统一的软件发布目录,按团队规范走即可。
2.2 configure 参数设计的实际考量
解开源码包后,就进入配置环节了:
tar -zxvf postgresql-13.3.tar.gz cd postgresql-13.3这个步骤核心是./configure,它决定 PostgreSQL 将要被编译成什么样,也直接影响后期能力边界。我常用的配置参数如下:
./configure --prefix=/usr/local/pgsql \ --with-pgport=5432 \ --with-openssl \ --with-icu \ --with-libxml \ --with-libxslt \ --with-readline \ --with-zlib \ --enable-thread-safety每个参数背后都有对应的实际需求:
--prefix:指定安装根目录。这里选/usr/local/pgsql是社区很常见的路径,简单且不易与其他软件冲突;--with-pgport:设置默认监听端口,不设置的话默认就是 5432。写在这里的好处是后续启动、连接都不用额外指端口;--with-openssl:开启 SSL,编译出支持加密连接的服务端和客户端;--with-icu:启用国际语言排序规则库;--with-libxml和--with-libxslt:XML/XLST 处理支持,配套 contrib 模块会用到,比如 PostgreSQL 的 XML 函数;--enable-thread-safety:让 libpq 在多线程客户端程序里更安全,并发连接场景这个挺重要。
其实官方默认是线程安全的,但手动写出来更明确,也方便后来维护的人看到预期行为。假如你确认某些功能不需要,可以去掉对应参数来减小体积和依赖,但我的原则是多带不用,而不是需要时找不到。
2.3 make 编译扛坑指南
配置成功后会提示你继续运行make,这一步在旧机器上可能需要十几分钟,可以用并行参数加速:
make -j 4-j 4表示四个编译任务并行,具体数字建议和 CPU 核心数保持一致或略少于核心数。别贪心,把-j开到 16 或者更大,内存不够的情况下反而会拖慢编译,甚至出现编译器进程被杀掉的情况。如果中途出现报错,会提示具体的源文件和错误原因,这时候先别慌,多数情况是缺头文件或者版本不匹配,带着报错去搜索引擎查会非常快。
编译无误后执行安装:
make install这一步会把可执行文件、库文件、头文件、文档等内容依次复制到--prefix指定的目录下。安装完成后再编译安装 contrib 扩展模块:
cd contrib make -j 4 make install这里必须提醒:contrib 不是可选项级别的东西,既有pg_stat_statements(性能诊断神器)、pageinspect(数据页内窥镜)这种分析工具,也有postgres_fdw(外部数据源连接)、dblink这类跨库操作模块。默认 PostgreSQL 核心功能是克制的,大量实用能力都跑到 contrib 里去了,所以编译完主体后一定顺手把 contrib 也装上。
3. 数据库初始化与服务配置
3.1 创建专用用户与目录规划
编译完成只是第一步,接下来要解决“以什么身份、在哪片地盘上运行数据库”这个问题。
我强烈建议创建一个操作系统级别的专用账号来跑 PostgreSQL,不允许它做普通用户该干的其他事情,这样即使数据库层面被攻破,攻击者能拿到的也只是受限的系统权限。
useradd postgres mkdir -p /data/pgdata chown -R postgres:postgres /data/pgdata数据目录放在/data/pgdata是从经验里得出的选择。早期我图省事把数据目录建在/home/postgres下,结果有一次/home分区写满,整个数据库服务直接挂掉。把数据目录独立放在/data下,既减少与系统盘的空间争抢,也在备份时容易通过挂载点做快照或者直接打包。
3.2 初始化数据库实例
用 postgres 用户来执行数据库初始化:
su - postgres /usr/local/pgsql/bin/initdb -D /data/pgdata -U postgres --encoding=UTF8 --locale=C.UTF-8-D指定数据目录,-U postgres指定超级用户名,--encoding设置字符集为 UTF8,这个几乎必须,国际化系统都离不开它。
--locale=C.UTF-8值得讲一下:locale 会影响数据排序、字符串比较的行为。在生产环境选 locale 要谨慎,因为改 locale 基本意味着需要pg_dump重放数据,代价极大。C.UTF-8 的好处是排序规则相对干净、可预测性强,也能覆盖大多数字符集;C 系列的选择对性能也相对友好。如果你想完全跟随系统默认区域设置,直接省略这个参数即可,但跨环境迁移时可能会出现排序不一致的坑,所以我的习惯是显式指定。
3.3 关键参数初调:postgresql.conf 不要全默认
初始化完成后,先不要急着启动,把postgresql.conf里几个核心参数过一遍:
listen_addresses = '*' port = 5432 max_connections = 200 shared_buffers = 1GBlisten_addresses:默认只监听本机,改成'*'才允许外部连接,但生产环境更稳妥的做法是改成实际业务网卡 IP 而不是全世界;max_connections:默认 100,在一般互联网场景够用,但并发较高的业务 200 是常见起步值。注意这个值和下面共享内存参数是联动的,共享内存太小而连接数很大,可能启动都会成问题;shared_buffers:PostgreSQL 自己管理的共享内存池,一般建议设为物理内存的四分之一。这是一个经验值,不是数学最优解,比如内存 16GB 的机器给 4GB 就比较合理。设置过大反而可能因为系统页缓存管理方式的差异,性能反而下降。
修改配置后先校验一下是否正常:
/usr/local/pgsql/bin/pg_ctl -D /data/pgdata -l /data/pgdata/logfile start-l指定日志文件路径,启动过程中出现的任何错误都会记录在里面。如果成功启动,会提示server started,此时可以用psql快速验证:
/usr/local/pgsql/bin/psql -U postgres -p 5432 -c "select version();"看到 PostgreSQL 13.x 的版本信息就说明这一步已经扎实了。
3.4 注册成系统服务
手动启动只能管当前会话,重启机器后 PostgreSQL 不会自己起来,这不符合生产要求。注册成 systemd 服务才是正途。
我习惯创建/etc/systemd/system/postgresql-13.service,内容如下:
[Unit] Description=PostgreSQL 13 database server After=network.target [Service] Type=simple User=postgres Group=postgres ExecStart=/usr/local/pgsql/bin/postgres -D /data/pgdata ExecReload=/usr/local/pgsql/bin/pg_ctl -D /data/pgdata reload TimeoutSec=600 [Install] WantedBy=multi-user.target解释一下这里为什么不调用pg_ctl启动,而是直接跑 postgres 二进制:systemd 内直接用主进程管理,日志统一交给 journald 收集,方便集中管理和排查问题,状态管理也更干净。Type=simple表示系统把 ExecStart 拉起的进程视作主进程,这正是我们想要的。
配置好以后:
systemctl daemon-reload systemctl enable postgresql-13 systemctl start postgresql-13若在启动或 enable 过程中报错,大概率是目录权限或配置文件里的路径不对,按提示检查即可。
4. 常见问题与排查技巧实录
4.1 编译阶段的高频报错对照表
我在指导同事部署和社区答疑时,经常见到下面这几类问题:
| 报错表现 | 根本原因 | 解决方式 |
|---|---|---|
readline/readline.h: No such file or directory | 缺少 readline-devel | 安装 readline-devel 后再执行 configure |
zlib.h: No such file or directory | 缺少 zlib-devel | 安装 zlib-devel |
could not find a usable perl | 缺少 Perl 或版本不兼容 | 安装 perl 和 perl-devel,并确认 PATH |
configure: error: ICU library not found | 缺少 ICU 或版本过旧 | 安装 libicu-devel,并确保版本不低于 4.2 |
make: *** No rule to make target | 源码目录不完整或架构不匹配 | 重新解压完整源码包,确认 CMAKE/目录结构没有残留 |
一个非常容易忽视的问题:老系统上的 Perl 版本可能是 5.10 甚至更低,PostgreSQL 13 对 Perl 最低要求是 5.8.1,理论上够,但配套模块可能需要更新一点的版本。如果碰到莫名其妙的符号错误,优先升级 Perl 到 5.16 以上再重新编译。
另外提一句编译前的“洁癖行动”:如果之前有编译失败的半成品源码目录,务必make distclean清理后再重新 configure,不清理干净反而会因为旧的编译缓存造成稀奇古怪的错误。
4.2 初始化失败的一个罕见但高发的坑
执行 initdb 时有一类报错几乎每个群友都问过:
initdb: error: invalid locale settings; check LANG and LC_* environment variables其实 initdb 默认继承你当前 shell 的区域设置,如果这些环境变量乱掉,甚至没有一个明确可用的 locale,就会直接罢工。最简单可靠的解决方案是在 initdb 时显式传入 locale:
export LANG=C.UTF-8 /usr/local/pgsql/bin/initdb -D /data/pgdata -U postgres --encoding=UTF8 --locale=C.UTF-8还有一种情况:系统本来就缺少C.UTF-8这个 locale,CentOS 7 上如果没安装过glibc-all-langpacks就可能出现找不到的情况。检查一下:
locale -a | grep 'C.UTF-8'没有的话,临时方案是退回去用--locale=C或系统已有的 en_US.UTF-8,但要清楚排序行为会和 C.UTF-8 有细微区别。这一点上千万不能随便妥协,数据库的排序规则一经初始化就很难改。
4.3 启动失败与日志定位技巧
源码安装的服务和包管理器装的服务在日志路径上有很大差异,很多人用惯了系统包的日志路径,转到源码安装就不知所措。源码安装方式下,没有意外的话日志就是你自己指定的-l路径,或者 journald。
我在生产环境长期使用 journald 来管理日志,比如用journalctl -u postgresql-13查看服务日志,查询启动失败原因时的效率和体验比翻文件好很多。不过注意,如果你在环境变量或 service 文件中自己指定了 logging collector,那么日志默认行为会和 journald 有冲突,两处都写可能造成重复记录。建议源码安装时保持简单,直接让 postgres 进程把 stderr/stdout 交给 systemd,依赖 journald 收集。
一个常见启动失败案例是端口被占用:
ss -tlnp | grep 5432查出占用进程后要么停掉它,要么改配置换端口。另一个常见案例是数据目录权限问题,启动用户不是 postgres,系统直接拒绝打开目录。这个排查起来很快:
ls -ld /data/pgdata chown -R postgres:postgres /data/pgdata如果报错信息是could not create lock file类型的,优先检查/tmp目录的权限和磁盘空间,PostgreSQL 运行时会在这里创建共享内存锁文件和 Unix socket 文件。
4.4 pg_stat_statements 等你需要的 contrib 插件
所有扩展插件都必须先执行对应的 SQL 安装脚本才能生效,否则它只是躺在磁盘上的一个编译好的库文件,不会被数据库加载进内存。比如性能调优神器pg_stat_statements:
CREATE EXTENSION pg_stat_statements;这条语句执行完成后,还需要把pg_stat_statements加进shared_preload_libraries参数并重启数据库,否则无法记录查询统计信息。早期版本中这里有一个很隐蔽的坑:如果你只执行了CREATE EXTENSION却忘了修改 shared_preload_libraries,重启后这个插件并不会自动加载,查询视图时还会报错。
另外一个我极力推荐安装的 contrib 模块是pg_repack,当然它是独立于官方 contrib 的第三方工具,但对付表膨胀问题非常出色。数据库运行久了,频繁 UPDATE/DELETE 的表会膨胀,传统VACUUM FULL会锁表,阻塞业务,而pg_repack可以在不长时间锁表的情况下完成重整。这个工具虽然不属于 PostgreSQL 13 源码包,但要配合对应版本的编译环境使用,这就要求你在源码编译时保留好pg_config和头文件,否则编译第三方插件时会各种缺东西。这也是我前面强调源码安装方式保留主动权的主要原因。
5. 性能验证与后续调优建议
5.1 首次启动后的健康检查清单
完成服务注册和启动后,用下列一组命令做健康检查,确认这台数据库能支撑实际业务:
/usr/local/pgsql/bin/pg_isready -h 127.0.0.1 -p 5432 /usr/local/pgsql/bin/psql -U postgres -h 127.0.0.1 -p 5432 -c "show server_version;" /usr/local/pgsql/bin/psql -U postgres -h 127.0.0.1 -p 5432 -c "select name, setting from pg_settings where name in ('shared_buffers','max_connections','listen_addresses');"pg_isready输出accepting connections说明服务正常监听。之后再用pg_settings视图核对关键参数生效值,防止配置文件写错但进程没读到的假象。
完成以上基础检查后,可以顺手创建一个简单的压测库,比如用pgbench初始化并做一轮基本测试:
/usr/local/pgsql/bin/createdb -U postgres bench /usr/local/pgsql/bin/pgbench -U postgres -i bench /usr/local/pgsql/bin/pgbench -U postgres -c 10 -j 2 -T 60 bench-c 10是并发客户端数量,-j 2是线程数,-T 60表示测试持续 60 秒。跑完后观察 TPS(每秒事务数)和延迟分布:如果 TPS 明显偏低,先检查shared_buffers和数据目录所在磁盘类型;如果是机械盘,顺序写性能会成为瓶颈,只能靠调大wal_buffers、checkpoint_timeout这类参数缓解,但仍需尽早规划换 SSD。
5.2 wal 参数与检查点行为的实用建议
PostgreSQL 默认wal_level是 replica,一般业务其实不用动;需要开启流复制时,主库把wal_level改为 logical 可以支持逻辑复制,但代价是 WAL 日志内容变多,因此非必要不开启。
我特别想强调的是checkpoint_timeout和max_wal_size这两个参数:
checkpoint_timeout = 15min max_wal_size = 4GB min_wal_size = 1GB很多新手以为 checkpoint 越频繁越安全,其实 checkpoint 本质是把脏页写回磁盘,做一次全量落盘的成本是不低的。频繁的 checkpoint 会让磁盘 I/O 出现周期性峰值,用户感知是“时不时的卡顿一下”。把间隔拉长到 15 到 30 分钟,配合足够大的 WAL 容量,日志落盘的顺序写和刷盘的大块写可以错峰,整体体验更平滑。
这里补充一个老鸟才注意到的细节:设置max_wal_size后,不要把它理解为硬上限。在实际运行中,PostgreSQL 会在 checkpoint 间隔期间动态估计合适的 WAL 总量,因此实际使用量可能略高于设置的max_wal_size。提前知道这个行为,查询监控面板时就不会被瞬时超出值吓到。
5.3 内存和存储选型要一起规划
shared_buffers设为内存的四分之一再往上提其实收益不大,因为 PostgreSQL 还有操作系统页缓存这一层。更大内存时,effective_cache_size这个计划器参数的作用就出来了,它告诉优化器系统剩余可用缓存大概有多大,影响优化器选索引扫描还是顺序扫描。一般建议设为物理内存的一半到 75%,比如 32GB 物理内存的机器可以设成 16GB 到 24GB 之间:
effective_cache_size = 20GB存储方面,数据目录所在位置对整体体验影响极大。同样一套默认配置,放机械盘和 NVMe 盘上,TPS 可能就是两倍以上差距。从架构上考虑,如果条件允许,WAL 日志单独放到一块独立 SSD 或高性能盘上,能在高并发写入时有质的改观。不过这是讲硬件规划的范畴,对于单纯做源码安装部署来说,先确保数据目录在可靠、独立的分区上即可。
6. 几条基于实战的补充体会
最后聊几句这次源码安装过程中的个人感受,不一定全在官方文档里写得明明白白,但实际踩过之后特别想让后来的朋友少走弯路。
第一,postgres 操作系统的用户密码我建议直接把登录 shell 设为/sbin/nologin,也就是禁用该用户直接登录。所有操作通过su或密钥跳转去执行即可,这能减少黑客在拿到 postgres 用户后扩充权限的可能。有些教程为了省事直接设置成可登录,生产环境很不推荐。
第二,源码编译完成后,/usr/local/pgsql/bin这个路径要加入系统的 PATH 环境中,或者直接在各个调用位置写全路径。很多人喜欢创建软链到/usr/local/bin下,但如果后续升级版本,软链要记得同步更新,不然后患不少。我更喜欢在/etc/profile.d/pgsql.sh中写入:
export PATH=/usr/local/pgsql/bin:$PATH这样每次登录 shell 都有可用的 psql、pg_dump 等工具,不用到处补路径。
第三,要保持一个习惯:每次改完postgresql.conf,在重启前用pg_ctl reload做一次热加载测试。有些参数支持 reload(比如大多数内存和日志类参数),有些则需要重启(比如 shared_preload_libraries),提前用pg_settings上下文判断一下,能省去不必要的业务中断时间。判断参数是否需要重启的方法是:
SELECT name, context FROM pg_settings WHERE name = 'shared_preload_libraries';如果 context 显示为postmaster,那么这个参数必须重启数据库才能生效。
PostgreSQL 13 的源码安装过程本质上并不复杂,但它是一个需要耐心的系统工程:从依赖准备、编译参数、初始化配置到系统服务,每个环节都环环相扣。只要按部就班地把环境梳理清楚,后续的维护和扩展会非常顺手。希望这篇笔记能帮你在部署 PostgreSQL 时少踩几个坑,尤其是那些看起来不起眼却能卡住你半天的细节。