☰
PostgreSQL 18 安装指南:Ubuntu 与 Rocky Linux 实战避坑
2026/10/13 11:03:14 网站建设 项目流程

先说个背景:我最近在一台闲置服务器上把一个内部工具的数据存储从 16 版本迁到了 18,所谓“迁移”,其实就是先在干净环境里完整走一遍安装、初始化、配置和压测流程。整个过程比我预想的多花了不少时间——不是安装本身有多难,而是那些“照旧文档做却不对”的细节,比如源没配对装出旧版本、内存参数手滑导致起不来、locale 跟老库不一致引发排序异常。这篇就当作一份独立的 PG18 安装笔记,把我在 Ubuntu 和 Rocky Linux 两条路线上踩过的坑、用过的参数、验证过的命令一次写清楚。无论你是第一次装 PostgreSQL,还是要给现有环境做版本升级,都可以照着走一遍。

1. 为什么单独写这篇 PG18 安装笔记

1.1 版本发布节奏与升级窗口

PostgreSQL 的主版本发布很有规律,基本是每年 9 月底放一个大版本。按这个节奏,18 正式版出来后,紧接着会有 18.1、18.2 这样的小版本修复补丁。对大多数生产环境来说,我从来不建议“当天发布当天升”,但测试环境提前装一个 18,把安装脚本、初始化参数、应用兼容性全部跑通,等到 .1 或 .2 版本再考虑生产切换,压力会小很多。

另外要说清楚一点:PostgreSQL 从 17 升级到 18,不是一个简单的“换二进制”操作,主版本升级必须走 pg_upgrade 或 pg_dump/restore 流程。很多人以为把旧数据目录挂到新版本下就能启动,结果只会看到一堆 “database files are incompatible” 错误。所以这篇安装笔记真正要解决的不只是“把软件装上”,还包括“把实例初始化好、把数据安稳接过来”这整条链路。

1.2 动手前必须确认的三件事

第一,平台和包管理方式。Debian/Ubuntu 系走 APT 官方源,RHEL/Rocky/CentOS 系走 PGDG 的 dnf 源,Windows/macOS 各有官方安装器。同一个 PostgreSQL 18,在不同平台上的包名、服务名、目录结构完全不同,千万别混着记忆。

第二,端口和数据目录。默认端口 5432,数据目录默认在 Debian 系是/var/lib/postgresql/18/main,在 RHEL 系是/var/lib/pgsql/18/data。如果你的机器上已经有一个老版本 PostgreSQL 在跑,新版本装好后端口会冲突,要么改端口,要么先停掉旧服务。数据目录更要提前规划:最好放在独立分区或 LVM 逻辑卷上,而不是和系统根目录挤在一起。

第三,备份和回退方案。哪怕是全新安装,也要想清楚“如果装到一半搞砸了,我能不能回到起点”。数据库最怕的不是装不上,而是数据目录所在磁盘被写满后,WAL 文件堆积导致整个实例崩溃。动手之前看一眼磁盘空间、确认一下备份策略,永远花不了几分钟。

2. 用官方源安装:Ubuntu 与 Rocky Linux 两条实操路线

2.1 Ubuntu/Debian 的 APT 安装流程

在 Ubuntu 上装 PostgreSQL 18,最省心的方式是先安装官方提供的postgresql-common包,再用它自带的脚本自动添加 PGDG 源。命令如下:

sudo apt update sudo apt install -y postgresql-common sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y sudo apt update sudo apt install -y postgresql-18

这个脚本会检测当前系统版本(比如 Ubuntu 24.04),然后往/etc/apt/sources.list.d/下写入对应的官方源配置,并导入签名密钥。装完以后,你不需要手动执行 initdb,因为 Debian 系的软件包在安装阶段就已经自动初始化好了一个默认实例,并且创建了postgres系统用户。

装好后可以马上验证:

systemctl status postgresql pg_lsclusters

pg_lsclusters是 Debian 系特有的命令,会列出本机所有 PostgreSQL 集群实例、端口、数据目录和状态。这个工具非常实用,尤其是当你想在同一台机器上维护多个版本时,靠它一眼就能看明白各实例的归属。

