Aekor

Aekor
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. Blog
  3. 正文

PostgreSQL单机部署

2026-10-17 132440点热度 0人点赞 0条评论

这篇把 PostgreSQL 18 的单机部署一次讲完:Ubuntu 26.04、8C/4G、50G 数据盘,源码编译到 /data/pgsql,端口 5433。命令按顺序可以直接执行,参数按 4G 内存给出一套能落地的基线;文末列出 12 个容易踩的坑,其中几个会直接导致启动失败或登录失败。

项目配置
架构单机
操作系统Ubuntu 26.04
CPU / 内存8C / 4GB
数据库版本PostgreSQL 18.0(源码编译)
端口5433
数据盘50GB
根目录/data

一、部署边界与目录规划

1.1 这套方案适合谁

适合单机测试、中小规模生产、以及需要自定义编译参数(SSL、ICU、LZ4/ZSTD、systemd)的场景。它不覆盖 Patroni/repmgr 高可用和连接池;第八节只给流复制的预留参数,主备切换与读写分离不在本文范围。

1.2 目录规划

目录用途
/data/soft源码包与安装包
/data/pgsql/install程序安装目录(--prefix)
/data/pgsql/data数据目录(PGDATA)
/data/pgsql/log数据库运行日志
/data/pgsql/archiveWAL 归档目录(可选,建议独立磁盘)

先把目录一次建好,后面所有命令都围绕这套路径,不再改。

二、操作系统准备

2.1 内核参数:shmmax/shmall 到底还要不要设

PostgreSQL 18 在 Linux 上默认使用 shared_memory_type=mmap(POSIX 共享内存),并不依赖 System V 共享内存。所以 kernel.shmmax、kernel.shmall 只在把 shared_memory_type 改成 sysv 时才真正生效。保留这两项作为兼容没问题,但值要算对。

MemTotal 的单位是 kB,shmmax 的单位是字节,shmall 的单位是页。以 4GB 内存为例:

grep MemTotal /proc/meminfo
# MemTotal: 3861296 kB

getconf PAGESIZE
# 4096

一半内存换算成字节:3861296 / 2 * 1024 = 1976983552;再除以页大小:1976983552 / 4096 = 482662。所以 4GB 机器应写成 kernel.shmmax = 1976983552、kernel.shmall = 482662,而不是 1930648 / 471。后者把 kB 当成了字节,小了 1024 倍;默认 mmap 下看不出问题,一旦切到 sysv,shared_buffers=1GB 会直接起不来。

编辑 /etc/sysctl.conf,在文件末尾追加:

kernel.sysrq = 1
kernel.pid_max = 524288
fs.file-max = 76724200
fs.aio-max-nr = 40960000

# System V IPC:仅 shared_memory_type=sysv 时需要
kernel.sem = 10000 10240000 10000 1024
kernel.shmmni = 4096
kernel.shmmax = 1976983552
kernel.shmall = 482662

net.ipv4.ip_local_port_range = 9000 65000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_retries2 = 5
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 4096
net.core.netdev_max_backlog = 3000

vm.swappiness = 10
vm.dirty_ratio = 20
vm.dirty_background_ratio = 3
vm.dirty_writeback_centisecs = 100
vm.dirty_expire_centisecs = 500
# 4GB 内存留 64MB 空闲页即可,512MB 会挤压 page cache
vm.min_free_kbytes = 65536

生效并确认:

sysctl -p
sysctl kernel.shmmax kernel.shmall vm.min_free_kbytes

2.2 用户资源限制

编辑 /etc/security/limits.conf,把下面几行加到文件末尾:

postgres soft nofile 1048576
postgres hard nofile 1048576
postgres soft nproc 131072
postgres hard nproc 131072
postgres soft stack 10240
postgres hard stack 32768
postgres soft core 6291456
postgres hard core 6291456
postgres soft memlock unlimited
postgres hard memlock unlimited

注意 /etc/security/limits.d/ 下的文件可能覆盖这里的值;用 su - postgres 登录后执行 ulimit -n、ulimit -u 验证。如果后续改用 systemd 托管,还要在 unit 里设置 LimitNOFILE、LimitNPROC、LimitMEMLOCK,否则这些 limits 对服务不生效。

2.3 防火墙

实验环境可以关掉 ufw:

systemctl status ufw
systemctl stop ufw
systemctl disable ufw

生产环境不要整体关闭。只放行数据库端口,并限定来源网段:

