跳转至

安装PostgreSQL

一. PostgreSQL简介

1. PostgreSQL的来源

PostgreSQL是以加州大学伯克利分校计算机系开发的POSTGRES,版本 4.2为基础的对象关系型数据库管理系统(ORDBMS)。POSTGRES领先的许多概念在很久以后才出现在一些商业数据库系统中。PostgreSQL是这个最初Berkeley代码的开源衍生版本。

2. 什么是PostgreSQL

  • PostgreSQL是一个强大的开源对象关系数据库系统,结合了SQL语言多种功能,安全存储和拓展最复杂的数据工作负载。
  • PostgreSQL引起经过验证的架构、可靠性、数据完整性、强大的功能集、可拓展性一级开源社区对软件始终提供高效且创新解决方案的奉献而赢得了良好声誉。PostgreSQL运行于所有主流操作系统,自2001年依赖一直符合ACID标准。并且拥有强大的插件,如流行的PostGIS地理空间数据库拓展器。

3. 为什么要使用PostgreSQL

PostgreSQL包含许多功能,帮助开发者构建应用程序,帮助管理员保护数据完整性并构建容错环境,并帮助你管理数据,无论数据大小。除了免费且开源外,PostgreSQL还具有高度可拓展性——例如可以自定义数据类型、构建自定义函数,甚至用不同的编程语言编写代码而无需重新编译数据库!

PostgreSQL 遵循 SQL 标准,截至 2025 年 9 月发布的 Version 18,已符合 SQL:2023 Core 标准的 170/177 项强制性功能,目前没有其他关系型数据库达到完全符合该标准。

4. PostgreSQL 的核心特性

数据类型

  • 基本类型:整数、数值、字符串、布尔
  • 结构化类型:日期/时间、数组、范围/多范围、UUID
  • 文档类型:JSON/JSONB、XML、键值对(Hstore)
  • 几何类型:点、线、圆、多边形
  • 自定义类型:复合类型、自定义类型

数据完整性

  • UNIQUE、NOT NULL 约束
  • 主键、外键
  • 排除约束
  • 显式锁、建议锁

并发与性能

  • 索引:B-tree、多列、表达式、部分索引
  • 高级索引:GiST、SP-GiST、KNN GiST、GIN、BRIN、覆盖索引、Bloom 过滤器
  • sophisticated 查询规划器/优化器、仅索引扫描、多列统计信息
  • 事务、嵌套事务(通过保存点)
  • 多版本并发控制(MVCC)
  • 读查询和构建 B-tree 索引的并行化
  • 表分区
  • SQL 标准定义的所有事务隔离级别(包括 Serializable)
  • 表达式的即时编译(JIT)
  • 异步 I/O(AIO)

可靠性与灾难恢复

  • 预写日志(WAL)
  • 复制:异步、同步、逻辑复制
  • 时间点恢复(PITR)、热备库
  • 表空间

安全性

  • 认证方式:GSSAPI、SSPI、LDAP、SCRAM-SHA-256、证书、OAuth 2.0 等
  • 强大的访问控制系统
  • 行级安全、列级安全
  • 证书 + 附加方法的多因素认证

可扩展性

  • 存储函数和过程
  • 过程语言:PL/pgSQL、Perl、Python、Tcl(通过扩展还支持 Java、JavaScript、R、Lua、Rust 等)
  • SQL/JSON 构造函数、查询函数、路径表达式、JSON_TABLE
  • 外部数据包装器(FDW):通过标准 SQL 接口连接其他数据库或数据流
  • 可自定义的存储接口
  • 丰富的扩展生态,包括 PostGIS(地理空间数据库扩展)

国际化与全文搜索

  • 支持国际化字符集(如 ICU 排序规则)
  • 大小写不敏感和重音不敏感的排序规则
  • 全文搜索

5. PostgreSQL 的数据规模

PostgreSQL 已被证明在管理数据量和并发用户数方面具有高度可扩展性。生产环境中存在管理数 TB 数据的活跃 PostgreSQL 集群,以及管理 PB 级数据的专用系统。

6. PostgreSQL 社区数据

指标 数据
开发历史 35+ 年(始于 1986 年 UC Berkeley POSTGRES 项目)
贡献者 725+ 人
提交次数 61,000+
代码量 1,780,000+ 行 C 代码
本地用户组 70+
活动/会议 720+
用户 数百万
许可证 免费且开源

二. PostgreSQL安装

0. 访问官网下载界面

alt text alt text alt text

1. 复制、粘贴并运行设置脚本中的相关部分

[root@3 ~]# sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf install -y postgresql18-server
#安装pgsql
sudo /usr/pgsql-18/bin/postgresql-18-setup initdb
#初始化数据库
sudo systemctl enable postgresql-18
#设置开机自启动
sudo systemctl start postgresql-18

2. 检查状态

[root@3 ~]# sudo -u postgres psql -c "SELECT version();"
                                                  version                                                  
-----------------------------------------------------------------------------------------------------------
 PostgreSQL 18.4 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-14), 64-bit
(1 行记录)
[root@3 ~]# systemctl status postgresql-18
● postgresql-18.service - PostgreSQL 18 database server
     Loaded: loaded (/usr/lib/systemd/system/postgresql-18.service; enabled; preset: disabled)
     Active: active (running) since Wed 2026-07-08 09:25:40 CST; 13min ago
       Docs: https://www.postgresql.org/docs/18/static/
    Process: 2729 ExecStartPre=/usr/pgsql-18/bin/postgresql-18-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
   Main PID: 2734 (postgres)
      Tasks: 10 (limit: 10841)
     Memory: 26.3M
        CPU: 181ms
     CGroup: /system.slice/postgresql-18.service
             ├─2734 /usr/pgsql-18/bin/postgres -D /var/lib/pgsql/18/data/
             ├─2735 "postgres: logger "
             ├─2736 "postgres: io worker 0"
             ├─2737 "postgres: io worker 2"
             ├─2738 "postgres: io worker 1"
             ├─2739 "postgres: checkpointer "
             ├─2740 "postgres: background writer "
             ├─2742 "postgres: walwriter "
             ├─2743 "postgres: autovacuum launcher "
             └─2744 "postgres: logical replication launcher "

7月 08 09:25:40 3 systemd[1]: Starting PostgreSQL 18 database server...
7月 08 09:25:40 3 postgres[2734]: 2026-07-08 09:25:40.940 CST [2734] 日志:  日志输出重定向到日志收集进程
7月 08 09:25:40 3 postgres[2734]: 2026-07-08 09:25:40.940 CST [2734] 提示:  后续的日志输出将出现在目录 "log"中.
7月 08 09:25:40 3 systemd[1]: Started PostgreSQL 18 database server.

3. PostgreSQL进程详解

[root@3 ~]# ps -ef --forest | grep postgres
root        3205    2076  0 09:41 pts/0    00:00:00              \_ grep --color=auto postgres
postgres    2734       1  0 09:25 ?        00:00:00 /usr/pgsql-18/bin/postgres -D /var/lib/pgsql/18/data/
postgres    2735    2734  0 09:25 ?        00:00:00  \_ postgres: logger 
postgres    2736    2734  0 09:25 ?        00:00:00  \_ postgres: io worker 0
postgres    2737    2734  0 09:25 ?        00:00:00  \_ postgres: io worker 2
postgres    2738    2734  0 09:25 ?        00:00:00  \_ postgres: io worker 1
postgres    2739    2734  0 09:25 ?        00:00:00  \_ postgres: checkpointer 
postgres    2740    2734  0 09:25 ?        00:00:00  \_ postgres: background writer 
postgres    2742    2734  0 09:25 ?        00:00:00  \_ postgres: walwriter 
postgres    2743    2734  0 09:25 ?        00:00:00  \_ postgres: autovacuum launcher 
postgres    2744    2734  0 09:25 ?        00:00:00  \_ postgres: logical replication launcher 

与nginx类似,postgres也是由主进程+工作进程+其他进程构成

postgres(2374)

主进程,管理所有子进程,类似于nginx的master process

