如果你已经会写一点 SQL,又在琢磨要不要换一个更“扛得住”的数据库,那 PostgreSQL 大概率是绕不开的名字。它不是突然蹿红的网红,而是一款从 1986 年一路演化到今天、被社区称为“最先进的开源数据库”的老牌选手。
这篇文章属于「从入门到精通」系列。和系列里其它篇目一样,它面向的是有一点编程与 SQL 基础、但希望把这项技能系统学一遍的读者。稍有不同的是,本篇会更强调“路线”——先讲清楚按什么顺序学、每个阶段学到什么程度算过关,再给一批能直接跑起来的实战代码。
全文以 2026 年 9 月的版本现状为基准:主力是 PostgreSQL 18(当前稳定版,最新小版本 18.6),PostgreSQL 19 仍在 Beta 测试中且已确认延期。文中的代码可以直接贴进 psql 执行;只有新版本才有的语法,我会单独标注“PG 18+”。
一、为什么 2026 年值得认真学 PostgreSQL
第一,它是典型的“学一次,用很多年”。
数据库技能有个特点:迁移成本高,复用价值也高。你一旦吃透了 PostgreSQL 的 SQL 标准写法、事务模型、索引原理,回头看别的数据库会轻松很多;反过来,把一套系统从零换库的代价往往又很大。所以选一门“值得长期投入”的数据库,比追热点重要得多。
第二,它的能力边界一直在扩张。
过去几年,PG 慢慢从“最好的开源关系型数据库”变成了“全场景数据平台”:地理信息有 PostGIS,时序有 TimescaleDB,向量检索有 pgvector,分布式有 Citus。这种“一库多用”的路线,最直接的好处是技术栈能少一层——凌晨三点排障时,少一个系统要看,就是少一份痛苦。
第三,它的社区和生态非常稳。
PG 由全球开发组(PGDG)主导,不属于任何一家商业公司,采用宽松的 PostgreSQL License(类 BSD/MIT),商用、修改、分发基本没有额外约束。每年一个大版本、每个大版本约五年官方维护的节奏,已经稳定执行了很多年。
几个能直观感受到热度的信号:在 DB-Engines 全球数据库流行度榜上,PostgreSQL 长期稳居前四,是近几年增长最快的关系型数据库之一;在各大开发者调查里,它常年是最受开发者青睐的数据库;阿里云、腾讯云、华为云也都在主推各自的 PG 托管服务。
不过也要说句公道话:选数据库要拿需求去卡,而不是拿热度去卡。 如果你的项目接口简单、以增删改查为主,MySQL 成熟、轻量、生态广,照样是很好的选择。PG 真正拉开差距的场景,通常是下面这几类:
- 复杂查询多:多表关联、聚合分析、报表类 SQL 频繁出现;
- 数据形态杂:既有规整的表,又有 JSON、数组、范围、地理坐标;
- 要扩展能力:想把全文检索、向量检索、时序、GIS 塞进同一个库;
- 一致性要求高:金融、账务、库存这类容不得含糊的业务。
一句话总结:当“一套数据库想干好几件事”时,优先考虑 PostgreSQL。
二、PostgreSQL 是什么:三分钟建立整体印象
一句话定义:PostgreSQL 是一个开源、免费、功能完备的对象-关系型数据库管理系统(ORDBMS)。
它的来历。 1986 年,加州大学伯克利分校启动了一个叫 POSTGRES 的研究项目,目标是做一个足够严谨、又能自由扩展的数据库。项目几经演进,名字从 POSTGRES 变成 PostgreSQL。几十年过去,当年一起起跑的很多产品早已停更或商业化,它却靠活跃的开源社区一路活到了今天。
一句话概括它的性格,可以记住这五个特点:
- 开源且协议宽松:PostgreSQL License 近似 BSD/MIT,几乎没有商业限制。
- 严格遵循 SQL 标准:窗口函数、CTE、递归查询、外键、触发器、存储过程这些都支持得很完整,写复杂 SQL 时很少因为“语法它不支持”而卡住。
- 扩展性极强:缺什么能力就装扩展——GIS 用 PostGIS,时序用 TimescaleDB,向量检索用 pgvector,慢查询分析用 pg_stat_statements。
- 并发控制稳:用多版本并发控制(MVCC),读写不互相阻塞,长事务、复杂事务下表现尤其扎实。
- 数据类型丰富:JSONB、数组、范围类型、网络地址、几何类型、枚举、UUID 都是原生支持,还能自定义类型。
如果只能打一个比方:MySQL 像一辆省心耐用的家用车,PostgreSQL 更像一辆能跑高速也能越野的全能车。 它上手时概念多一些、门槛略高,但越用越能体会到“底子厚”的好处。
三、2026 版本全景:到底该装哪一个
PG 的版本节奏很规律:每年发一个大版本,每个大版本持续获得约五年的官方维护。 小版本号(如 18.6)只做缺陷修复和安全补丁,不做新功能,因此“大版本号”才是你需要关注的对象。
截至 2026 年 9 月,处于维护期的版本大致如下:
| 版本 | 状态 | 最新小版本 | 官方维护至 | 给你的建议 |
|---|---|---|---|---|
| PostgreSQL 19 | Beta 测试中 | Beta 4(2026-09-24) | — | 尝鲜、测特性,别上生产 |
| PostgreSQL 18 | 当前稳定版 | 18.6(2026-08-13) | 约 2030 年 | 新项目首选 |
| PostgreSQL 17 | 上一稳定版 | 17.11 | 约 2029 年 | 求稳的老项目 |
| PostgreSQL 16 | 维护中 | 16.15 | 约 2028 年 | 过渡使用 |
| PostgreSQL 15 | 维护中 | 15.19 | 约 2027 年 | 存量系统 |
| PostgreSQL 14 | 即将停止维护 | 14.24 | 2026 年 11 月 | 尽快规划升级 |
给你的结论很直接: 新项目直接上 PostgreSQL 18.x,功能最新、维护周期最长。老项目如果还停在 14,建议尽早安排升级,因为它的维护窗口今年年底就要关了。
关于 PG 19,多说一句。 2026 年 4 月 8 日它进入特性冻结,原计划 9 月发布,但 9 月中旬社区确认延期——Beta 4 在 9 月 24 日才放出,GA(正式版)时间待定。原因是这一版过于“激进”,Beta 期间有五十多项特性因为设计缺陷或正确性问题被撤回。对普通开发者来说结论很实际:生产环境用 18.x,等 19 正式发布并稳定一段时间后再评估升级。
四、完整学习路线图:四个阶段,从零到专家
下面这张表是全文的骨架。它回答的核心问题是:我现在该学什么,学到什么程度算这一步过关。
| 阶段 | 大致周期 | 关键词 | 过关标准 |
|---|---|---|---|
| ① 入门打地基 | 约 1 个月 | 安装、psql、建表、增删改查、约束 | 能独立做完一个带多表的小项目 |
| ② 进阶写查询 | 2–3 个月 | JOIN、窗口函数、CTE、JSONB、索引、EXPLAIN | 会写“漂亮”的查询,能定位并优化一条慢 SQL |
| ③ 高级为生产 | 3–6 个月 | 事务、MVCC、VACUUM、WAL、备份、复制、分区 | 能对一个真实的生产库负责 |
| ④ 专家之路 | 长期 | 内核原理、性能调优、扩展开发、分布式、云原生 | 能设计并运维大规模集群 |
阶段一:入门打地基(约 1 个月)
目标:能用它完整跑通一个小项目,而不是只会背语法。
要掌握的内容:
- 在 Linux / macOS / Windows / Docker 上完成安装与初步配置,认识
postgresql.conf、pg_hba.conf这两个核心配置文件; - 熟悉
psql命令行和元命令,这是后续所有操作的基础功; - DDL:建库、建表、建索引;DML:增、删、改、查;
- 主键、外键、
NOT NULL、UNIQUE、CHECK这些约束的意义和写法; - 常用数据类型:
INT、BIGINT、TEXT、NUMERIC、TIMESTAMPTZ、BOOLEAN、JSONB。
过关标准: 脱开教程,自己设计几张有关联的表(比如一个博客的“用户 / 文章 / 评论”),并能写出完整的增删改查。
阶段二:进阶写查询(2–3 个月)
目标:从“会写”到“写得好”,并能看懂数据库在想什么。
要掌握的内容:
- 多表关联
JOIN的各类形态(INNER、LEFT、FULL)、子查询、集合操作; - 聚合与分组
GROUP BY/HAVING,以及窗口函数; - CTE(
WITH)与递归 CTE,用来拆解复杂逻辑、处理树形结构; JSONB的半结构化查询、全文检索;- 索引的原理与类型,配合
EXPLAIN/EXPLAIN ANALYZE读懂执行计划。
过关标准: 拿到一条慢 SQL,能看懂它的执行计划,知道瓶颈在哪(全表扫描?索引没命中?排序开销大?),并给出可验证的优化方案。
阶段三:高级为生产(3–6 个月)
目标:能对一个线上数据库负责。
要掌握的内容:
- 事务与隔离级别,理解 ACID 到底保证了什么;
- MVCC 的实现思路,以及它带来的“表膨胀”问题和
VACUUM机制; - WAL(预写式日志)与崩溃恢复,理解数据为什么不会丢;
- 锁机制与常见锁等待的排查;
- 备份恢复(逻辑备份 / 物理备份)、流复制与逻辑复制、高可用方案;
- 分区表的设计与选型;
- 监控体系:
pg_stat_statements、pg_stat_activity、慢查询日志; - 连接池(PgBouncer 等)的部署与取舍。
过关标准: 你能设计出一套“主从复制 + 定时备份 + 监控告警 + 连接池”的最小可用生产架构,并说清每个组件的失效后果。
阶段四:专家之路(长期)
目标:从“会用”走向“懂原理、能定方案”。
方向包括:深入内核(缓冲池管理、查询优化器、MVCC 实现细节、WAL 机制)、大规模运维与调优、扩展开发、分布式(Citus)、云原生部署(Kubernetes 上的 PG Operator)、以及多模应用(GIS / 时序 / 向量)。到了这个层次,写 SQL 已经是基本功,更值钱的是架构判断力和排障直觉。
五、实战篇 ①:安装、连接与 psql 入门
5.1 三种常见的安装方式
Linux(Ubuntu / Debian):
# 用官方源安装,能拿到较新的版本
sudo apt update
sudo apt install -y postgresql postgresql-contrib
# 看一眼服务有没有跑起来
sudo systemctl status postgresql
macOS(Homebrew):
brew install postgresql@18
brew services start postgresql@18
Docker(只想快速试一下,最省事):
docker run --name pg18 \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
-d postgres:18
Windows 用户可以直接下载官方提供的图形化安装包,一路下一步即可。唯一要注意的是把安装目录下的 bin 加进系统 PATH,否则命令行里敲 psql 会提示“不是内部或外部命令”。
5.2 连接数据库
PG 安装后默认会创建一个系统用户 postgres 和一个同名数据库。在本机上,通常这样进去:
sudo -u postgres psql
要用密码连指定库、并显式指定主机:
psql -U postgres -h 127.0.0.1 -d appdb
5.3 psql 常用元命令(务必背下来)
psql 除了直接写 SQL,还提供一批以反斜杠开头的元命令,管理数据库时极其好用:
| 命令 | 作用 |
|---|---|
\l | 列出所有数据库 |
\c 数据库名 | 切换到指定数据库 |
\dt | 列出当前库的所有表 |
\d 表名 | 查看表结构(列、类型、索引) |
\d+ 表名 | 更详细的表信息 |
\dn | 列出所有 schema |
\du | 列出角色和用户 |
\df | 列出函数 |
\x | 切换横向 / 纵向显示(宽表非常实用) |
\timing | 打开 / 关闭语句耗时显示 |
\q | 退出 |
六、实战篇 ②:SQL 基础与数据类型避坑
6.1 建库与建表
CREATE DATABASE appdbENCODING 'UTF8';
建表时,现代写法推荐用 GENERATED ALWAYS AS IDENTITY 生成自增主键,它比老式的 SERIAL 更符合 SQL 标准:
CREATE TABLE users (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE,
age INT CHECK (age >= 0),
profile JSONB NOT NULL DEFAULT '{}'::jsonb,
tags TEXT[] NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
这段语句里藏了几个 PG 的“性格”,值得逐条读懂:
GENERATED ALWAYS AS IDENTITY:主键自动递增。老教程里的SERIAL/BIGSERIAL现在仍可用,但新代码建议用新写法。CHECK (age >= 0):列级约束,插入负数会被直接拒绝。profile JSONB:存半结构化 JSON;tags TEXT[]:数组类型。这两种类型在 MySQL 里没有等价物。TIMESTAMPTZ带了时区,比不带时区的TIMESTAMP更适合表达“时间点”。DEFAULT now():插入时可以省略这一列。
6.2 增、删、改、查
插入数据:
INSERT INTO users (username, email, age)
VALUES ('alice', 'alice@example.com', 30), ('bob', 'bob@example.com', 25);
查询:
SELECT id, username, email
FROM users
WHERE age >= 18
ORDER BY created_at DESC
LIMIT 10;
更新与删除:
UPDATE users SET age = age + 1 WHERE username = 'alice';DELETE FROM users WHERE username = 'bob';
“存在就更新、不存在就插入”这种需求,PG 用 ON CONFLICT 一条语句就能搞定,这是它比很多数据库顺手的地方:
INSERT INTO users (username, email, age)
VALUES ('alice', 'alice@example.com', 31)
ON CONFLICT (username)
DO UPDATE SET age = EXCLUDED.age, email = EXCLUDED.email;
这里的 EXCLUDED 代表“本次想插入、但撞上冲突的那一行”。
6.3 数据类型上最容易踩的四个坑
- 文本优先用
TEXT。 在 PG 里TEXT和VARCHAR性能基本一致,用TEXT还省得提前纠结长度。 - 金额一定用
NUMERIC,别用浮点。NUMERIC(12, 2)能精确保存两位小数,FLOAT会有误差。 - 时间优先用
TIMESTAMPTZ。 涉及跨时区、多地区用户时,带时区的类型能帮你省掉大量换算烦恼。 - 自增主键优先
BIGINT而不是INT。 表一大,INT的上限会非常难受。
七、实战篇 ③:进阶查询六件套
这一节的东西,是把 PG 从“能用”拉向“好用”的关键。
7.1 关联查询 JOIN
SELECT o.id, u.username, o.amount
FROM orders AS o
JOIN users AS u ON u.id = o.user_id
WHERE o.created_at >= now() - INTERVAL '7 days';
7.2 聚合与分组
SELECT user_id, count(*) AS order_count,
sum(amount) AS total
FROM orders
GROUP BY user_id
HAVING sum(amount) > 1000
ORDER BY total DESC;
记住区分:WHERE 在分组前过滤行,HAVING 在分组后过滤组。
7.3 窗口函数
窗口函数能在不合并行的前提下做“组内计算”,是 PG 的强项:
SELECTuser_id,
amount,
row_number() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn,
sum(amount) OVER (PARTITION BY user_id) AS user_total
FROM orders;
PARTITION BY 相当于“按用户分组”,但每一行都保留下来。想取每个用户的“最近一笔订单”,用 rn = 1 一筛就行,比在应用层写循环干净得多。
7.4 CTE 与递归查询
普通 CTE 可以把复杂查询拆成几段,读起来清楚:
WITH recent AS (SELECT * FROM orders WHERE created_at >= now() - INTERVAL '30 days'
)
SELECT user_id, count(*) FROM recent GROUP BY user_id;
递归 CTE 专门处理树形结构,比如组织架构、分类目录:
WITH RECURSIVE org AS (SELECT id, name, manager_id, 1 AS depth
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.id, e.name, e.manager_id, org.depth + 1
FROM employees AS e
JOIN org ON e.manager_id = org.id
)
SELECT * FROM org ORDER BY depth, id;
7.5 JSONB 查询
JSONB 是 PG 处理半结构化数据的招牌能力。它不像普通文本那样“只能存不能查”,而是能建索引、能按路径过滤:
-- 假设 profile 形如 {"city": "yichang", "vip": true}-- 取某个键的值(->> 返回文本)
SELECT username
FROM users
WHERE profile ->> 'city' = 'yichang';
-- 容器包含判断(@>)
SELECT count(*)
FROM users
WHERE profile @> '{"vip": true}';
当查询集中在某个 JSONB 字段上时,给它配一个 GIN 索引,速度会有质的变化,下一节会讲到。
7.6 全文检索
不装外挂搜索引擎,PG 自己就能做一定规模的全文检索:
SELECT title
FROM articles
WHERE to_tsvector('simple', body) @@ to_tsquery('simple', 'postgres & index');
八、实战篇 ④:索引与性能调优
8.1 先理解索引的“两面性”
索引的作用,是让数据库不必逐行扫描整张表就能定位数据。PG 默认用 B-tree 索引,它像一本排好序的目录,等值查询和范围查询都能用上。
但索引有代价:它会拖慢写入。 每次插入、更新、删除,相关索引都要跟着维护。所以索引不是越多越好,而是要“按查询来建”。
8.2 常用索引类型
| 类型 | 适合场景 |
|---|---|
| B-tree | 默认类型;等值、范围、排序,覆盖面最广 |
| Hash | 只做等值查询时偶尔更快 |
| GIN | 数组、JSONB、全文检索这类“一个字段多个值”的场景 |
| GiST | 地理数据、范围类型 |
| BRIN | 超大表且数据物理上有序(如按时间顺序写入的日志) |
几种实用写法:
-- 普通 B-tree
CREATE INDEX idx_orders_user_id ON orders (user_id);-- 生产环境给大表建索引,别锁表
CREATE INDEX CONCURRENTLY idx_orders_created_at ON orders (created_at);
-- 部分索引:只索引真正会被查的行
CREATE INDEX idx_orders_pending ON orders (created_at) WHERE status = 'pending';
-- JSONB 用 GIN
CREATE INDEX idx_users_profile ON users USING GIN (profile);
-- 时间序列大表用 BRIN
CREATE INDEX idx_events_ts ON events USING BRIN (created_at);
CONCURRENTLY 值得单独记一下:在大表上直接建普通索引会锁住写入,线上用它可以平和很多,代价是建得慢一点、且不能放在事务里。部分索引也很香——如果 pending 状态的订单只占全表的 5%,那这个索引的体积就只有全量索引的 5%。
8.3 复合索引与“最左前缀”
多列索引 (a, b) 的可用规则是:查询里得带上最左边的那一列。
WHERE a = 1:能用上;WHERE a = 1 AND b = 2:能用上;WHERE b = 2:用不上。
所以列的顺序很关键。通常把区分度高(不同值多)的列放左边,但最终还是要以真实查询为准。
8.4 用 EXPLAIN 看执行计划
别靠感觉调优。拿到慢查询的第一件事,是看它的执行计划:
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 42;
三个要点:
- 裸
EXPLAIN只给计划,不会真的执行; - 加
ANALYZE会真跑一遍并给出实际耗时和行数,所以别在线上对写语句或大查询随手加它; - 加
BUFFERS会显示读了多少缓存页、多少磁盘页,判断 I/O 压力很有用。
看到 Seq Scan(全表扫描)也别慌——如果查询本来就要读表里大部分数据,顺序扫描反而更快。规划器通常比你更懂,除非基准测试证明它错了。
8.5 四条能立刻用上的经验
- 戒掉
SELECT *:少取一列就少读一点数据,也更容易走“只用索引返回结果”的快速路径。 - 分页别用大 OFFSET:
OFFSET 100000意味着前面十万行被白扫一遍。
-- 不推荐:越翻到后面越慢
SELECT * FROM orders ORDER BY id LIMIT 20 OFFSET 100000;-- 推荐:记住上一页最后一个 id,成本基本恒定
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 20;
- 批量插入别一条条来:一万条单行
INSERT就是一万次网络往返。
海量导入首选 COPY:百万行数据往往几分钟搞定
psql -U postgres -d appdb -c "\copy logs (msg) FROM 'logs.csv' WITH (FORMAT csv)"
- 小心 N+1 查询:在应用代码里循环查十次,往往不如一条写好的
JOIN。
九、实战篇 ⑤:事务、MVCC 与 VACUUM
9.1 事务与 ACID
事务保证一组操作要么全部成功、要么全部回滚:
BEGIN;UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT; -- 中途出错可以改用 ROLLBACK; 回退
PG 有个很容易被忽略的优势:它的 DDL 也能放进事务。 也就是说,一串建表、改列的操作如果中途失败,可以整体回滚,不会留下半成品结构。
9.2 隔离级别
SQL 标准有四个隔离级别。PG 默认是 Read Committed(读已提交):你只能看到已提交的数据,但同一条语句里两次读到的结果可能不同。需要更强一致性时,可以按事务指定:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 这个事务里,重复读同一行结果保持一致
COMMIT;
PG 还实现了可串行化(Serializable)级别,底层是 SSI(可串行化快照隔离),能在不太牺牲并发的前提下防止“写偏斜”这类隐蔽问题。金融类逻辑值得考虑开启。
9.3 MVCC:一句话讲清“为什么读写不打架”
PG 的并发控制靠 MVCC(多版本并发控制)。可以这样理解:改数据时它不覆盖旧数据,而是留一个旧版本、再写一个新版本。
每一行背后都隐含着 xmin(这个版本由哪个事务创建)和 xmax(被哪个事务标记删除)。查询时,数据库拿着自己的“快照”去判断哪一行对自己可见。于是读和写互不阻塞——这正是 PG 在高并发下表现稳的根本原因。
代价是:更新和删除留下的旧版本(叫“死元组”)会占地方。更新越频繁,表越容易“膨胀”。谁来清理?VACUUM。
-- 看看哪些表堆积了死元组
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;-- 手动清理(通常 autovacuum 会自动做)
VACUUM (ANALYZE, VERBOSE) users;
平时不用天天盯 VACUUM,但大批量更新或删除之后,值得手动跑一次。长期不管的话,表会膨胀、查询变慢,严重时还会逼近事务 ID 回卷的红线——那是会让整个库进入只读的危险状态。
9.4 WAL:崩溃了数据为什么还在
WAL(预写式日志)的原则很朴素:在数据真正写进磁盘之前,先把它写进日志。 数据库崩溃重启时,从最近的检查点开始把日志重放一遍,就能恢复到崩溃前的一刻。
日志里的位置用 LSN(日志序列号)标记,它也是复制、时间点恢复的“刻度尺”。
-- 当前 WAL 写入位置
SELECT pg_current_wal_lsn();
9.5 别忘了:PG 的连接是“进程”
PG 采用“每个连接一个后端进程”的模型。每个连接都有自己的内存和调度开销,所以连接数一高,压力上升比线程模型快得多。高并发场景下,第一件该做的事往往不是调参数,而是在应用和数据库之间架一层连接池(如 PgBouncer)。
十、实战篇 ⑥:生产必备——备份、复制、分区
10.1 备份与恢复
逻辑备份适合单库迁移、版本兼容性要求高的场景:
-Fc 输出自定义压缩格式,便于选择性恢复
pg_dump -U postgres -d appdb -Fc -f appdb.dump
恢复到新库
pg_restore -U postgres -d appdb_new appdb.dump
物理备份是整实例级别的,常用于搭备库或做全量快照:
pg_basebackup -h 192.168.1.10 -U repl \
-D /var/lib/postgresql/18/main \
-Fp -Xs -P
10.2 复制与高可用
- 物理复制(流复制):备库持续接收主库的 WAL 并重放,主备数据几乎一致,是最常见的方案。
- 逻辑复制:按表、按行同步,粒度更细,适合跨版本迁移、异构同步、只同步部分数据。
要做到自动故障转移,通常还要配合 Patroni、repmgr 这类工具,让主库挂掉之后备库能自动顶上。
10.3 分区表
当单表涨到几千万行、几十 GB,索引维护和清理都会变得吃力。分区表的思路很朴素:把一张逻辑大表,按规则切成若干张物理小表。 查询时优化器会自动跳过无关分区,这叫分区裁剪。
CREATE TABLE orders (order_id BIGINT GENERATED ALWAYS AS IDENTITY,
customer_id INT NOT NULL,
order_date DATE NOT NULL,
amount NUMERIC(12,2) NOT NULL,
PRIMARY KEY (order_id, order_date) -- 主键必须包含分区键
) PARTITION BY RANGE (order_date);
CREATE TABLE orders_2026_01 PARTITION OF orders
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
CREATE TABLE orders_2026_02 PARTITION OF orders
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
分区最爽的地方体现在“删旧数据”:直接 DROP TABLE 掉过期分区是秒级的,比 DELETE 快几个数量级,也不产生垃圾版本。分区键要选高频查询条件里的那一列(时间序列通常用时间);选错了,优化器裁剪不掉,反而可能比不分区更慢。
十一、2026 新版本实战:PG 18 用起来,PG 19 看方向
11.1 PostgreSQL 18:值得用上的六件事
PG 18 于 2025 年秋发布,是目前新建项目最稳妥的选择。截至目前最新小版本为 18.6(2026 年 8 月发布,修复了 28 个安全漏洞和大量缺陷,建议所有 18.x 用户升级)。有体感的变化主要有这几项:
① 异步 I/O(Async I/O)。 过去读磁盘是同步等待,一个块读完才读下一个;18 引入预取后,可以一次性把一批 I/O 请求交给内核流水线处理,大表扫描、VACUUM 这类操作在云盘环境下的提升尤其明显。
SHOW io_method; -- 当前使用的异步 I/O 实现(如 io_uring / worker)
SHOW effective_io_concurrency; -- 普通查询允许的并发 I/O 请求数
② UUID v7。 随机 UUID(v4)做主键会让 B-tree 索引频繁分裂、空间膨胀。v7 把时间戳放在高位,天然按时间有序,新写入集中在索引末尾,对高并发写入友好得多。
SELECT uuidv7(); -- 生成一个按时间有序的 UUIDCREATE TABLE app_events (
id uuid PRIMARY KEY DEFAULT uuidv7(),
account_id uuid NOT NULL,
event_type text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
③ UPDATE ... RETURNING 能同时返回旧值和新值。 以前要拿到“更新前”和“更新后”两套值,得先查再改、还得加锁保证一致;现在一条原子语句搞定。
UPDATE products
SET price = price * 1.1
WHERE id = 100
RETURNING old.price AS price_before, new.price AS price_after;
④ Skip Scan。 对于 (class_id, custom_id) 这类复合索引,哪怕查询里只带了后面的 custom_id,只要前导列基数低,优化器也能“跳过”着用上索引,不再被迫全表扫描。
⑤ 原生时态约束。 “同一会议室在同一时间段不能被重复预定”这类需求,过去要用复杂的排他约束(EXCLUDE)实现,现在有了清晰的声明式写法:
CREATE TABLE subscriptions (user_id uuid NOT NULL,
type varchar(50) NOT NULL,
valid_period daterange NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id),
UNIQUE (user_id, valid_period WITHOUT OVERLAPS)
);
⑥ pg_upgrade 可迁移统计信息。 大版本升级后,统计信息不再“归零”,不用等 ANALYZE 跑完才能开放业务,升级停机窗口能明显缩短。
11.2 PostgreSQL 19:延期,但方向值得关注
必须先提醒:截至 2026 年 9 月,PG 19 还没有正式发布。 它 4 月 8 日进入特性冻结,Beta 4 直到 9 月 24 日才放出,社区已确认发布时间推迟(原目标 10 月底,实际可能是数周乃至数月之后)。
这一版原本规划了很多大功能,但 Beta 期间撤回了不少,其中就包括被反复宣传的 SQL/PGQ 原生图查询、GROUP BY ALL、MERGE / SPLIT PARTITIONS、按时间段局部更新等——原因多是设计缺陷、结果正确性或兼容性顾虑,它们大概率会顺延到下一个大版本。
仍然值得关注、有望随 PG 19 落地的主要有:
| 特性 | 它解决什么问题 |
|---|---|
pg_plan_advice / 查询计划建议 | 官方原生的执行计划干预能力,可给特定查询“锁定”计划 |
| 并行 autovacuum | 垃圾回收更聪明,降低事务 ID 回卷风险 |
ON CONFLICT DO SELECT | upsert 时能直接返回已存在的那一行 |
窗口函数支持 IGNORE NULLS | 补齐一个长期缺失的标准能力 |
REPACK 内核化 | 在线重整表,不必再依赖第三方扩展 |
WAIT FOR LSN | 让应用等到指定 WAL 位置同步后再返回,简化读写一致性 |
其中执行计划治理能力最受关注,用法大致是这样(PG 19 尚未 GA,语法以正式版文档为准):
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT *
FROM orders AS o
JOIN users AS u ON u.id = o.user_id;-- 拿到建议字符串后,可以按需应用到后续查询
SET pg_plan_advice.advice = 'JOIN_ORDER(o u) HASH_JOIN(u)';
给普通开发者的结论依旧很简单:生产环境安心用 18.x;等 19 正式发布并稳定一段时间,再评估要不要升级。 社区宁可延期也不发布带缺陷的版本,这种克制,长期看对用户是好事。
十二、扩展生态:PG 为什么“什么都能干”
PG 真正让人上头的,是它的扩展机制。缺什么能力,装个扩展就行,不用为此再维护一套独立系统。
- PostGIS:地理空间数据的事实标准,路径规划、距离计算、区域统计都能在库里做。
- TimescaleDB:把 PG 变成时序数据库,适合 IoT、监控、指标类数据。
- pgvector / pgvectorscale:向量检索,是当下 AI 应用的热门选项。
- Citus:横向扩展,把单机 PG 变成分布式集群。
- pg_stat_statements:慢查询分析必备,几乎每个生产库都该开。
- FDW(外部数据包装器):能直接查询 MySQL、MongoDB、CSV 等外部数据源。
举个例子,只用 PG 加 pgvector,就能给一篇文档做语义检索(也就是大模型 RAG 的基础):
CREATE EXTENSION IF NOT EXISTS vector;CREATE TABLE documents (
id bigserial PRIMARY KEY,
title text NOT NULL,
content text,
embedding vector(1536) -- 维度要和你的 embedding 模型一致
);
-- 用 HNSW 索引加速近似最近邻检索
CREATE INDEX idx_doc_embedding ON documents
USING hnsw (embedding vector_cosine_ops);
检索时按余弦距离(pgvector 的 <=> 运算符)排序取前几名即可。值得注意的是,“语义匹配 + 关系遍历”正是 PG 多模能力最迷人的地方——你可以在同一条 SQL 里,既做向量的模糊相似度搜索,又做表之间的精确关联过滤。
也正因为这些扩展,PG 的运维工具链和生态在不断完善。虽然在某些细分上(如 MySQL 的 Percona Toolkit)还不如对手成熟,但“一个库解决问题”的诱惑,对多数团队来说更实在。
十三、避坑清单:16 条能立刻用上的经验
- 新项目直接用 PG 18.x,别贪新版本,也别停在即将 EOL 的老版本。
- 金额用
NUMERIC,别用浮点;时间用TIMESTAMPTZ,别用裸TIMESTAMP。 - 自增主键优先
BIGINT,INT迟早会不够用。 - 戒掉
SELECT *,只取你真正需要的列。 - 分页别用大
OFFSET,改用“记住上一页最后一个 id”的游标式分页。 - 大表建索引加
CONCURRENTLY,避免锁住线上写入。 - 给 JSONB、数组、全文检索字段配 GIN 索引,否则查询依然会慢。
- 批量写入用
COPY或单条多行INSERT,别在循环里一条条插。 - 连接数是甜区,不是越多越好;上生产前先架好连接池。
- 大批量更新或删除后手动跑一次
VACUUM (ANALYZE),避免表膨胀。 - 迁移或重建之后记得
ANALYZE,否则统计信息不准、执行计划会跑偏。 - 开
pg_stat_statements,它会告诉你最耗资源的 SQL 是哪几条。 - 备份要演练恢复,没恢复过的备份等于没有备份。
- DDL 也能进事务,迁移脚本可以包在事务里,失败就整体回滚。
- 主键别用随机 UUID v4,要么用
BIGINT,要么用 PG 18 的 UUID v7。 - 分区键要选高频查询条件那一列,选错了可能比不分区更慢。
十四、学习资源与进阶方向
第一手资料:官方文档。 PostgreSQL 的官方文档质量在开源项目里属于顶尖,而且有中文翻译。遇到语法问题,先查官方文档,比搜二手博客靠谱。
工具选型:
- psql:官方命令行客户端,保底必备,值得练熟;
- pgAdmin:官方图形化管理工具,功能全面;
- DBeaver:跨平台的通用数据库客户端,支持多种数据库;
- DataGrip:JetBrains 出品的数据库 IDE,写 SQL 体验很好;
- PgBouncer:生产环境几乎必备的连接池。
社区与社区资源: GitHub 上的 awesome-postgres 项目把主流的工具、扩展、教程整理成了一张清单,适合当“地图”翻;邮件列表和各大技术社区的中文圈子氛围也不错。
进阶方向(给自己定个小目标):
- 把
EXPLAIN的执行计划读熟,能一眼看出性能瓶颈; - 亲手搭一套“流复制 + Patroni 故障转移”的最小高可用集群;
- 深入理解 MVCC 和 WAL,读一读官方文档里对应的章节;
- 接触扩展开发或分布式方案(Citus),理解 PG 的天花板在哪。
顺带说下认证。 如果想把学习路径做得更“有章法”,可以关注 PG 的认证体系(PGCA 入门 / PGCE 进阶 / PGCM 专家),它给了一条相对清晰的能力分级参考。当然,认证只是路标,真正的功力还是在项目里练出来的。
最后提一句 AI 时代的趋势。 如今越来越多的 PG 岗位会问两件事:一是 RAG 与向量检索(pgvector、HNSW / IVFFlat / DiskANN 索引怎么选),二是 AI 辅助运维(用自然语言做容量风险识别、慢 SQL 优化)。这不是噱头——大模型不会让 DBA 失业,但会重塑这个岗位的技能结构,早点接触没坏处。
结语
从一条 SELECT 到撑起一个生产系统,PostgreSQL 中间隔着不少东西:数据类型、索引、事务、MVCC、WAL、复制、分区、扩展,每一块都能单独写一本书。但别被这份清单吓到——绝大多数日常开发,用到的就是前面五到八节的内容。
真正拉开差距的,往往不是记住了多少语法,而是遇到慢查询时知道先看 EXPLAIN、更新频繁时记得关注 VACUUM、连接数一涨就想到上连接池。这些习惯,比背下所有参数都值钱。
如果这篇长文只能让你带走一句话,那就是:先用起来,再慢慢往深里走。
本文是「从入门到精通」系列的一篇,与系列中的 SQL、MySQL、Python、Go 等篇目可搭配阅读。数据库的世界里,广度决定选择,深度决定价值。