2.2 Rocky/CentOS 的 dnf 安装流程

Rocky Linux 这边要稍微多几步。先装 PGDG 仓库包,然后一定要禁用系统自带的低版本 PostgreSQL 模块,再安装对应版本的服务包:

sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %major)-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo dnf -qy module disable postgresql sudo dnf install -y postgresql18-server sudo /usr/pgsql-18/bin/postgresql-18-setup initdb sudo systemctl enable --now postgresql-18

这里解释一下几个关键点。module disable postgresql是为了避免系统自带模块里的旧 PostgreSQL 二进制和 PGDG 的 18 版本冲突,我见过很多人没做这一步,结果psql调到的还是老版本的命令。postgresql-18-setup initdb会以postgres用户身份,在默认的数据目录下自动完成初始化,这一步在 Debian 系是安装时自动做的,在 RHEL 系必须手动执行。

装完后检查:

systemctl status postgresql-18 sudo -u postgres psql -c "SELECT version();"

注意这里的服务名是postgresql-18,跟 Debian 系的postgresql不同,这也是跨平台时最容易混淆的地方。

2.3 为什么我默认劝你不要源码编译

每年 PostgreSQL 发布后,总有人问我“是不是源码编译更好,性能更强”。我的回答通常是:除非你有极其特殊的定制需求,或者要部署在一个完全离线的受限环境,否则不要源码编译。

原因有三条。第一,PG 的依赖关系比看上去要复杂,ICU 字符库、readline、zlib、pam、systemd 支持等等,任何一个版本不对都可能让 configure 阶段失败;第二,源码安装的目录结构和包管理逻辑跟发行版脱节,以后想用包管理器升级小版本、回滚配置会非常别扭;第三,编译参数没选好,反而会丢掉系统包默认开启的安全特性和优化选项。官方仓库的二进制包通常在正式版发布后几天内就会跟进到最新小版本,安全修复也比自己编译的版本更及时。老老实实用官方源,把省下来的时间花在数据目录规划和备份上,性价比高得多。

3. initdb 初始化的参数细节与 systemd 服务管理

3.1 数据目录、系统用户与权限设计

PostgreSQL 安全模型的第一条铁律是:数据库实例绝不能用 root 运行。每个主版本都会创建一个独立的系统用户,Debian 系是postgres,RHEL 系同样是postgres用户,但数据目录路径不同。权限上,数据目录必须是该用户拥有,否则初始化会直接失败。

如果你打算把数据目录放到一个自定义位置,比如挂载在/data/pgsql下的独立磁盘,那手动初始化的完整流程应该是:

sudo mkdir -p /data/pgsql sudo chown postgres:postgres /data/pgsql sudo -u postgres /usr/lib/postgresql/18/bin/initdb -D /data/pgsql

Debian 系下,/usr/lib/postgresql/18/bin/initdb是 18 版本二进制所在位置,RHEL 系对应的是/usr/pgsql-18/bin/initdb。路径虽有差异,但 initdb 这个程序的行为是一致的。自定义目录时,还要记得修改 systemd 服务文件里的PGDATA指向,或者用pg_ctlcluster来管理集群。

3.2 initdb 里值得显式指定的参数

很多情况下,发行版的初始化脚本已经给了合理默认值,但有两个参数我建议每次都显式指定。

第一个是-E UTF8,也就是数据库默认编码。虽然现代 Linux 系统默认 locale 基本都是 UTF-8,但显式写出来可以避免后续导入数据时出现编码猜谜。

第二个是--data-checksums,开启数据页校验和。这是我最想强调的一项:它可以让数据库在读取数据页时检测到物理磁盘损坏或内存位翻转导致的数据异常,并主动报错而不是悄悄返回坏数据。磁盘损坏往往不是立刻让整库崩掉,而是某个数据页悄悄变坏,等备份恢复时才发现已经晚了。开启校验和的代价是微乎其微的写入开销,收益却是实打实的数据完整性和提前发现故障的能力。