ufw allow from 192.168.0.0/24 to any port 5433 proto tcp
ufw status verbose

2.4 用户、组与目录

先建组,再建用户。Ubuntu 的 useradd 在 USERGROUPS_ENAB=yes 时可能自动创建同名组,顺序反过来 groupadd 会报“已存在”。

groupadd -f postgres
useradd -d /home/postgres -m -g postgres -s /bin/bash postgres

# Ubuntu 的 passwd 不支持 --stdin,用 chpasswd
echo 'postgres:换成系统密码' | chpasswd

mkdir -p /data/{soft,pgsql}
mkdir -p /data/pgsql/{install,data,log,archive}

# 运行目录归 postgres;soft 和 install 归 root,编译安装用 root 执行
chown -R postgres:postgres /data/pgsql/data /data/pgsql/log /data/pgsql/archive
chmod 750 /data /data/pgsql
chmod 700 /data/pgsql/data /data/pgsql/archive

不要用 chmod -R 755 /data。数据目录对同组以外的用户可读,等于把数据库文件暴露给机器上的所有账号;initdb 自己会把 PGDATA 收紧到 700,手工再放开就前功尽弃了。

三、源码编译 PostgreSQL 18

本节命令用 root 执行:程序装到 /data/pgsql/install,由 root 管理;数据目录在第四节交给 postgres。

3.1 安装依赖

apt update
apt -y install build-essential libssl-dev libxml2-dev libxslt1-dev \
  python3-dev zlib1g-dev bzip2 libreadline-dev expect git \
  uuid-dev libossp-uuid-dev libsystemd-dev libicu-dev liblz4-dev \
  libzstd-dev liburing-dev pkg-config bison flex libxml2-utils docbook-xsl

liburing-dev 只在启用 --with-liburing 时需要;如果发行版仓库没有这个包,把对应 configure 参数一起去掉。

3.2 下载与校验

cd /data/soft
curl -LO https://ftp.postgresql.org/pub/source/v18.0/postgresql-18.0.tar.gz
sha256sum postgresql-18.0.tar.gz
# 期望值:
# 30e97fc1f1594a7226580ea51080d3d883f4a0fb6ff716dbf7ad09e9f7e93ab2

输出与期望值一致再解压:

tar -zxf postgresql-18.0.tar.gz -C /data/pgsql/install/
cd /data/pgsql/install/postgresql-18.0

3.3 configure

./configure --prefix=/data/pgsql/install \
  --with-ssl=openssl \
  --with-libxml \
  --with-libxslt \
  --with-uuid=ossp \
  --with-systemd \
  --with-icu \
  --with-lz4 \
  --with-zstd \
  --with-liburing

--with-openssl、--with-ossp-uuid 是旧写法,PG 18 仍接受但会提示已过时;新写法分别是 --with-ssl=openssl 和 --with-uuid=ossp。ICU 在 18 里默认开启,显式写出来是为了让构建参数可追溯。--with-liburing 会让 io_method=io_uring 可用,但 18 的默认值仍是 worker,要启用异步 I/O 得在 postgresql.conf 里显式设置。

3.4 编译与安装

make -j"$(nproc)" world-bin
make install-world-bin

这里不要只执行 make && make install。那只会装核心程序,contrib 里的 uuid-ossp、pg_stat_statements、auto_explain、passwordcheck 都不会安装;后面在 shared_preload_libraries 里写这些名字,启动时会报“could not access file”。world-bin 会连 contrib 一起编译,但跳过文档,适合生产机。

3.5 确认版本与编译参数

/data/pgsql/install/bin/postgres --version
/data/pgsql/install/bin/pg_config --configure

四、初始化与首次启动

4.1 initdb

切到 postgres 用户执行:

su - postgres
/data/pgsql/install/bin/initdb \
  --wal-segsize=256 \
  -D /data/pgsql/data \
  -E UTF8 --locale=C -U postgres -W

--wal-segsize=256 表示单个 WAL 段 256MB,必须是 1–1024 之间的 2 的幂,初始化后不能再改。PG 18 默认开启数据页校验和,输出里会看到 Data page checksums are enabled。

-W 会提示输入超级用户密码。测试环境可以直接回车留空,但要知道后果:空密码会被清空,等于这个账号没有密码;后面把 pg_hba.conf 改成 scram-sha-256 或 md5 后,远程登录一定失败。所以下一步必须先设密码,再改认证方式。

