15%

全场主机优惠15%

测试技能,享折扣

使用代码:

Skills
开始使用
23.10.2024
1 +1

PostgreSQL在VPS上的部署:架构、性能调优与部署指南

PostgreSQL 是一个先进的开源对象关系数据库管理系统(ORDBMS),支持 SQL 和 JSON 查询、符合 ACID 的事务处理以及可扩展的数据类型。当部署在 虚拟私有服务器 上时,它获得了专用计算资源、完整的内核级配置访问权限和网络隔离——这些是共享主机从根本上无法提供的能力。

对于生产工作负载,这种组合的重要性立竿见影:在共享主机上错误配置的 shared_buffers 值无法修复,邻居实例上失控的查询可能耗尽您的 I/O,而且没有 root 访问权限就无法安装 PostGIS 或 pg_partman 等扩展。VPS 可以同时消除这三个限制。

为什么 PostgreSQL 在开源 RDBMS 选项中表现优异

在研究 VPS 的具体优势之前,有必要了解是什么让 PostgreSQL 成为复杂工作负载中优于 MySQL/MariaDB 的首选引擎。

功能PostgreSQLMySQL 8.xMariaDB 10.x
ACID 合规性完整,包括 DDL完整完整
JSON/JSONB 索引原生 JSONB 配合 GIN 索引JSON(无二进制存储)JSON(无二进制存储)
地理空间支持PostGIS(行业标准)有限的空间类型有限的空间类型
全文搜索内置,可配置基本 FULLTEXT 索引基本 FULLTEXT 索引
表分区声明式,范围/列表/哈希支持分区支持分区
并行查询执行是(可配置工作进程)有限有限
自定义数据类型是(CREATE TYPE)
存储过程(PL/pgSQL)完整过程语言基本基本
预写日志(WAL)可配置,流复制二进制日志二进制日志
并发模型MVCC(无读锁)MVCCMVCC
逻辑复制是(发布/订阅)
外部数据包装器是(postgres_fdw 等)

PostgreSQL 的多版本并发控制(MVCC)模型值得特别关注:读操作永远不会阻塞写操作,写操作也永远不会阻塞读操作。对于混合 OLTP/OLAP 工作负载,这在架构上具有明显优势,因为长时间运行的分析查询否则会锁定事务表。

成本效益:按需分配资源,避免过度配置

VPS 主机方案以远低于裸机硬件的成本提供有保障的 CPU 核心、RAM 和 NVMe SSD 存储。经济逻辑很简单:PostgreSQL 的内存需求随 max_connectionswork_mem 扩展,而非随原始服务器大小扩展。一个经过适当调优、配备 4 GB RAM 的 VPS 服务 50 个并发连接,其性能将优于一个配备 8 GB RAM 但使用默认设置且有 200 个空闲连接占用共享内存的实例。

实际的成本效益策略是:从中等级别的 VPS 开始,在两周的生产负载后分析实际的 pg_stat_activitypg_stat_bgwriter 指标,然后进行垂直扩展。这种数据驱动的方法可以避免在上线时过度配置的常见错误。

一个常被忽视的成本因素:PostgreSQL 的 autovacuum 守护进程需要 CPU 余量。在共享主机上,autovacuum 经常被提供商限速,导致表膨胀和查询计划随时间退化。在 VPS 上,您可以直接控制 autovacuum_vacuum_cost_delayautovacuum_max_workers

完整 Root 访问权限与环境控制

与托管数据库服务或共享虚拟主机不同,VPS 为您提供对操作系统层的不受限访问。这不仅仅是一种便利——对于 PostgreSQL 的某些功能而言,这是硬性要求。

