Skip to content

初始化数据库集群(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)。

安装与配置 ​

基本用法 ​

bash
# 最简形式:以 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枚举libclibc / icu / builtin;18 推荐 icu
--icu-locale=LOCALE字符串无使用 ICU 时的语言标识,如 zh-Hans-CN
-U, --username=NAME字符串当前系统用户名初始超级用户名字
-W, --pwpromptflag关闭交互式设置超级用户密码
--pwfile=FILE路径无从文件读取密码,适合自动化
-A, --auth=METHOD枚举trust同时设置本地与主机的认证方式
--auth-local / --auth-host枚举同 -A分别设置 Unix Socket 与 TCP 的认证方式
-k, --data-checksumsflag关闭开启页校验和,可发现磁盘静默损坏
-N, --no-syncflag关闭跳过 fsync 直接返回,仅测试用,掉电即损坏
--wal-segsize=SIZE整数(MB)16WAL 段大小,仅初始化时可改,之后不可变
--no-localeflag——等价于 --locale=C,排序最快但不支持本地化
-X, --waldir=DIR路径$PGDATA/pg_wal把 WAL 单独放到其他磁盘
--allow-group-accessflag关闭允许同组用户读取数据目录
-T, --text-search-config字符串simple默认全文检索配置
-L, --pgdata(目录)路径编译期 share 目录指定 postgres.bki 等模板文件位置

认证方式不要用 trust

-A trust 意味着任何能连上端口的客户端都无需密码即为超级用户。若数据库监听在公网地址,等同于把数据库完全开放。生产环境必须使用 scram-sha-256。

初始化过程输出 ​

text
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 是根据机器自动推导的。这些初始值决定了后续调优的起点。

详细说明 ​

数据目录布局 ​

初始化完成后,数据目录会呈现如下结构:

text
/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 会创建三个数据库,理解它们的差异对日常运维很重要:

sql
-- 查看内置数据库
SELECT datname, datistemplate, datallowconn, encoding
FROM pg_database
ORDER BY oid;
text
 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 只是全部参数的一个极小样本,其余参数都使用内置默认值:

ini
# 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'
ini
# 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 下的脚本。

text
容器首次启动
  └─ 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 OperatorOperator 自动执行CRD 中的参数

容器中 PGDATA 的路径变化

PostgreSQL 18 官方镜像把 PGDATA 的父目录调整为 /var/lib/postgresql,并把 PGDATA 指向带版本号的子目录。因此挂卷时应挂 /var/lib/postgresql,这与旧版教程中挂 /var/lib/postgresql/data 的写法不同。

实战示例 ​

场景一:为一台新服务器初始化生产集群 ​

bash
# ---------- 步骤 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.conf
text
18
-rw------- 1 postgres postgres 26934 Nov 12 10:22 /var/lib/pgsql/18/data/postgresql.conf

场景二:用脚本做可复现的初始化 ​

bash
#!/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."
text
cluster initialized.

# 再执行一次,验证幂等性
cluster already initialized at /var/lib/pgsql/18/data, skip.

场景三:验证初始化结果 ​

bash
# 启动服务后逐个验证关键配置是否与预期一致
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;"
text
 server_encoding
-----------------
 UTF8
(1 row)

 lc_collate
------------
 C.UTF-8
(1 row)

 data_checksums
----------------
 on
(1 row)

 wal_segment_size
------------------
 64MB
(1 row)
sql
-- 确认模板库状态与默认模板
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 启停服务

参考链接 ​