在单台物理机或虚拟机上部署多个 PostgreSQL 实例,核心逻辑其实并不复杂:每个实例拥有独立的端口、独立的数据目录(Data Directory)和独立的配置文件。
只要做到“多进程隔离”,它们就能在同一台 OS 上和平共处。下面我从生产环境常用的几种方案入手,由浅入深讲清楚怎么做,以及踩坑点在哪里。
一、 核心原理:为什么能共存?
PostgreSQL 是典型的 C/S 架构,后端进程 postgres 负责处理 SQL,前端监听进程 postmaster(在 PG16+ 中已重构为后台辅助进程)负责管理连接。
关键点在于:
- 端口不同:默认是 5432,第二个实例必须用其他端口(如 5433, 5434)。
- 数据目录不同:每个实例有自己的
PGDATA目录,里面存放表空间、日志、配置文件等。 - 用户权限隔离:建议每个实例使用不同的系统用户运行,避免权限混乱导致的安全问题。
二、 实操方案:从源码/包管理器安装后的手动配置
假设你使用的是 CentOS/RHEL 或 Ubuntu,通过 yum/apt 安装了 PostgreSQL。这是最通用、最可控的方式。
第 1 步:创建专用系统用户(推荐)
不要直接用 root 或同一个 postgres 用户跑所有实例。为了安全和资源隔离,建议为每个实例创建独立的用户。
# 创建新用户 group
sudo groupadd pg_group
# 创建第一个实例用户 (pg_instance1)
sudo useradd -g pg_group -d /home/pg_instance1 -s /bin/bash pg_instance1
# 创建第二个实例用户 (pg_instance2)
sudo useradd -g pg_group -d /home/pg_instance2 -s /bin/bash pg_instance2
第 2 步:初始化数据库集群(Initdb)
使用对应的用户执行 initdb,指定不同的数据目录和端口。
# 切换到 pg_instance1
su - pg_instance1
# 初始化第一个实例,数据目录在 ~/data1,端口 5432
initdb -D ~/data1 -p 5432 --encoding=UTF8
# 启动并测试
pg_ctl -D ~/data1 -l logfile start
psql -p 5432 -U pg_instance1 -d postgres
# 退出后,切换到 pg_instance2
exit
su - pg_instance2
# 初始化第二个实例,数据目录在 ~/data2,端口 5433
initdb -D ~/data2 -p 5433 --encoding=UTF8
# 启动
pg_ctl -D ~/data2 -l logfile start
注意:确保
/etc/sysctl.conf中的kernel.shmmax和kernel.shmall足够大,否则多实例同时运行时可能因为共享内存不足而启动失败。
第 3 步:配置防火墙与访问控制
修改 postgresql.conf 中的 listen_addresses 和 port,并确保 pg_hba.conf 允许远程或本地访问。
# postgresql.conf (针对实例1)
listen_addresses = '*'
port = 5432
# pg_hba.conf (针对实例1)
# 允许本地 IPv4 连接
host all all 127.0.0.1/32 scram-sha-256
# 允许特定网段连接
host all all 192.168.1.0/24 scram-sha-256
第 4 步:设置开机自启(Systemd 方式)
这是生产环境必备步骤。为每个实例编写独立的 systemd service 文件。
创建 /etc/systemd/system/postgresql-instance1.service:
[Unit]
Description=PostgreSQL Database Instance 1
After=network.target
[Service]
Type=forking
User=pg_instance1
Group=pg_group
ExecStart=/usr/bin/pg_ctl start -D /home/pg_instance1/data1 -l /var/log/postgres/instance1.log
ExecStop=/usr/bin/pg_ctl stop -D /home/pg_instance1/data1
ExecReload=/usr/bin/pg_ctl reload -D /home/pg_instance1/data1
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
同理创建 postgresql-instance2.service,只需修改 User、Directory 和 Log 路径。
然后启用:
sudo systemctl daemon-reload
sudo systemctl enable postgresql-instance1
sudo systemctl enable postgresql-instance2
三、 进阶方案:容器化部署(Docker/Kubernetes)
如果你已经在使用云原生技术栈,强烈建议使用 Docker 或 Kubernetes 来管理多实例。这比手动维护 systemd 服务更优雅、可移植性更强。
Docker 方式示例
# 实例1
docker run -d
--name pg-instance-1
-e POSTGRES_PASSWORD=pass1
-e POSTGRES_DB=mydb1
-v pg_data_1:/var/lib/postgresql/data
-p 5432:5432
postgres:15-alpine
# 实例2
docker run -d
--name pg-instance-2
-e POSTGRES_PASSWORD=pass2
-e POSTGRES_DB=mydb2
-v pg_data_2:/var/lib/postgresql/data
-p 5433:5432
postgres:15-alpine
优点:
- 环境隔离彻底,依赖不冲突。
- 备份、迁移、销毁极其方便。
- 可以配合 Docker Compose 统一管理。
缺点:
- 需要额外维护容器编排工具。
- 性能略有损耗(但现代内核下几乎可忽略)。
四、 关键注意事项 & 避坑指南
1. 资源竞争问题(最重要!)
单台服务器上的多个 PostgreSQL 实例会竞争 CPU、内存和 I/O。
- 内存:PostgreSQL 的
shared_buffers和work_mem是每个实例独立分配的。如果两个实例都设了很大的值,总内存需求会叠加,可能导致 OOM(Out of Memory)。务必根据服务器总内存合理分配。 - I/O:如果磁盘是机械硬盘或普通 SSD,高并发下 IOPS 会成为瓶颈。建议将不同实例的数据目录放在不同的物理磁盘或 RAID 阵列上,或者至少使用 LVM 卷进行逻辑隔离。
- CPU:使用 cgroups 限制每个实例的最大 CPU 使用率,防止某个实例“饿死”其他实例。
2. 端口冲突
确保没有和其他服务(如 MySQL、Redis)占用相同端口。检查 /etc/services 和已有服务。
3. 时间同步
如果这些实例需要参与分布式事务(如使用 Citus 扩展),必须保证所有节点时间严格同步,否则会出现严重的数据一致性问题。
4. 监控与日志
- 每个实例的日志文件要分开存放,避免日志轮转时混淆。
- 推荐使用 Prometheus + Grafana 监控每个实例的 QPS、连接数、锁等待等指标。
5. 备份策略
- 使用
pg_basebackup或pg_dump分别对每个实例进行备份。 - 如果使用 WAL-G 或 Barman 等工具,可以为每个实例配置独立的备份仓库。
五、 什么时候不该这么做?
虽然技术上可行,但在以下场景中,不建议在单台服务器上部署多个 PostgreSQL 实例:
- 高可用要求极高:单点故障风险太高。一旦服务器宕机,所有业务中断。
- 性能敏感型业务:多实例共享硬件资源会导致不可预测的性能波动。建议按业务拆分到不同服务器。
- 合规性要求:某些XX或XX行业法规要求物理隔离或网络隔离,多实例无法满足。
总结
在单台服务器上部署多个 PostgreSQL 实例,本质上是操作系统层面的多进程管理问题。
- 轻量级/开发测试环境:直接用不同端口和数据目录,systemd 管理即可。
- 生产环境/微服务架构:优先使用 Docker 或 Kubernetes 部署,便于运维和资源调度。
- 核心原则:资源隔离、端口唯一、日志分离、监控到位。
希望这份解答能帮你理清思路。如果有具体的云平台(如阿里云 ECS、腾讯云 CVM)上的特殊配置问题,可以再进一步交流。
云技术博客