手动初始化一个自定义实例的推荐命令是:

sudo -u postgres /usr/lib/postgresql/18/bin/initdb -D /data/pgsql -E UTF8 --locale=C.UTF-8 --data-checksums

这里--locale=C.UTF-8是一个常见但不绝对安全的选择。C.UTF-8 的排序规则比en_US.UTF-8简单,对很多内部系统来说反而稳定。但如果你要和老库做迁移、做复制,locale 必须和源库保持一致,这是我在后面第 5 节会重点说的坑。

3.3 通过 systemd 拉起实例并做基本验证

无论是在哪个发行版,最终都要靠 systemd 来管理 PostgreSQL 的拉起和自启。Debian 系直接:

sudo systemctl enable --now postgresql

RHEL 系则是:

sudo systemctl enable --now postgresql-18

设置开机自启后,别急着结束,先做一轮基础验证。看状态、看端口、看进程归属:

systemctl status postgresql-18 ss -tlnp | grep 5432 ps -u postgres -f | grep [p]ostgres

状态菜单里只要显示active (running),端口监听正常,进程是postgres用户运行,就说明实例已经健康起来。如果启动失败,第一时间去看日志:

sudo journalctl -u postgresql-18 --no-pager -n 50

日志里通常会直接告诉你失败原因,比如data directory has invalid permissions,或者could not open shared memory。这两个是我见最多的启动失败原因,前者是目录权限不对,后者是共享内存参数问题,下一节会展开讲。

4. 配置文件里的第一轮调优与访问控制收紧

4.1 postgresql.conf 中优先调整的 6 个参数

安装完成后,postgresql.conf还是默认参数。所谓默认参数,本质上是“为了在最差配置下也能跑起来”的保守值,不要指望它发挥出硬件应有的性能。我每次安装后都会优先调整下面 6 个参数。

参数推荐起点设置依据
shared_buffers物理内存的 25%PG 的共享缓冲区,用于缓存数据页
effective_cache_size物理内存的 75%告诉优化器系统总缓存有多大,影响执行计划
work_mem8MB 起步每个排序、哈希操作可用的内存,过大易导致内存吃紧
maintenance_work_mem1GB 或更多影响VACUUM、CREATE INDEX等维护操作的速度
max_connections默认 100,按需改连接数不是越大越好,每个连接都会占用内存
wal_level默认为replica要做逻辑复制时才需要调到logical

用大白话解释一下前两个。shared_buffers像厨房里的案板——切菜、备料都在上面进行;effective_cache_size则像整个厨房的储物柜——它不代表 PG 自己在用这么多内存,而是告诉优化器:“系统里大概有这么多缓存空间可用”。优化器知道了这个大仓库的容量,才能更准确地估算是走索引扫描还是全表扫描更划算。

修改配置后,区分一下哪些参数需要重启、哪些可以reload热加载。shared_buffers必须重启实例才能生效,因为它涉及共享内存的申请;effective_cache_size、work_mem这些可以SELECT pg_reload_conf();直接热更新。一个常见错误是想用pg_reload_conf()让shared_buffers生效,结果怎么改都不变,就以为是没保存成功。

4.2 pg_hba.conf 的修改顺序与风险

pg_hba.conf是控制谁能连数据库的访问策略文件,主要涉及 TCP 连接受限问题。刚装好的实例,Debian 系默认本地peer认证,也就是说本地连接时操作系统用户名必须和数据库用户名一致。你在服务器上用sudo -u postgres psql能连进去,但直接psql -U postgres可能会被拒绝,原因就在这。很多人以为这是密码不对,其实是认证方式不是密码。

变更pg_hba.conf前,先保留一个 root 或 postgres 的可用会话,防止把自己锁在门外。修改完成后用reload而不是restart:

sudo -u postgres psql -c "SELECT pg_reload_conf();"

我个人的建议是:本地 socket 连接保留peer,内网应用服务器 IP 段按需放开,并改成scram-sha-256,公网则一律不开。trust认证在任何时候都不要用,相当于把数据库大门敞开,任何人都能随便登录。