4.2 环境变量

cat >> ~/.bashrc <<'EOF'
export PGHOME=/data/pgsql/install
export PGDATA=/data/pgsql/data
export PGPORT=5433
export PGUSER=postgres
export PGHOST=127.0.0.1
export PATH=$PGHOME/bin:$PATH
export MANPATH=$PGHOME/share/man:$MANPATH
export LD_LIBRARY_PATH=$PGHOME/lib
export PGPASSFILE=$HOME/.pgpass
EOF

source ~/.bashrc

PGHOST=127.0.0.1 强制走 TCP,所以 pg_hba.conf 里的 host 规则会生效。如果想走本地 socket,去掉 PGHOST,并把 unix_socket_directories 指到一个固定目录(见 5.1)。

4.3 首次启动

mkdir -p /data/pgsql/log
chown postgres:postgres /data/pgsql/log

# 首次启动把日志落到文件;后续重启沿用 postgresql.conf 里的 log_directory
pg_ctl -D "$PGDATA" -l /data/pgsql/log/pg.log start
ps -ef | grep '[p]ostgres'

日志路径写绝对路径。pg_ctl -l logfile 是相对当前工作目录的,换一个目录启动就会把日志丢到别处;下面 5.1 里把 log_directory 也指到 /data/pgsql/log,两边保持一致。

4.4 设置管理员密码

此时本地连接还是 initdb 默认的 trust,可以直接进:

psql -p 5433 -d postgres
ALTER USER postgres WITH PASSWORD '换成强密码';
\q

PG 18 的 password_encryption 默认是 scram-sha-256,新密码会按 SCRAM 存储。md5 认证已被标记为废弃,新部署直接用 SCRAM。改完 pg_hba.conf 并 reload 后,再用 psql 连接就会要求输入这个密码。

五、参数配置

5.1 postgresql.conf 基线

配置文件是 $PGDATA/postgresql.conf,不是 postgres.conf。改之前先确认路径:

psql -c "SHOW config_file;"

下面这套按 8C/4G 给基线。它是替换 postgresql.conf 中对应段落,不是追加;同一个参数出现两次时,后出现的生效。

# ---------- 连接 ----------
listen_addresses = '*'
port = 5433
max_connections = 300
superuser_reserved_connections = 10
unix_socket_directories = '/tmp'
unix_socket_permissions = 0700

# ---------- 内存 ----------
shared_buffers = 1GB
effective_cache_size = 3GB
work_mem = 16MB
maintenance_work_mem = 256MB
huge_pages = try

# ---------- WAL ----------
wal_level = replica
synchronous_commit = on
wal_buffers = 16MB
checkpoint_timeout = 15min
max_wal_size = 4GB
min_wal_size = 1GB
checkpoint_completion_target = 0.9

# ---------- 并行 ----------
max_worker_processes = 8
max_parallel_workers = 8
max_parallel_workers_per_gather = 2

# ---------- 规划器 ----------
random_page_cost = 1.1
effective_io_concurrency = 200

# ---------- 日志 ----------
logging_collector = on
log_directory = '/data/pgsql/log'
log_filename = 'postgresql-%a.log'
log_rotation_age = 1d
log_rotation_size = 0
log_truncate_on_rotation = on
log_min_duration_statement = 1000
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_statement = 'ddl'
log_temp_files = 0
log_timezone = 'Asia/Shanghai'
log_line_prefix = '%m [%p] %q%u@%d/%a '

# ---------- 自动清理 ----------
autovacuum_max_workers = 3
autovacuum_naptime = 30s
autovacuum_vacuum_scale_factor = 0.1
autovacuum_analyze_scale_factor = 0.05

# ---------- 客户端默认值 ----------
timezone = 'Asia/Shanghai'
datestyle = 'iso, mdy'
lc_messages = 'C'
default_text_search_config = 'pg_catalog.english'

# ---------- 扩展 ----------
shared_preload_libraries = 'pg_stat_statements,auto_explain,passwordcheck'
pg_stat_statements.max = 10000
pg_stat_statements.track = all
auto_explain.log_min_duration = 5s
auto_explain.log_analyze = on
auto_explain.log_buffers = on
auto_explain.log_verbose = on
auto_explain.log_nested_statements = on

5.2 几个关键参数的取舍

