初始化数据库集群(initdb)
无论用哪种方式安装 PostgreSQL,第一次启动之前都必须先「初始化数据库集群」。这台机器上的数据目录就是由 initdb 从零创建的。本节详解 initdb 的作用、常用参数、生成的文件布局,以及它与容器化部署之间的关系。
前置知识
阅读本节前,建议先了解:源码编译安装
基础概念
- 初始化(Initialization):
initdb在指定目录中创建 PostgreSQL 运行所需的全部基础文件,包括系统目录表、模板数据库、配置文件与 WAL - 数据库集群(Database Cluster):一个数据目录 + 一套配置,是一台机器上可独立启动的最小运行单元。「集群」一词容易误解,它与多机高可用集群无关
- 数据目录(Data Directory):
PGDATA指向的目录,初始化后包含base/、global/、pg_wal/等子目录。也叫「集簇目录」 - 系统目录(System Catalog):以
pg_开头的元数据表,记录表结构、权限、函数等,由initdb预先写入 - 模板数据库(Template Database):
template0与template1。CREATE DATABASE默认复制template1 - 本地化(Locale):决定字符分类与排序规则;
--locale一旦初始化就无法轻易修改,需要重建集群
| 概念 | 说明 | 是否可后期修改 |
|---|---|---|
| 数据目录路径(-D) | 集群所在位置 | 可以移动,但要同步改配置与脚本 |
| 编码(-E) | 存储字符集,通常 UTF8 | 不可改,只能重建 |
| Locale | 排序规则、大小写规则等 | 不可改,只能重建 |
| 超级用户名(-U) | 初始超级用户 | 可改名,但风险高 |
| 认证方式(--auth) | pg_hba.conf 的默认策略 | 可修改,改完 reload 即可 |
| 数据校验和(--data-checksums) | 页级校验,提前发现静默损坏 | 不可关闭(开启后建议保持) |
版本说明
PostgreSQL 11 起,initdb 默认会创建 pg_wal/(而不是旧的 pg_xlog/),并在初始化时启用 WAL 归档相关目录结构。18 中 initdb 还默认使用更安全的认证方式与更强的密码加密(scram-sha-256)。
安装与配置
基本用法
# 最简形式:以 postgres 用户执行,指定数据目录
sudo -u postgres /usr/lib/postgresql/18/bin/initdb -D /var/lib/postgresql/18/data
# 推荐的生产级初始化:明确编码、locale、校验和与认证方式
sudo -u postgres initdb \
-D /var/lib/postgresql/18/data \
-E UTF8 \
--locale=C.UTF-8 \
-U postgres \
--auth-local=peer \
--auth-host=scram-sha-256 \
--data-checksums不要用 root 运行 initdb
initdb 出于安全考虑会拒绝以 root 身份运行,因为服务进程本身也不允许以 root 启动。请使用 sudo -u postgres 或专门创建的运行用户。
参数详解
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-D, --pgdata=DIR | 路径 | 读取 $PGDATA | 数据目录;目录必须为空或不存在 |
-E, --encoding=ENC | 字符串 | 由 locale 推导 | 数据库默认字符集,中文环境推荐 UTF8 |
--locale=LOCALE | 字符串 | 继承系统环境 | 同时设置所有 locale 分类 |
--lc-collate / --lc-ctype | 字符串 | 同 --locale | 分别设置排序规则与字符分类 |
--locale-provider | 枚举 | libc | libc / icu / builtin;18 推荐 icu |
--icu-locale=LOCALE | 字符串 | 无 | 使用 ICU 时的语言标识,如 zh-Hans-CN |
-U, --username=NAME | 字符串 | 当前系统用户名 | 初始超级用户名字 |
-W, --pwprompt | flag | 关闭 | 交互式设置超级用户密码 |
--pwfile=FILE | 路径 | 无 | 从文件读取密码,适合自动化 |
-A, --auth=METHOD | 枚举 | trust | 同时设置本地与主机的认证方式 |
--auth-local / --auth-host | 枚举 | 同 -A | 分别设置 Unix Socket 与 TCP 的认证方式 |
-k, --data-checksums | flag | 关闭 | 开启页校验和,可发现磁盘静默损坏 |
-N, --no-sync | flag | 关闭 | 跳过 fsync 直接返回,仅测试用,掉电即损坏 |
--wal-segsize=SIZE | 整数(MB) | 16 | WAL 段大小,仅初始化时可改,之后不可变 |
--no-locale | flag | —— | 等价于 --locale=C,排序最快但不支持本地化 |
-X, --waldir=DIR | 路径 | $PGDATA/pg_wal | 把 WAL 单独放到其他磁盘 |
--allow-group-access | flag | 关闭 | 允许同组用户读取数据目录 |
-T, --text-search-config | 字符串 | simple | 默认全文检索配置 |
-L, --pgdata(目录) | 路径 | 编译期 share 目录 | 指定 postgres.bki 等模板文件位置 |
认证方式不要用 trust
-A trust 意味着任何能连上端口的客户端都无需密码即为超级用户。若数据库监听在公网地址,等同于把数据库完全开放。生产环境必须使用 scram-sha-256。
初始化过程输出
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locale "C.UTF-8".
The default text search configuration will be set to "english".
Data page checksums are enabled.
fixing permissions on existing directory /var/lib/postgresql/18/data ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... Asia/Shanghai
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or
Success. You can now start the database server using:
pg_ctl -D /var/lib/postgresql/18/data -l logfile start注意其中几行关键信息:locale、编码、校验和状态、默认时区、max_connections 与 shared_buffers 是根据机器自动推导的。这些初始值决定了后续调优的起点。
详细说明
数据目录布局
初始化完成后,数据目录会呈现如下结构:
/var/lib/postgresql/18/data/
├── base/ # 各数据库的数据文件,子目录名即数据库 OID
│ ├── 1/ # template1
│ ├── 4/ # template0
│ └── 5/ # postgres
├── global/ # 跨库共享的系统表(如 pg_database、pg_authid)
├── pg_wal/ # 预写日志(WAL),崩溃恢复的核心
│ └── archive_status/ # 归档状态标记
├── pg_xact/ # 事务提交状态(原 pg_clog)
├── pg_multixact/ # 多事务状态
├── pg_subtrans/ # 子事务状态
├── pg_tblspc/ # 表空间软链接
├── pg_twophase/ # 两阶段提交状态文件
├── pg_stat/ # 统计信息持久化文件
├── pg_stat_tmp/ # 统计信息临时文件
├── pg_replslot/ # 逻辑复制槽
├── pg_dynshmem/ # 动态共享内存
├── pg_notify/ # LISTEN/NOTIFY 队列
├── pg_serial/ # 可串行化快照
├── pg_snapshots/ # 导出快照
├── pg_commit_ts/ # 提交时间戳(track_commit_timestamp 开启时)
├── postgresql.conf # 主配置文件
├── postgresql.auto.conf # ALTER SYSTEM 写入,优先级高于 postgresql.conf
├── pg_hba.conf # 客户端认证规则
├── pg_ident.conf # 系统用户到数据库用户的映射
├── pg_ctl.conf # 供 pg_ctl 使用的参数(可选)
├── PG_VERSION # 大版本号,仅一行文本
└── postmaster.pid # 运行时才存在,记录 PID 与共享内存地址| 文件 / 目录 | 类型 | 说明 | 能否手工编辑 |
|---|---|---|---|
PG_VERSION | 文件 | 记录大版本号,如 18 | 不可 |
postgresql.conf | 配置文件 | 主配置 | 可以 |
postgresql.auto.conf | 配置文件 | ALTER SYSTEM 的目标,勿手工改 | 不建议 |
pg_hba.conf | 配置文件 | 认证规则 | 可以,改后 reload |
pg_ident.conf | 配置文件 | 用户映射 | 可以,改后 reload |
base/ global/ | 目录 | 真实数据,绝不能手工删除 | 绝不可 |
pg_wal/ | 目录 | WAL,决定能否崩溃恢复 | 绝不可 |
postmaster.pid | 文件 | 运行期锁文件 | 服务停止时自动删除 |
三个内置数据库
initdb 会创建三个数据库,理解它们的差异对日常运维很重要:
-- 查看内置数据库
SELECT datname, datistemplate, datallowconn, encoding
FROM pg_database
ORDER BY oid; datname | datistemplate | datallowconn | encoding
----------+---------------+--------------+----------
template1| t | t | UTF8
template0| t | f | UTF8
postgres | f | t | UTF8
(3 rows)template1:默认模板。所有CREATE DATABASE(未指定模板时)都复制它,因此在这里装的扩展会「遗传」给新库template0:纯净模板,datallowconn = false,禁止连接以保持内容不变。用于恢复、指定不同 locale 建库postgres:面向连接的默认库,供psql等工具无参连接
为什么不能随便往 template1 装东西
在 template1 中创建对象(如 CREATE EXTENSION pg_stat_statements),会让之后每一个新建数据库都自动带上它。这在统一规范时很方便,但一旦装错扩展,排查成本很高。更稳妥的做法是显式指定模板:CREATE DATABASE app TEMPLATE template1;(默认行为)或 TEMPLATE template0(纯净)。
配置文件的初始内容
initdb 生成的 postgresql.conf 只是全部参数的一个极小样本,其余参数都使用内置默认值:
# postgresql.conf(initdb 生成的初始版本,节选并加了注释)
# 该文件由 initdb 生成,几乎所有行都被注释掉,注释值即为默认值
#listen_addresses = 'localhost' # 默认只监听本机回环
#port = 5432
#max_connections = 100
#shared_buffers = 128MB # 实际值为 shared_buffers 推导结果
#dynamic_shared_memory_type = posix
#log_timezone = 'Asia/Shanghai'
#datestyle = 'iso, mdy'
#timezone = 'Asia/Shanghai'
#lc_messages = 'C.UTF-8'
#lc_monetary = 'C.UTF-8'
#default_text_search_config = 'pg_catalog.english'# pg_hba.conf(initdb 生成的初始版本,节选)
# TYPE DATABASE USER ADDRESS METHOD
# 本地 Unix Socket 连接
local all all peer
# IPv4 回环
host all all 127.0.0.1/32 scram-sha-256
# IPv6 回环
host all all ::1/128 scram-sha-256注意 --auth-local=peer 的效果:本地连接沿用操作系统用户名做认证,因此 sudo -u postgres psql 可以直接登录。
initdb 与容器化的关系
使用 Docker 官方镜像时,initdb 不需要手动执行。镜像的入口点脚本会判断数据目录是否为空,为空则自动调用 initdb,并执行 /docker-entrypoint-initdb.d 下的脚本。
容器首次启动
└─ docker-entrypoint.sh 检查 $PGDATA
├─ 目录为空 → 调用 initdb(使用 POSTGRES_INITDB_ARGS)
│ ↓
│ 创建 POSTGRES_USER / POSTGRES_DB
│ ↓
│ 执行 /docker-entrypoint-initdb.d/*.sh|*.sql
│ ↓
│ 调整 pg_hba.conf 为 POSTGRES_HOST_AUTH_METHOD
└─ 目录非空 → 跳过初始化,直接启动| 场景 | 谁来执行 initdb | 传参方式 |
|---|---|---|
| 包管理器安装(Debian) | 安装包自动调用 | 由 postgresql-common 生成 |
| 包管理器安装(RPM) | /usr/pgsql-18/bin/postgresql-18-setup initdb | 脚本内部固定 |
| 源码编译安装 | 手工执行 | 命令行参数 |
| Docker 官方镜像 | 入口点脚本自动执行 | POSTGRES_INITDB_ARGS 环境变量 |
| Kubernetes Operator | Operator 自动执行 | CRD 中的参数 |
容器中 PGDATA 的路径变化
PostgreSQL 18 官方镜像把 PGDATA 的父目录调整为 /var/lib/postgresql,并把 PGDATA 指向带版本号的子目录。因此挂卷时应挂 /var/lib/postgresql,这与旧版教程中挂 /var/lib/postgresql/data 的写法不同。
实战示例
场景一:为一台新服务器初始化生产集群
# ---------- 步骤 1:准备目录与权限 ----------
sudo mkdir -p /var/lib/pgsql/18/data /var/lib/pgsql/18/wal /var/log/pgsql
sudo chown -R postgres:postgres /var/lib/pgsql /var/log/pgsql
# 数据目录必须收紧权限,否则 initdb 会拒绝
sudo chmod 700 /var/lib/pgsql/18/data
# ---------- 步骤 2:初始化。WAL 单独放到独立磁盘以提升可靠性 ----------
sudo -u postgres /usr/local/pgsql-18/bin/initdb \
-D /var/lib/pgsql/18/data \
-E UTF8 \
--locale-provider=icu --icu-locale=zh-Hans-CN --locale=C.UTF-8 \
-U postgres \
--pwfile=/tmp/pgpass.txt \
--auth-local=peer \
--auth-host=scram-sha-256 \
--data-checksums \
--wal-segsize=64 \
-X /var/lib/pgsql/18/wal
# 用完立即销毁临时密码文件,避免明文残留
shred -u /tmp/pgpass.txt
# ---------- 步骤 3:确认结果 ----------
sudo -u postgres cat /var/lib/pgsql/18/data/PG_VERSION
sudo -u postgres ls -l /var/lib/pgsql/18/data/postgresql.conf18
-rw------- 1 postgres postgres 26934 Nov 12 10:22 /var/lib/pgsql/18/data/postgresql.conf场景二:用脚本做可复现的初始化
#!/usr/bin/env bash
# init-cluster.sh:幂等初始化脚本,重复执行不会破坏已有数据
set -euo pipefail
PGBIN=/usr/lib/postgresql/18/bin
PGDATA=/var/lib/pgsql/18/data
# 目录已存在 PG_VERSION 说明已经初始化过,直接跳过
if [ -s "${PGDATA}/PG_VERSION" ]; then
echo "cluster already initialized at ${PGDATA}, skip."
exit 0
fi
# 数据目录必须为空,否则 initdb 会报错退出
if [ -d "${PGDATA}" ] && [ -n "$(ls -A "${PGDATA}")" ]; then
echo "ERROR: ${PGDATA} is not empty" >&2
exit 1
fi
install -d -m 700 -o postgres -g postgres "${PGDATA}"
sudo -u postgres "${PGBIN}/initdb" \
-D "${PGDATA}" \
-E UTF8 \
--locale=C.UTF-8 \
-U postgres \
--auth-host=scram-sha-256 \
--data-checksums
echo "cluster initialized."cluster initialized.
# 再执行一次,验证幂等性
cluster already initialized at /var/lib/pgsql/18/data, skip.场景三:验证初始化结果
# 启动服务后逐个验证关键配置是否与预期一致
sudo -u postgres /usr/local/pgsql-18/bin/pg_ctl -D /var/lib/pgsql/18/data \
-l /var/log/pgsql/pg18.log start
sudo -u postgres psql -c "SHOW server_encoding;" -c "SHOW lc_collate;" \
-c "SHOW data_checksums;" -c "SHOW wal_segment_size;" server_encoding
-----------------
UTF8
(1 row)
lc_collate
------------
C.UTF-8
(1 row)
data_checksums
----------------
on
(1 row)
wal_segment_size
------------------
64MB
(1 row)-- 确认模板库状态与默认模板
SELECT datname, datistemplate, datallowconn FROM pg_database ORDER BY oid;
-- 若确有统一规范需求,可把扩展装进 template1,让新库自动继承
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;注意事项
initdb只能作用于空目录,已有内容的目录会直接报错;这是防止误覆盖数据的保护机制- 编码与 locale 一旦初始化就固定,无法在不重建集群的前提下更改;生产环境选型务必一次到位
--no-sync只应用于一次性测试,跳过 fsync 后掉电可能造成数据目录永久损坏--wal-segsize只在初始化时生效,之后无法修改;大型写入场景可考虑 64MB 以减少归档文件数pg_wal单独放到-X指定目录后,该目录与数据目录一起构成集群,备份时必须同时覆盖postgresql.auto.conf由ALTER SYSTEM管理与生成,手工编辑会被下一次ALTER SYSTEM覆盖postmaster.pid残留会导致启动失败,服务非正常终止后需确认进程确实已退出再清理- 超级用户名字建议固定为
postgres,使用其他名字会让大量依赖该约定的脚本与教程直接失效 - Docker 环境下改
PGDATA必须同步修改卷挂载路径,否则数据会落在容器可写层里,重建即丢失 initdb不会创建业务数据库,只创建三个内置库;业务库应通过CREATE DATABASE或初始化脚本创建- 数据目录的父目录权限也要注意:Debian 系中
/var/lib/postgresql属主是postgres,权限通常为755
最佳实践
- 初始化参数写进可复现的脚本并纳入版本控制,禁止手工敲命令,保证每台机器完全一致
- 统一使用
UTF8编码;locale 优先选择C.UTF-8或 ICU provider,避免因 OS locale 差异导致排序不同 - 一律启用
--data-checksums,让磁盘静默损坏在读取时立刻暴露而非悄悄扩散 - 认证方式固定为本地
peer、远程scram-sha-256,禁止trust与已废弃的md5 - 生产环境用
--pwfile或外部密钥管理注入初始密码,避免密码出现在 shell 历史与进程列表 - 数据目录与 WAL 目录最好位于不同物理磁盘,兼顾写入性能与故障隔离
- 初始化完成后立即备份一份「空集群基线」,为后续对比与快速回滚提供参照
- 业务对象不要放进
template1,除非团队已明确约定并写入文档 - 把
initdb的完整输出与SHOW ALL结果归档,作为集群的初始状态快照 - 容器环境下只用
POSTGRES_INITDB_ARGS定制参数,不手工在容器里执行initdb
下一节
继续学习:pg_ctl 启停服务