4.3 慢查询日志与 pg_stat_statements

安装后第一件事,我建议把慢查询日志抓起来,不要等到线上出问题才想起来。在postgresql.conf里设置:

log_min_duration_statement = 1000 logging_collector = on log_directory = 'log' log_filename = 'postgresql-%Y-%m-%d.log'

log_min_duration_statement = 1000意思是执行超过 1 秒的 SQL 会记录到日志里。这个值可以根据业务调整,但不要一开始就设成 0,否则日志量会大到没意义。另外强烈建议启用pg_stat_statements扩展,它是 PG 自带的 SQL 执行统计工具,可以统计每种 SQL 的总执行次数、总耗时、平均耗时和缓冲命中情况,查性能瓶颈时比慢查询日志更直观。

启用方式:

shared_preload_libraries = 'pg_stat_statements'

然后重启实例,并在库里执行:

CREATE EXTENSION pg_stat_statements;

这一步必须重启而不是 reload,因为shared_preload_libraries需要在实例启动时就加载。重启后再查看:

SELECT calls, total_exec_time, mean_exec_time, query FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;

就能很快定位到最耗时的几条 SQL,这个习惯可以说是 DBA 的基本功。

5. 安装过程中最容易掉进去的四个坑

5.1 坑一:源没配对,装出来还是旧版本

这是新手最容易犯的错。在 Ubuntu 上执行sudo apt install postgresql或者直接sudo apt install postgresql-17,结果系统从默认软件源里找到了一个老版本,装完一看psql --version发现完全不是想要的 18。

排查方法很简单:

apt-cache policy postgresql-18

如果显示Candidate指向的还是旧版本,说明官方源没有正确添加,或者你的 apt 源优先级有问题。解决方式就是重新执行一遍官方仓库的添加脚本,确保/etc/apt/sources.list.d/下确实有 PGDG 的源文件,并且apt update成功。安装完成后还可以用pg_lsclusters确认默认集群的版本号。

RHEL 系也有同类问题:装完postgresql18-server后,psql可能仍然指向系统自带模块的旧版客户端,因为 PATH 顺序里/usr/bin排在/usr/pgsql-18/bin前面。这时候要么用完整路径调用,要么把 PostgreSQL 18 的 bin 目录加进 PATH。这个坑一般不致命,但会让人困惑半天。

5.2 坑二:shared_buffers 调整后实例直接起不来

调整内存参数翻车,是我见过最多的问题之一。我曾在某次调优时把shared_buffers设成了物理内存的 50%,结果systemctl restart postgresql-18直接失败,看日志是:

could not map anonymous shared memory: Cannot allocate memory

原因是 PostgreSQL 默认使用匿名共享内存(shared_memory_type = mmap),单个共享内存段过大加上系统已有负载,导致内核无法分配足够的连续内存。而且shared_buffers一旦设置错误,实例不会自动回退到默认值,它会直接拒绝启动。

解决办法:先把配置里的shared_buffers改回一个合理范围,比如 25% 左右,再重启一次确认能起来。然后逐步往上调,每次调整后立即验证。这里有个很实用的经验:shared_buffers超过 8GB 之后,对于大多数业务提升并不明显,即便内存充足,也不建议给到 40% 以上,因为 PG 本身还有大量进程级的缓存和工作内存。

5.3 坑三:locale 不一致导致排序与迁移异常

PostgreSQL 的排序规则取决于初始化时指定的 locale。两个实例的数据完全一样,但一个用en_US.UTF-8,一个用C.UTF-8,对中文字符串的排序结果可能完全不同。更麻烦的是,做主从复制或逻辑复制时,发布端和订阅端的 locale 必须一致,否则会出现索引不一致或主键冲突。

我在一次模拟迁移中就是吃了这个亏:准备测试库时用了默认 locale,生产库是C.UTF-8,数据导过去后表面一切正常,但某张表上的唯一索引在复制数据时报了隐式转换错误,整个订阅链路卡住。所以再次强调:initdb 时显式指定--locale,并且让整个复制拓扑里的所有实例保持一致。不要在安装完成后才想着改 locale,改起来要重建集群,非常痛苦。