shared_buffers 不是越大越好。4GB 内存给 1GB(约 25%)是合理起点;给 4GB 会和操作系统抢页缓存,启动阶段就可能失败。effective_cache_size 不是真实分配,只是告诉优化器“系统大概有多少缓存可用”,4GB 机器填 3GB,16GB 机器填 12GB,64GB 机器填 48GB。

max_connections=3000 在 4GB 机器上不现实。每个后端进程都有基础内存开销,3000 个连接光这部分就超过整机内存;再叠加 work_mem(每个排序/哈希节点一份,不是每个连接一份),很容易 OOM。单机先设 300,应用侧上 PgBouncer 之类的连接池,比盲目加连接数有效。

synchronous_commit=on 是单机默认值,保证事务提交后 WAL 已落盘;改成 off 能换一点吞吐,但崩溃时可能丢掉最近若干已提交事务。它常被当成“异步流”的开关,那是主备场景的取舍;单机没有备库兜底,不建议关。

wal_level=replica 足够单机使用;需要逻辑复制或 CDC 再改 logical,代价是 WAL 体积增加。random_page_cost=1.1 假设数据盘是 SSD/NVMe;机械盘保持 4.0 左右。

5.3 pg_hba.conf:顺序比内容更重要

pg_hba.conf 从上往下匹配,第一条命中就生效,后面的规则不再看。所以“拒绝超级用户远程登录”的规则必须放在放行规则之前,否则永远不会命中。另外 md5 是认证方式,不是拒绝;要拒绝就写 reject。

# TYPE   DATABASE   USER       ADDRESS          METHOD

# 1) 本机管理入口:只允许 postgres 从回环地址登录
host     all        postgres   127.0.0.1/32     scram-sha-256
host     all        postgres   ::1/128          scram-sha-256

# 2) 拒绝 postgres 从其他地址登录(必须在放行规则之前)
host     all        postgres   0.0.0.0/0        reject
host     all        postgres   ::/0             reject

# 3) 业务网段普通账号
host     all        all        192.168.0.0/24   scram-sha-256

# 4) 本地 socket
local    all        all                         scram-sha-256

改完不需要重启,reload 即可:

pg_ctl reload -D "$PGDATA"
# 或者
psql -c "SELECT pg_reload_conf();"

5.4 reload 还是 restart

shared_buffers、max_connections、port、shared_preload_libraries 这些是 postmaster 级参数,必须重启;work_mem、log_min_duration_statement、pg_hba.conf 这类可以 reload。拿不准就看参数上下文,或者直接重启——单机维护窗口内重启最省事:

pg_ctl -D "$PGDATA" -l /data/pgsql/log/pg.log restart

5.5 开机自启(systemd)

用 pg_ctl 手工启动不适合生产。写一个 unit:

[Unit]
Description=PostgreSQL 18
After=network.target

[Service]
Type=notify
User=postgres
Group=postgres
Environment=PGDATA=/data/pgsql/data
Environment=PGPORT=5433
ExecStart=/data/pgsql/install/bin/postgres -D /data/pgsql/data
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5min
LimitNOFILE=1048576
LimitNPROC=131072
LimitMEMLOCK=infinity

[Install]
WantedBy=multi-user.target

保存为 /etc/systemd/system/postgresql-18.service,然后:

systemctl daemon-reload
systemctl enable --now postgresql-18
systemctl status postgresql-18

Type=notify 依赖编译时的 --with-systemd;没有这个参数就用 Type=simple。

六、验证

6.1 连接与版本

psql -h 127.0.0.1 -p 5433 -U postgres -d postgres -c "SELECT version();"

6.2 建库、建表、写入

CREATE DATABASE test;
\c test

CREATE TABLE testtbs (
    id   bigserial PRIMARY KEY,
    name varchar(30)
);

INSERT INTO testtbs (name) VALUES ('测试');
SELECT * FROM testtbs;

6.3 清理

DROP TABLE testtbs;
\c postgres
DROP DATABASE test;

6.4 日常巡检

-- 当前连接
SELECT pid, usename, datname, state, wait_event_type, wait_event,
       now() - query_start AS duration, left(query, 80) AS query
FROM pg_stat_activity
WHERE state <> 'idle'
ORDER BY duration DESC;

-- 数据库大小
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;

-- 表与索引大小
SELECT relname, pg_size_pretty(pg_total_relation_size(oid)) AS total
FROM pg_class
WHERE relkind IN ('r','i')
ORDER BY pg_total_relation_size(oid) DESC
LIMIT 20;