postgres: io worker(2736,2737,2738)

工作进程,三个进程异步进行I/O处理,类似于nginx的worker process

postgres: logger(2735)

日志收集进程,统一写入日志文件

postgres: checkpointer(2739)

定期将脏页写入数据文件,创建检查点

postgres: background writer(2740)

后台把共享缓冲区脏页写入磁盘

postgres: walwriter(2742)

把WAL日志从缓冲区刷到磁盘

postgres: autovacuum launcher(2743)

自动回收过期的数据行,防止表膨胀

postgres: logical replication launcher(2744)

逻辑复制相关

补充:脏页(Dirty Pages)

脏页是内存中被修改但尚未写回磁盘的数据页。流程如下:

读数据:磁盘 → 内存缓冲区(s干净页)
改数据:在内存中修改         → 变成脏页
刷盘  :checkpointer / background writer 把脏页写回磁盘 → 变回干净页
进程 与脏页的关系
background writer 持续少量地把脏页刷回磁盘,保证缓冲区始终有空位
checkpointer 定期集中一次性刷所有脏页,创建检查点(checkpoint),标记 WAL日志可以回收

如果脏页太多来不及刷盘就宕机,重启后 PostgreSQL 需要从 WAL 重放来恢复数据,启动时间会变长。

脏页就像一张草稿纸,数据库会将接收且处理完毕但还未写入的请求用铅笔写在上面,等到有时间处理这些写入请求后,数据库会将脏页的内容一点点写入到数据库中,然后擦掉脏页对应数据,使其变成干净页。但是脏页不在数据库这个本子而在内存上,如果没来及写入到本子上电脑就断电会导致脏页上的数据丢失,这时候就需要其他日志来保底进行恢复了。

  • [x] 有关脏页和WAL日志的内容等到日志篇再细说

注意:以上进程列表仅包含 PostgreSQL 的后台进程,不包含客户端连接进程。

4. PostgreSQL 的连接模型

与 MySQL 使用线程处理客户端连接不同,PostgreSQL 采用进程模型——每来一个客户端连接,主进程就 fork 一个独立的后端进程(postgres: connected backend)。

当客户端连接后,用 ps 会看到多出类似这样的进程:

postgres    2800    2734  0 09:30 ?        00:00:00  \_ postgres: postgres testdb 127.0.0.1(5432) idle

进程模型的优缺点:

说明
✅ 稳定性 一个连接崩溃不会影响其他连接
✅ 隔离性 每个进程独立内存空间
❌ 资源消耗 1000 个连接 = 1000 个进程,内存压力大
❌ 性能 高并发下频繁 fork,上下文切换开销大

正因为这个原因,PostgreSQL 在高并发场景下需要引入连接池来复用连接。最常用的就是 PgBouncer。

5. PgBouncer——连接池解决方案

PgBouncer 是一个轻量级的 PostgreSQL 连接池中间件,原理是在客户端和 PostgreSQL 之间维护一批固定数量的后端连接:

客户端A ─┐
客户端B ─┼──→ PgBouncer ──→ PostgreSQL(只需少量进程)
客户端C ─┘
特性 说明
模式 session / transaction / statement 三种池化模式
资源节省 几百个客户端连接映射到几十个数据库连接
配置简单 单独的配置文件,不侵入 PostgreSQL
安装 dnf install pgbouncer

与nginx原理相同,PostgreSQL也支持systemctl reload postgresql-18

此时主进程收到 SIGHUP 信号后,会重新加载配置文件(postgresql.conf 等),不中断正在处理的连接,和 Nginx 的 nginx -s reload 原理相同。

  • [x] 有关PgBouncer的安装配置部分后续文档再提

3. Nginx reload vs PostgreSQL reload 对比

Nginx reload 流程:

nginx -s reload
  → 主进程检查配置文件语法
  → 创建新的 worker 进程(加载新配置)
  → 向旧 worker 发信号:处理完当前请求后自己退出
  → 旧 worker 优雅退出,新 worker 接手

本质:旧进程死掉 → 新进程诞生,配置完全替换。