5.4 坑四:旧数据目录直接指向新二进制

这个坑几乎每个版本升级的人都会踩一次。PostgreSQL 主版本升级后,数据目录的磁盘格式会变化,所以绝对不能直接用旧数据目录启动新版本。我看到的最常见错误操作是:

sudo systemctl stop postgresql-17 sudo mv /var/lib/postgresql/17 /var/lib/postgresql/18 sudo systemctl start postgresql-18

然后日志里果断报一堆incompatible错误。正确做法是使用pg_upgrade工具。它的核心流程是先停掉新旧两个实例,然后执行:

sudo -u postgres /usr/lib/postgresql/18/bin/pg_upgrade \ -b /usr/lib/postgresql/17/bin \ -B /usr/lib/postgresql/18/bin \ -d /var/lib/postgresql/17/main \ -D /var/lib/postgresql/18/main \ --check

先用--check做预检,它会检查权限、磁盘空间、locale、extension 兼容性等一大堆项目,全部通过后再去掉--check真正执行。升级前务必备份,pg_upgrade 虽然成熟,但备份才是最后一道安全网。小库(几个 GB 级别)用pg_dump搭配pg_restore反而更省事,但大库必须走 pg_upgrade,否则导出导入的时间不可接受。

6. 装完之后花十分钟做一轮快速验证

6.1 用 pgbench 目测实例性能

安装和配置都完成后,我建议用一个标准工具pgbench快速确认实例是真的“能用”,而不只是“能启动”。它随 PostgreSQL 一起安装,是一个轻量级压测工具。先建一个压测库,并生成测试数据:

sudo -u postgres createdb bench_test sudo -u postgres pgbench -i -s 50 bench_test

-s 50表示把基准数据放大 50 倍,大约生成 500 万行左右的数据,足够模拟一个中小规模表。然后跑一分钟的压测:

sudo -u postgres pgbench -c 20 -j 4 -T 60 -P 5 bench_test

参数含义是:-c 20开 20 个并发客户端,-j 4用 4 个线程,-T 60持续 60 秒,-P 5每 5 秒打印一次进度。结束后会输出 tps(每秒事务数)和平均延迟。tps 的高低受硬件影响很大,不要拿别人的数字跟自己比,而是用同一套参数分别在旧版本和新版本上跑一次,这个对比才有意义。另外还要留意压测过程中实例有没有报错,有没有明显的内存增长异常,这些比绝对分数更重要。

6.2 确认缓存命中率与连接状态

压测跑完后,连接数可能还残留着,先看一眼连接状态:

SELECT datname, state, count(*) FROM pg_stat_activity GROUP BY datname, state;

正常生产环境里,active连接应该稳定在一个合理范围,idle in transaction如果有大量堆积,通常意味着有应用没正确提交事务。然后在目标库上查缓存命中率:

SELECT datname, blks_read, blks_hit, round(blks_hit * 100.0 / (blks_hit + blks_read), 2) AS hit_ratio FROM pg_stat_database WHERE datname NOT LIKE 'template%';

缓存命中率如果低于 95%,说明shared_buffers可能偏小,或者查询频繁全表扫描导致缓存刷得太快。新装的库刚压测完,这个数字通常会很高,等跑一段真实业务后数字才有参考意义。最后别忘了清理压测库:

sudo -u postgres dropdb bench_test

到这里,一个 PostgreSQL 18 实例就算真正确认可以交付使用了。这次安装那台服务器上测试时留下的最大教训,其实是“版本升级前把每个步骤的退出策略都想清楚”:源没配好就重来,参数设错就改回来,旧数据导坏了就从备份恢复。安装本身从来不是难点,真正决定数据库长期稳定的是初始化时那些看似不起眼的选择——编码、locale、校验和、访问控制、日志策略,这些才是值得你认真对待的东西。

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

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

立即咨询