root 访问权限所能实现而共享环境所阻止的功能:

  • 安装 PostgreSQL 扩展(CREATE EXTENSION postgisCREATE EXTENSION pg_trgmCREATE EXTENSION timescaledb
  • 修改直接影响 PostgreSQL 性能的内核参数(vm.overcommit_memoryvm.swappinesshuge_pages
  • 使用自定义身份验证方法配置 pg_hba.conf(SCRAM-SHA-256、LDAP、基于证书的身份验证)
  • 运行 pg_upgrade 进行主要版本迁移,无需提供商介入
  • 在独立块设备上挂载专用表空间卷,实现索引与堆文件之间的 I/O 分离

Linux 上 PostgreSQL 的关键内核调优:

# Disable transparent huge pages (causes latency spikes in PostgreSQL)
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# Set vm.overcommit_memory to allow PostgreSQL shared memory allocation
sysctl -w vm.overcommit_memory=2
sysctl -w vm.overcommit_ratio=80

# Reduce swappiness to prevent paging PostgreSQL shared buffers
sysctl -w vm.swappiness=1

# Persist these settings
echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
echo "vm.overcommit_ratio=80" >> /etc/sysctl.conf
echo "vm.swappiness=1" >> /etc/sysctl.conf

这些设置对应用层托管数据库服务不可见,且经常是云托管 PostgreSQL 实例中无法解释的性能下降的根本原因。

性能调优:真正重要的 postgresql.conf 参数

PostgreSQL 的默认安装参数是有意保守的——它们设计用于在 2000 年代初期的 256 MB RAM 机器上运行。在配备 4–16 GB RAM 和 NVMe 存储的现代 VPS 上,默认设置使大部分硬件能力闲置。

内存配置

# postgresql.conf — tuned for a 8 GB RAM VPS, OLTP workload

# Set to 25% of total RAM
shared_buffers = 2GB

# Estimate of OS cache available to PostgreSQL (typically 50-75% of RAM)
effective_cache_size = 6GB

# Per-sort/hash operation memory (multiply by max_connections for worst case)
work_mem = 32MB

# For VACUUM, CREATE INDEX, ALTER TABLE operations
maintenance_work_mem = 512MB

# Enable huge pages if kernel supports it
huge_pages = try

work_mem 陷阱:work_mem = 256MBmax_connections = 100 结合设置意味着 PostgreSQL 理论上可以为排序操作单独分配 25.6 GB RAM——远超物理内存并触发 OOM 终止。始终按以下公式计算 work_mem(available_RAM - shared_buffers) / (max_connections * 2)

存储与 WAL 配置

# For NVMe SSD storage — set to 1 for spinning disks, 200 for NVMe
random_page_cost = 1.1
effective_io_concurrency = 200

# WAL configuration for durability vs. performance tradeoff
wal_buffers = 64MB
checkpoint_completion_target = 0.9
checkpoint_timeout = 10min

# For write-heavy workloads, consider asynchronous commit
# (risk: last ~1 transaction lost on crash, not data corruption)
synchronous_commit = on  # Keep 'on' for financial data

连接管理

# Avoid setting this above what your application actually needs
max_connections = 100

# Use PgBouncer in transaction pooling mode for high-concurrency apps
# A VPS allows you to install and configure PgBouncer locally

对于并发用户超过 50 的应用,PgBouncer 不是可选项。每个 PostgreSQL 后端进程消耗约 5–10 MB RAM。在 200 个连接时,空闲进程将消耗 1–2 GB。PgBouncer 在事务池模式下可将数百个应用连接多路复用到少量实际 PostgreSQL 后端上。

# Install PgBouncer on Debian/Ubuntu
apt install pgbouncer

# Minimal pgbouncer.ini configuration
cat /etc/pgbouncer/pgbouncer.ini
[databases]
mydb = host=127.0.0.1 port=5432 dbname=mydb

[pgbouncer]
listen_port = 6432
listen_addr = 127.0.0.1
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20

安全加固:超越默认安装

VPS 上全新安装的 PostgreSQL 存在几个安全漏洞,必须在实例可从网络访问之前加以修复。

网络级隔离

除非绝对必要,PostgreSQL 不应监听公共 IP。将其绑定到 localhost,并使用 SSH 隧道或 VPN 进行远程管理。

# In postgresql.conf
listen_addresses = 'localhost'

# For replication or application servers on a private network only
# listen_addresses = '127.0.0.1,10.0.0.1'

配置 pg_hba.conf 以强制执行 SCRAM-SHA-256 身份验证(默认的 md5 在密码学上较弱):

# /etc/postgresql/16/main/pg_hba.conf
# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             postgres                                peer
local   all             all                                     scram-sha-256
host    all             all             127.0.0.1/32            scram-sha-256
host    all             all             ::1/128                 scram-sha-256
# Reject all other connections by default (no catch-all line)

远程连接的 SSL/TLS 加密

如果您的应用服务器通过网络连接到 PostgreSQL,则必须对连接进行加密。将此与应用层的 SSL 证书配合使用,并配置 PostgreSQL 自身的 TLS 堆栈:

# Generate a self-signed certificate for internal use
openssl req -new -x509 -days 365 -nodes 
  -out /etc/postgresql/16/main/server.crt 
  -keyout /etc/postgresql/16/main/server.key

chmod 600 /etc/postgresql/16/main/server.key
chown postgres:postgres /etc/postgresql/16/main/server.{crt,key}
# postgresql.conf
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
ssl_min_protocol_version = 'TLSv1.2'

基于角色的访问控制

最小权限原则严格适用于数据库角色:

-- Create an application role with minimal permissions
CREATE ROLE app_user WITH LOGIN PASSWORD 'strong_password_here';
GRANT CONNECT ON DATABASE mydb TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;

-- Create a read-only analytics role
CREATE ROLE analytics_reader WITH LOGIN PASSWORD 'another_strong_password';
GRANT CONNECT ON DATABASE mydb TO analytics_reader;
GRANT USAGE ON SCHEMA public TO analytics_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO analytics_reader;

-- Never use the superuser 'postgres' role for application connections

防火墙规则

# Allow PostgreSQL only from specific application server IP
ufw allow from 10.0.0.5 to any port 5432
ufw deny 5432

# Verify
ufw status verbose

备份与恢复架构

PostgreSQL 提供两种根本不同的备份机制,各自适用于不同的恢复目标。

备份方法工具恢复类型RPORTO使用场景
逻辑备份pg_dump / pg_dumpall对象级恢复小时级中等模式迁移,选择性表恢复
物理备份pg_basebackup完整集群恢复分钟级(配合 WAL)快速灾难恢复,备用节点创建
持续归档WAL 归档 + pg_basebackup时间点恢复秒级取决于 WAL 量零数据丢失要求
快照VPS 提供商快照完整服务器恢复快照时间点快速升级前安全保障

带压缩的逻辑备份:

# Dump a single database in custom format (supports parallel restore)
pg_dump -U postgres -Fc -Z 9 mydb > /backup/mydb_$(date +%Y%m%d).dump

# Restore
pg_restore -U postgres -d mydb_restored /backup/mydb_20240115.dump

# Dump all databases including roles and tablespaces
pg_dumpall -U postgres | gzip > /backup/full_cluster_$(date +%Y%m%d).sql.gz

用于灾难恢复的物理备份:

# Take a base backup (can run while PostgreSQL is live)
pg_basebackup -U replication_user -D /backup/base -Ft -z -Xs -P

# This creates base.tar.gz and pg_wal.tar.gz
# Combined with WAL archiving, enables point-in-time recovery

使用 cron 自动化:

# /etc/cron.d/postgres-backup
0 2 * * * postgres pg_dump -Fc mydb > /backup/mydb_$(date +%Y%m%d_%H%M).dump
0 3 * * 0 postgres pg_dumpall | gzip > /backup/full_$(date +%Y%m%d).sql.gz

# Prune backups older than 30 days
0 4 * * * root find /backup/ -name "*.dump" -mtime +30 -delete

可扩展性:垂直扩展、水平扩展与读扩展

垂直扩展

在 VPS 上,垂直扩展(增加 CPU、RAM、存储)通常是在线操作,或仅需短暂重启。升级 RAM 后,按比例更新 shared_bufferseffective_cache_sizework_mem。增加 CPU 核心后,增大 max_parallel_workers_per_gathermax_parallel_maintenance_workers

# After upgrading from 4 to 8 CPU cores
max_parallel_workers_per_gather = 4
max_parallel_maintenance_workers = 2
max_parallel_workers = 8

用于读扩展的流复制

PostgreSQL 内置的流复制可创建热备节点,该节点可服务读查询,从主节点卸载分析工作负载:

# On primary: create replication user
psql -U postgres -c "CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'rep_password';"

# In pg_hba.conf on primary
# host replication replicator 10.0.0.2/32 scram-sha-256

# In postgresql.conf on primary
# wal_level = replica
# max_wal_senders = 3
# wal_keep_size = 1GB
# On standby: initialize from primary
pg_basebackup -h 10.0.0.1 -U replicator -D /var/lib/postgresql/16/main 
  -P -Xs -R

# The -R flag creates standby.signal and populates primary_conninfo automatically

用于选择性复制的逻辑复制

逻辑复制允许将特定表复制到另一个 PostgreSQL 实例,适用于数据仓库管道:

-- On publisher
CREATE PUBLICATION analytics_pub FOR TABLE orders, customers, products;

-- On subscriber
CREATE SUBSCRIPTION analytics_sub
  CONNECTION 'host=10.0.0.1 dbname=mydb user=replicator password=rep_password'
  PUBLICATION analytics_pub;

对于需要将独立服务器用于主数据库、VPS 副本处理读流量的应用,这种架构同时提供了性能和成本效益。

值得启用的高级 PostgreSQL 功能

用于混合关系/文档工作负载的 JSONB

-- Create a table with JSONB column
CREATE TABLE events (
  id BIGSERIAL PRIMARY KEY,
  occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  payload JSONB NOT NULL
);

-- Create a GIN index for fast JSONB queries
CREATE INDEX idx_events_payload ON events USING GIN (payload);

-- Query nested JSON efficiently
SELECT * FROM events
WHERE payload @> '{"type": "purchase", "currency": "USD"}';

-- Extract and index a specific JSON key
CREATE INDEX idx_events_user_id ON events ((payload->>'user_id'));

用于大型数据集的表分区

-- Range partitioning by month (ideal for time-series data)
CREATE TABLE measurements (
  id BIGSERIAL,
  recorded_at TIMESTAMPTZ NOT NULL,
  sensor_id INT,
  value NUMERIC
) PARTITION BY RANGE (recorded_at);

CREATE TABLE measurements_2024_01
  PARTITION OF measurements
  FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');

CREATE TABLE measurements_2024_02
  PARTITION OF measurements
  FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');

-- PostgreSQL automatically routes inserts and prunes partitions in queries

用于地理空间应用的 PostGIS

# Install PostGIS extension
apt install postgresql-16-postgis-3

# Enable in database
psql -U postgres -d mydb -c "CREATE EXTENSION postgis;"
-- Store and query geographic coordinates
CREATE TABLE locations (
  id SERIAL PRIMARY KEY,
  name TEXT,
  geom GEOMETRY(Point, 4326)
);

-- Find all locations within 10km of a point
SELECT name, ST_Distance(geom::geography, ST_MakePoint(-73.935242, 40.730610)::geography) AS distance_m
FROM locations
WHERE ST_DWithin(geom::geography, ST_MakePoint(-73.935242, 40.730610)::geography, 10000)
ORDER BY distance_m;

监控:生产 PostgreSQL 的可观测性堆栈

被动故障排除对于生产数据库来说是不够的。主动可观测性堆栈可在性能下降演变为中断之前发现问题。

PostgreSQL 内置统计视图

-- Identify slow queries (requires pg_stat_statements extension)
CREATE EXTENSION pg_stat_statements;

SELECT query, calls, total_exec_time / calls AS avg_ms,
       rows / calls AS avg_rows
FROM pg_stat_statements
ORDER BY avg_ms DESC
LIMIT 20;

-- Check for table bloat and vacuum status
SELECT schemaname, relname, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

-- Monitor replication lag
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
       (sent_lsn - replay_lsn) AS replication_lag_bytes
FROM pg_stat_replication;

-- Find long-running queries
SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state
FROM pg_stat_activity
WHERE (now() - pg_stat_activity.query_start) > interval '5 minutes';

Prometheus + postgres_exporter 堆栈

# Install postgres_exporter
wget https://github.com/prometheus-community/postgres_exporter/releases/download/v0.15.0/postgres_exporter-0.15.0.linux-amd64.tar.gz
tar xzf postgres_exporter-0.15.0.linux-amd64.tar.gz
mv postgres_exporter-0.15.0.linux-amd64/postgres_exporter /usr/local/bin/

# Create a monitoring role in PostgreSQL
psql -U postgres -c "CREATE ROLE postgres_exporter WITH LOGIN PASSWORD 'monitor_pass';"
psql -U postgres -c "GRANT pg_monitor TO postgres_exporter;"

# Run the exporter
export DATA_SOURCE_NAME="postgresql://postgres_exporter:monitor_pass@localhost:5432/postgres?sslmode=disable"
postgres_exporter --web.listen-address=":9187"

postgres_exporter 与 Grafana 仪表板配合使用(grafana.com 上的仪表板 ID 9628 涵盖所有关键 PostgreSQL 指标),并为以下情况配置告警:复制延迟超过 30 秒、事务 ID 回绕临近、缓存命中率低于 95%,以及死元组数量超过活跃元组的 10%。

使用场景矩阵:将 PostgreSQL 配置与工作负载匹配

工作负载关键参数扩展扩展策略
OLTP(电商、SaaS)work_mem、PgBouncer、synchronous_commit=onpg_stat_statementspgcrypto垂直扩展 + 读副本
分析 / OLAPwork_mem、并行工作进程、synchronous_commit=offpg_partmantablefunc分区 + 列式存储
时序 IoT按时间分区、autovacuum 调优TimescaleDB、pg_partman分区裁剪 + 压缩
地理空间 / GIS空间索引、effective_io_concurrencyPostGIS、pg_routing大型数据集使用独立服务器
API 后端(JSON)JSONB 上的 GIN 索引、work_mem 用于聚合pg_trgmuuid-ossp读副本用于 GET 密集型 API
全文搜索tsvector 列、GIN 索引pg_trgmunaccent仅索引扫描,部分索引

对于构建 API 后端或 Web 应用的团队,将 PostgreSQL 与带 cPanel 的 VPS 配合使用,可在完整数据库灵活性的同时提供托管控制面板。对于偏好 CLI 驱动管理的基础设施团队,VPS 控制面板提供了更广泛的面板选项。

在 VPS 上部署 PostgreSQL 前的实用决策清单

硬件规格:

  • shared_buffers 计算为总 RAM 的 25%
  • 确认 NVMe SSD 存储——PostgreSQL 的 WAL 写入对延迟敏感
  • 为生产工作负载分配至少 2 个专用 CPU 核心

安全基线:

  • listen_addresses 绑定到私有/localhost 地址
  • pg_hba.conf 中将 md5 替换为 scram-sha-256
  • 为任何远程连接启用最低 TLS 1.2 的 SSL
  • 创建应用专用角色——切勿在应用代码中使用 postgres 超级用户
  • 配置 ufwiptables,仅将已知源 IP 加入 5432 端口白名单

性能基线:

  • 在操作系统级别禁用透明大页
  • 设置 vm.swappiness=1 以防止共享缓冲区分页
  • 如果连接数超过 50,安装并配置 PgBouncer
  • 从第一天起启用 pg_stat_statements——事后查询分析是不可能的

备份与恢复:

  • 使用 cron 自动化 pg_dump,每月测试恢复
  • 如果 RPO 要求低于 1 小时,实施 WAL 归档
  • 将应用层备份与 VPS 提供商快照结合,实现分层保护

可观测性:

  • 在上线前部署 postgres_exporter + Prometheus + Grafana
  • 针对复制延迟、事务 ID 年龄和缓存命中率设置告警
  • 每周审查 pg_stat_bgwriter 以检测检查点压力

常见问题

在新 VPS 上应该安装哪个 PostgreSQL 版本?

始终从官方 PGDG 仓库安装最新稳定主要版本(截至 2024 年为 PostgreSQL 16),而非 Linux 发行版自带的版本。发行版软件包通常落后 1–2 个主要版本,且不接收上游功能向后移植。使用 apt.postgresql.orgyum.postgresql.org 进行安装。

PostgreSQL VPS 实际需要多少 RAM?

对于并发连接数低于 50、数据集低于 50 GB 的小型生产应用,4 GB RAM 是实际最低要求。设置 shared_buffers = 1GBwork_mem = 16MB,并使用 PgBouncer。对于超出可用 RAM 的数据集,在增加硬件之前应专注于索引覆盖和查询计划优化——100 GB 表上缺失的索引无法通过增加 RAM 来解决。

在同一 VPS 上同时运行 PostgreSQL 和应用是否安全?

对于中小型工作负载,是安全的。风险在于资源竞争:应用中的内存峰值可能触发 OOM 终止,进而终止 PostgreSQL。通过将 PostgreSQL 的 oom_score_adj 设置为负值(使其不太可能被终止)并使用 cgroups 限制应用的内存上限来降低此风险。

pg_dumppg_basebackup 有什么区别?

pg_dump 生成单个数据库的逻辑备份——它导出 SQL 语句或自定义二进制格式,可以选择性地恢复(单个表、模式)。pg_basebackup 在二进制级别复制整个 PostgreSQL 数据目录,生成适用于灾难恢复和备用服务器初始化的完整集群备份。两者都要使用:pg_dump 用于细粒度恢复,pg_basebackup 用于完整恢复场景。

如何在 VPS 上安全地将 PostgreSQL 升级到新主要版本?

首先使用带 --check 标志的 pg_upgrade 验证兼容性,而不做任何更改。在继续之前进行完整的 pg_basebackup 备份。升级本身需要离线执行(PostgreSQL 必须停止)。对于零停机主要版本升级,使用逻辑复制:将新的 PostgreSQL 16 实例设置为 PostgreSQL 15 主节点的逻辑订阅者,等待其追上进度,然后以最短停机时间执行协调切换。

15%

全场主机优惠15%

测试技能,享折扣

使用代码:

Skills
开始使用