-- 慢 SQL 排行(需先 CREATE EXTENSION pg_stat_statements;)
SELECT calls, round(total_exec_time::numeric, 2) AS total_ms,
       round(mean_exec_time::numeric, 2) AS mean_ms,
       left(query, 100) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

七、12 个容易踩的坑

  1. 配置文件名写错:是 postgresql.conf,不是 postgres.conf。用 SHOW config_file; 确认,改错文件不会报错,只会“怎么改都不生效”。
  2. shmmax/shmall 单位错 1024 倍:MemTotal 是 kB,shmmax 是字节,shmall 是页。默认 mmap 下不影响,切到 sysv 后 shared_buffers=1GB 会直接起不来。
  3. Ubuntu 没有 passwd --stdin:passwd 不支持从标准输入读密码;用 echo 'postgres:密码' | chpasswd。
  4. groupadd 放在 useradd 之后:Ubuntu 可能已经自动建了同名组,groupadd 会报“已存在”。先建组,再 useradd -g。
  5. pg_hba.conf 顺序写反:第一条命中即生效。把 reject 放在放行规则后面,等于没写;md5 也不是拒绝。
  6. initdb 留空密码后又改成 md5/scram:空密码会被清空,账号等于没有密码,远程登录必然失败。先 ALTER USER 设密码,再改认证方式。
  7. 4GB 内存配 4GB shared_buffers / 3000 连接:启动失败或 OOM。按内存比例缩放,连接数交给连接池。
  8. 只执行 make install:contrib 没装,uuid-ossp、pg_stat_statements、auto_explain、passwordcheck 全部缺失,shared_preload_libraries 启动时报“could not access file”。
  9. chmod -R 755 /data:数据目录对所有人可读。用 750/700,PGDATA 交给 initdb 收紧。
  10. lc_messages='en_US.utf8':最小化系统没生成这个 locale,启动直接失败。用 C,或先 locale-gen en_US.UTF-8 并确认名称大小写。
  11. unix_socket_directories='.':socket 落在数据目录,裸 psql 去 /tmp 找不到。改成 /tmp,或用 psql -h /data/pgsql/data。
  12. 归档和 WAL 放同一块 50G 盘:archive_mode=on 后归档会持续增长,迟早写满。归档目录放独立盘或对象存储,并监控磁盘水位。

八、下一步:备份、归档与流复制

单机跑起来只是开始。最低限度做三件事:

# 逻辑备份(每天)
pg_dump -Fc -d appdb -f /backup/appdb_$(date +%F).dump

# 物理备份(每周,需先配置归档或复制槽)
pg_basebackup -D /backup/base_$(date +%F) -Ft -z -P -X stream

# 归档目录权限
chown postgres:postgres /data/pgsql/archive
chmod 700 /data/pgsql/archive

如果准备做流复制,在主库预留这些参数,等备库搭好再启用:

wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = 2GB
hot_standby = on

archive_mode、archive_command、restore_command 属于备份恢复链路,建议用 pgBackRest 统一管理,不要只靠一条 cp 命令。synchronous_standby_names 只在有同步备库时设置,单机写了会导致提交等待一个不存在的备库。

recovery_target_timeline 只在崩溃恢复或备库提升时使用,主库上写了不生效,不要把它当成流复制的开关。archive_mode 改动需要重启,archive_command 用 cp 也不是原子操作;正式环境交给 pgBackRest 或 pg_receivewal。

附录:按内存放大参数

参数4GB16GB64GB
shared_buffers1GB4GB16GB
effective_cache_size3GB12GB48GB
maintenance_work_mem256MB1GB2GB
work_mem16MB32MB64MB
max_connections3005001000

work_mem 是每个排序/哈希节点可用的上限,不是每个连接。连接数越多、并发排序越多,实际占用越接近“连接数 × 节点数 × work_mem”。上线后看 log_temp_files 和 pg_stat_database.temp_bytes,有临时文件再调大,不要一开始就拍一个很大的值。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: initdb Linux运维 pg_ctl PG18 PostgreSQL 参数调优 数据库部署 源码编译
最后更新:2026-10-17

Aekor

这个人很懒,什么都没留下

点赞
< 上一篇

使用AI教程

  • API报错解决方案
  • API 基础知识
  • API Key 获取
  • 最新接入教程

分类

  • Blog
  • Oracle 数据库
  • TradingAgents-CN
  • 使用教程
  • 数据库

COPYRIGHT © 2026 Aekor. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang