如果你已经会写一点 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 19Beta 测试中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.242026 年 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 appdb
ENCODING '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 的强项:


SELECT
user_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(); -- 生成一个按时间有序的 UUID

CREATE 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 SELECTupsert 时能直接返回已存在的那一行
窗口函数支持 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 条能立刻用上的经验

  1. 新项目直接用 PG 18.x,别贪新版本,也别停在即将 EOL 的老版本。
  2. 金额用 NUMERIC,别用浮点;时间用 TIMESTAMPTZ,别用裸 TIMESTAMP。
  3. 自增主键优先 BIGINT,INT 迟早会不够用。
  4. 戒掉 SELECT *,只取你真正需要的列。
  5. 分页别用大 OFFSET,改用“记住上一页最后一个 id”的游标式分页。
  6. 大表建索引加 CONCURRENTLY,避免锁住线上写入。
  7. 给 JSONB、数组、全文检索字段配 GIN 索引,否则查询依然会慢。
  8. 批量写入用 COPY 或单条多行 INSERT,别在循环里一条条插。
  9. 连接数是甜区,不是越多越好;上生产前先架好连接池。
  10. 大批量更新或删除后手动跑一次 VACUUM (ANALYZE),避免表膨胀。
  11. 迁移或重建之后记得 ANALYZE,否则统计信息不准、执行计划会跑偏。
  12. 开 pg_stat_statements,它会告诉你最耗资源的 SQL 是哪几条。
  13. 备份要演练恢复,没恢复过的备份等于没有备份。
  14. DDL 也能进事务,迁移脚本可以包在事务里,失败就整体回滚。
  15. 主键别用随机 UUID v4,要么用 BIGINT,要么用 PG 18 的 UUID v7。
  16. 分区键要选高频查询条件那一列,选错了可能比不分区更慢。

十四、学习资源与进阶方向

第一手资料:官方文档。 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 等篇目可搭配阅读。数据库的世界里,广度决定选择,深度决定价值。

发表评论