PostgreSQL reload 流程:

systemctl reload postgresql-18
  → 主进程重新读取 postgresql.conf
  → 子进程们收到 SIGHUP 信号
  → 子进程在自己的生命周期内应用新参数

本质:不创建新进程,子进程活着,参数热更新。

但要注意的是,不是所有参数都能 reload 生效:

参数 reload 生效? 说明
listen_addresses ❌ 必须 restart
port ❌ 必须 restart
shared_buffers ❌ 必须 restart
max_connections ❌ 必须 restart
wal_level ❌ 必须 restart
log_min_duration_statement ✅ 可热生效
work_mem ✅ 可热生效
log_directory ✅ 可热生效

要查某个参数能不能 reload,进 psql 里执行:

SELECT name, context FROM pg_settings WHERE name = 'shared_buffers';

context 列如果是 postmaster 就必须重启,是 sighup 就可以 reload。

三. Docker部署

docker run --name my-pg18-learn \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -d -v pgdata:/var/lib/postgresql \
  --restart=unless-stopped \
  postgres:18-alpine

# 绝对最小安装,只有密码是必须的

如果不带上参数会出错

参数意义:

  1. --name my-pg-learn # 指定容器名称(方便管理)
  2. -d # 后台运行
  3. -e POSTGRES_PASSWORD=mysecretpassword # -e 输入密码
  4. -v pgdata:/var/lib/postgresql/data # 数据卷挂载(数据持久化)或者直接指定挂载目录
  5. --restart=unless-stopped # 自动重启策略

进入数据库: docker exec -it my-pg18-learn psql -U postgres

错误

docker run --name my-pg18-learn \
-e POSTGRES_PASSWORD=mysecretpassword \
-d -v pgdata:/var/lib/postgresql/data \
-p 5432:5432 \
--restart=unless-stopped \
postgres:18-alpine
[root@5 docs]# docker exec -it my-pg18-learn psql -U postgres
Error response from daemon: Container e4406cc431680a3a666a8c0400dcee496d06559ff99d8119abe815382b5baef4 is restarting, wait untilthe container is running

解决:查看日志

 docker logs my-pg18-learn

 Error: in 18+, these Docker images are configured to store database data in a
       format which is compatible with "pg_ctlcluster" (specifically, using
       major-version-specific directory names).  This better reflects how
       PostgreSQL itself works, and how upgrades are to be performed.

       See also https://github.com/docker-library/postgres/pull/1259

       Counter to that, there appears to be PostgreSQL data in:
         /var/lib/postgresql/data (unused mount/volume)

       This is usually the result of upgrading the Docker image without
       upgrading the underlying database using "pg_upgrade" (which requires both
       versions).

       The suggested container configuration for 18+ is to place a single mount
       at /var/lib/postgresql which will then place PostgreSQL data in a
       subdirectory, allowing usage of "pg_upgrade --link" without mount point
       boundary issues.

       See https://github.com/docker-library/postgres/issues/37 for a (long)
       discussion around this process, and suggestions for how to do so.

PostgreSQL 18 的官方 Docker 镜像更改了数据目录的存储方式。

18版本以前的数据存储在容器的 /var/lib/postgresql/data 目录里;而18及以后的版本,数据实际存储在 /var/lib/postgresql/18/docker 这个子目录下

-v pgdata:/var/lib/postgresql/data 是旧版本的用法。当PostgreSQL 18启动时,它发现挂载的目录结构和预期不符,因此拒绝启动并报错。

a. 先停止并删除容器

docker stop my-pg18-learn
docker rm my-pg18-learn

b. 删除旧的、不兼容的数据卷

# 查看一下现有的卷
docker volume ls

# 删除旧的 pgdata 卷
docker volume rm pgdata

c. 使用正确命令

docker run --name my-pg18-learn \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -d -v pgdata:/var/lib/postgresql \
  --restart=unless-stopped \
  postgres:18-alpine

d. 验证并进入容器

# 检查容器状态,应该为 Up
docker ps

# 进入 PostgreSQL 命令行
docker exec -it my-pg18-learn psql -U postgres