PostgreSQL(简称 Postgres)是当前最先进的开源关系型数据库管理系统,始于 1986 年 UC Berkeley 的 POSTGRES 项目,拥有超过 38 年的持续演进历史。它完整支持 SQL:2016 标准,同时提供海量扩展能力:JSONB 文档存储、全文搜索、地理空间数据(PostGIS)、自定义类型/函数/运算符,以及用 C/Python/Perl/Rust 编写的存储过程。
一句话总结:PostgreSQL 让关系型数据库兼具 NoSQL 灵活性,同时保持 ACID 完整性和 SQL 生态的兼容性。
一、PostgreSQL 的核心特性
1.1 为什么选择 PostgreSQL?
| 特性 | 说明 | 优势 |
|---|---|---|
| ACID 完整支持 | 原子性/一致性/隔离性/持久性 | 金融级数据安全 |
| MVCC 并发控制 | 读写互不阻塞 | 高并发场景性能优异 |
| 扩展性极强 | 自定义类型、函数、索引、语言 | 几乎无限的可定制能力 |
| JSONB 文档存储 | 二进制 JSON + GIN 索引 | NoSQL 灵活 + SQL 强大 |
| 全文搜索 | 内置 tsvector/tsquery | 轻量场景无需 Elasticsearch |
| 地理空间 | PostGIS 扩展 | GIS 应用首选 |
| 表分区 | RANGE/HASH/LIST 分区 | 超大表性能优化 |
| 流复制 | 主从同步 + 热备 | 高可用架构 |
| 开源协议 | PostgreSQL License(类 MIT) | 完全免费,无商业限制 |
1.2 PostgreSQL 与其他数据库对比
| 维度 | PostgreSQL | MySQL | MongoDB |
|---|---|---|---|
| 数据模型 | 关系型 + JSONB | 关系型 | 文档型 |
| ACID | ✅ 完整(全特性) | ✅(InnoDB) | ✅(多文档事务) |
| 复杂查询 | ✅ 极强(CTE/窗口函数) | 中等 | 较弱 |
| JSON 支持 | JSONB(二进制+索引) | JSON 列(有限) | 原生文档 |
| 扩展性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| GIS 支持 | PostGIS(最强) | 有限 | 有限 |
| 表分区 | ✅ RANGE/HASH/LIST | ✅ HASH/RANGE(有限) | 分片 |
| 适用场景 | OLTP + OLAP + 混合负载 | 简单 Web 应用 | 文档型应用 |
1.3 PostgreSQL 版本策略
PostgreSQL 每年发布一个大版本,每个版本支持 5 年(最后两个小版本享受一个延长窗口)。生产环境建议始终使用最近的稳定版本(当前为 PostgreSQL 17):
- PostgreSQL 17(当前):LIKE/ILIKE 性能提升 2x、VACUUM 更快、JSON 功能增强
- PostgreSQL 16:更多 SQL/JSON 函数、并行聚合改进、逻辑复制增强
- PostgreSQL 15:压缩 WAL、排序性能提升、显式 MERGE 语句
升级路径:pg_upgrade(原地升级)或 pg_dumpall → 新版本恢复(更安全)。
二、核心架构
2.1 MVCC(多版本并发控制)
PostgreSQL 不使用传统的读写锁,而是通过保存数据行的多个版本来实现并发:
事务 A(Started at T1)读取 ID=1 的行(版本 v1)
↓
事务 B(Started at T2)更新 ID=1 的行 → 创建新版本 v2
↓
事务 A 继续读取 → 仍然看到 v1(事务开始时的一致视图)
↓
事务 B 提交 → v1 被标记为 dead tuple,等待 VACUUM 回收
↓
新事务 C(Started at T3)读取 → 看到 v2
效果:
- 读操作永远不阻塞写操作
- 写操作也不阻塞读操作
- 不存在行级锁冲突导致的死锁(针对纯读)
副作用:MVCC 产生 dead tuple(死元组),需要通过 VACUUM 后台进程回收,否则表会膨胀(bloat)。
2.2 WAL(预写日志,Write-Ahead Log)
所有数据修改先写入 WAL(磁盘顺序写,极快),再标记为完成:
1. BEGIN 事务
2. 修改 Buffer Pool 中的数据页(内存)
3. 将 WAL 记录追加到 WAL Buffer
4. WAL Buffer 强制刷盘(fsync)
5. 返回客户端成功(COMMIT)
6. 后台异步将脏页刷入数据文件
配置权衡:synchronous_commit = on(默认,数据最安全) vs off(3x 写入提升,最多丢失最后一个事务)。
2.3 索引类型与选择策略
| 索引类型 | 适用场景 | 说明 | 版本 |
|---|---|---|---|
| B-tree(默认) | 等值、范围查询 | 最通用 | 全版本 |
| Hash | 等值查询 | 比 B-tree 小,但不支持范围 | 全版本 |
| GiST | 地理空间、范围类型、相似度搜索 | 通用搜索树 | 全版本 |
| SP-GiST | 不平衡数据结构 | 四叉树等空间分区 | 全版本 |
| GIN | 数组、JSONB、全文搜索 | 倒排索引 | 全版本 |
| BRIN | 超大有序表(时序数据) | 块范围索引,极小体积 | 全版本 |
-- B-tree(默认)
CREATE INDEX idx_email ON users(email);
-- GIN 索引(JSONB 查询加速)
CREATE INDEX idx_data ON orders USING GIN(data jsonb_path_ops);
-- BRIN(时序大数据)
CREATE INDEX idx_time ON logs USING BRIN(created_at) WITH (pages_per_range = 128);
索引选择口诀:等值首选 B-tree,JSONB 用 GIN,时序数据用 BRIN,地理位置必用 GiST。
三、JSONB:关系型 + 文档型的融合
PostgreSQL 的 JSONB 类型允许在关系表中存储和操作半结构化数据:
-- 创建含 JSONB 列的表
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
price DECIMAL(10,2),
attributes JSONB
);
-- 插入数据(数组、嵌套对象)
INSERT INTO products (name, price, attributes) VALUES
('MacBook Pro', 1999.00,
'{"color": "silver", "weight": 1.4, "tags": ["laptop", "apple"], "spec": {"ram": "16GB", "cpu": "M3"}}'),
('iPad Air', 599.00,
'{"color": "blue", "tags": ["tablet", "apple"], "spec": {"ram": "8GB", "gpu": true}}');
-- JSONB 包含查询(需 GIN 索引加速)
SELECT * FROM products WHERE attributes @> '{"color": "silver"}';
-- JSONB 路径取值(->> 返回文本,-> 返回 JSONB)
SELECT name, attributes->>'color' as color,
attributes->'spec'->>'ram' as ram
FROM products;
-- JSONB 数组包含(包含 "laptop" 标签)
SELECT * FROM products WHERE attributes->'tags' @> '["laptop"]';
-- JSONB 创建 GIN 索引后的高效查询
CREATE INDEX idx_products_attrs ON products USING GIN(attributes jsonb_path_ops);
JSONB 的三大优势:
- 二进制存储 — 比文本 JSON 更快、更紧凑
- 支持索引 — GIN 索引加速复杂 JSONB 条件查询
- 事务安全 — 与关系数据在同一事务中,ACID 完整
JSONB 不能完全替代 MongoDB:纯文档型应用(大量非结构化数据、Schema 频繁变更)仍首选 MongoDB 或专用文档数据库。
四、全文搜索(Full Text Search)
PostgreSQL 内置全文搜索,轻量场景无需 Elasticsearch:
-- 为文章表设置全文搜索
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
body TEXT,
search_vector tsvector GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'B')
) STORED
);
CREATE INDEX idx_articles_search ON articles USING GIN(search_vector);
-- 搜索标题权重最高
SELECT id, title, ts_rank(search_vector, query) AS rank
FROM articles, plainto_tsquery('english', 'postgre') query
WHERE search_vector @@ query
ORDER BY rank DESC;
-- 中文全文本搜索需要额外安装 zhparser
-- 或使用 pg_search(较新的扩展)
五、表分区(Partitioning)
当单表超过 1 亿行 或占用 数百 GB 时,应考虑分区:
-- RANGE 分区示例(按日期按月分区)
CREATE TABLE logs (
id SERIAL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
level TEXT,
message TEXT
) PARTITION BY RANGE (created_at);
-- 创建具体分区
CREATE TABLE logs_2025_01 PARTITION OF logs
FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');
CREATE TABLE logs_2025_02 PARTITION OF logs
FOR VALUES FROM ('2025-02-01') TO ('2025-03-01');
CREATE TABLE logs_default PARTITION OF logs DEFAULT;
-- 插入时自动路由到对应分区
INSERT INTO logs(level, message) VALUES ('INFO', 'Application started');
-- 查询单个分区(秒级 → 毫秒级)
SET constraint_exclusion = on; -- 自动生效
SELECT * FROM logs WHERE created_at >= '2025-01-01' AND created_at < '2025-02-01';
分区类型选择:
| 分区类型 | 适用场景 | 示例 |
|---|---|---|
| RANGE | 时间序列 | 日志/事件按日/月/年 |
| HASH | 均匀分布 | 用户表按 ID 散列 |
| LIST | 离散分类 | 订单按地区/状态 |
| 分层分区 | 超大表 | 先 RANGE 再 HASH |
六、事务与隔离级别
PostgreSQL 支持 SQL 标准的四种隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | PostgreSQL 实现 |
|---|---|---|---|---|
| READ UNCOMMITTED | 不允许 | 可能有 | 可能有 | 实际以 READ COMMITTED 处理 |
| READ COMMITTED(默认) | 不允许 | 可能 | 可能 | MVCC + 资源清理 |
| REPEATABLE READ | 不允许 | 不允许 | 可能 | MVCC + 快照隔离 |
| SERIALIZABLE | 不允许 | 不允许 | 不允许 | 序列快照 + 冲突检测 |
-- 显式事务
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SAVEPOINT sp1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- 如果失败可以 ROLLBACK TO SAVEPOINT sp1
COMMIT;
-- 隐式事务(单语句自动包裹)
UPDATE orders SET status = 'shipped' WHERE id = 123;
生产建议:默认 READ COMMITTED 通常足够;REPEATABLE READ 用于需要事务内数据视图一致的环境;SERIALIZABLE 用于对一致性要求极高的金融操作,但可能遇到 serialization_failure 冲突(需重试)。
七、安装与基本操作
7.1 安装(多种方式)
# macOS(推荐)
brew install postgresql@17
brew services start postgresql@17
# Ubuntu/Debian
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
sudo apt update
sudo apt install postgresql-17 postgresql-contrib
sudo systemctl enable --now postgresql
# Docker(开发推荐)
docker run -d --name postgres \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=admin123 \
-e POSTGRES_DB=myapp \
-p 5432:5432 \
-v pg_data:/var/lib/postgresql/data \
postgres:17-alpine
7.2 常用 psql 命令
\l -- 列出所有数据库
\c database_name -- 切换数据库
\dt -- 列出所有表
\d table_name -- 查看表结构(含索引、外键)
\du -- 列出所有角色
\dn -- 列出所有 Schema
\df -- 列出所有函数
\i /path/to/file.sql -- 执行 SQL 文件
\timing on -- 显示每条 SQL 执行时间
\x -- 以扩展模式显示结果
\q -- 退出
7.3 用户与权限管理
-- 创建带密码的用户
CREATE USER myuser WITH ENCRYPTED PASSWORD 'mypassword' LOGIN;
DELETE FROM pg_authid WHERE rolname = 'myuser'; -- 危险操作
-- 创建角色(禁止登录,用于分组权限)
CREATE ROLE app_read;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_read;
GRANT app_read TO myuser;
-- 数据库级权限
CREATE DATABASE myapp OWNER myuser;
REVOKE ALL ON DATABASE myapp FROM PUBLIC; -- 安全基线
-- 给用户开启创建 Schema 的权限
ALTER USER myuser CREATEDB;
八、高可用架构
8.1 流复制(Streaming Replication)
Primary(主库,读写)
├── Sync Standby 1(同步复制,保证零丢数据)
├── Async Standby 2(异步复制,允许秒级延迟)
└── Async Standby 3(只读查询)
WAL → wal_send → Network → wal_receive → Apply → Standby
配置要点(postgresql.conf):
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
synchronous_commit = remote_apply # 主库等待备库应用
hot_standby = on # 允许备库只读
8.2 自动故障转移
PostgreSQL 原生不支持选主(没有 Raft),需配合外部工具:
| 工具 | 原理 | 复杂度 | 适用规模 |
|---|---|---|---|
| Patroni | etcd/ZooKeeper 选主 + 回调脚本 | 中等 | 生产首选 |
| repmgr | ssh 直连管理节点,支持级联复制 | 低 | 中小型 |
| pg_auto_failover | Citus 团队开发,整合在官方 postgres 分发中 | 低 | 生产推荐 |
推荐架构:Patroni + etcd + HAProxy/VIP = 生产级高可用。
九、连接池(PgBouncer)
PostgreSQL 每个连接消耗 ~10MB 内存,无连接池场景下应用可能压垮数据库。部署架构:
App Server × N
↓ pooled connections (10-100)
PgBouncer (Middle Layer)
↓ max connections (取决于硬件,通常 100-500)
PostgreSQL Primary
PgBouncer 三种池模式:
| 模式 | 行为 | 适用 |
|---|---|---|
| Session | 连接关闭才归还 | 会话级操作(临时表、SET) |
| Transaction(推荐) | 事务结束归还 | 大多数 Web 应用 |
| Statement | 语句结束归还 | 极高负载、不允许事务 |
; pgbouncer.ini
databases:
myapp = host=localhost port=5432 dbname=myapp
pgbouncer:
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 20
min_pool_size = 10
为什么不用应用层连接池(如 Node.js max 参数)?应用层连接池 = 每个进程 × N,当应用实例有数百个时总连接数仍然暴敞。PgBouncer 作为进程级连接池将所有实例的短连接汇聚为少量长连接。
常见问题(FAQ)
PostgreSQL vs MySQL,新项目该选谁?
选 PostgreSQL:需要复杂查询(窗口函数、CTE)、GIS 地理空间、JSONB 混合存储、表分区、可扩展性(自定义类型/函数)。
选 MySQL:纯简单 CRUD、已有 MySQL 生态深度嵌入、团队更熟悉且不需要高级特性。
Postgres 在中国访问慢怎么办?
如果数据库服务器在海外(如 AWS RDS us-east-1):
- 国内云部署:阿里云 RDS PostgreSQL、腾讯云 TDSQL PostgreSQL、AWS 北京/宁夏区域
- 使用连接池(PgBouncer)减少 TCP TLS 握手开销
- 在应用近端加缓存层(Redis)缓存热点查询
- 开启压缩(PostgreSQL 16+ WAL 压缩 + pg_dump 压缩传输)
JSONB 能完全替代 MongoDB 吗?
不能。JSONB 适合"以关系型为主、偶尔需要 JSON 灵活性"的场景。纯文档应用(Schema 自由、海量日志存储、无需 JOIN)MongoDB 更合适,因为它在分片水平扩展上更原生。
PostgreSQL 表膨胀(Bloat)怎么解决?
-- 查看膨胀率最高的表
SELECT schemaname, tablename, n_dead_tup, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC LIMIT 10;
-- 手动 VACUUM(不锁表)
VACUUM (VERBOSE, ANALYZE) logs;
-- 查看表物理大小与膨胀率
SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total,
round(100 * (pg_relation_size(relid) - pg_table_size(relid)) / pg_relation_size(relid), 2) AS bloat_pct
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
如何估算 PostgreSQL 硬件需求?
| 规模 | CPU | 内存 | 磁盘 | 适用 |
|---|---|---|---|---|
| 开发/测试 | 2 核 | 4 GB | 50 SSD | 本地开发 |
| 小型生产 | 4 核 | 16 GB | 200 SSD | 10万日活 |
| 中型生产 | 8 核 | 64 GB | 1TB NVMe | 100万日活 |
| 大型生产 | 16+ 核 | 128+ GB | 多 TB NVMe + 副本 | 千万级日活 |
内存分配建议:shared_buffers = 内存的 25%,剩余留给 OS 文件缓存和连接消耗。
相关阅读
- PostgreSQL vs MySQL vs MongoDB 选型对比 — 12 维度评分 + 8 场景决策矩阵
- PostgreSQL Docker 部署与初始化 — docker-compose、PgBouncer、参数调优
- PostgreSQL 高级 SQL 查询实战 — 窗口函数、CTE、JSONB 深度
- Prisma + PostgreSQL 实战 — Schema 设计、Migration、类型安全查询
- PostgreSQL 性能优化 — EXPLAIN ANALYZE、索引选择、VACUUM
- PostgreSQL 高可用与备份 — 流复制、Patroni、备份策略
- PostgreSQL 安全与权限管理 — RLS、SSL、pg_hba、审计
- PostgreSQL 扩展生态 — pgvector、PostGIS、TimescaleDB、Citus
- Node.js + Prisma + PostgreSQL 实战 — Nest.js/Express 集成
- PostgreSQL 专题导航 — 所有文章的完整索引
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。