PostgreSQL 死了吗?(第二部分)

由 John Doe 十一月 3, 2026

PostgreSQL 这套架构已经有 40 年的历史了,它还能够适应 2026 年的世界吗?

目录

作者:Oz

上周我发布《PostgreSQL 死了吗?》之后,引发的争论远比我想象的更大、更激烈。一个阵营接受了这篇文章,说:终于有人谈到水平扩展的痛苦和现代工作负载的沉重压力了。与此同时,数十位资深工程师和数据库纯粹主义者则在评论区把我批得体无完肤:问题不在 Postgres,而在你的架构视野。

我读完了每一条评论、每一个架构上的反对意见,以及每一场 Discord 辩论。我要坦白一件事:我在第一篇文章里故意留了一个缺口。是的,如果你把一套 40 年历史的架构,配上默认的 postgresql.conf 设置,硬塞进 2026 年多区域、AI 原生工作负载的世界里,你就会撞上一堵砖墙。系统不仅会变慢;它会在自身重量下崩溃。

但答案并不是抛弃这个让我们夜里能睡安稳觉的传奇数据库。答案是现代化我们使用它的方式。让我们把粉丝心态和愤怒放到一边,深入纯工程、硬件限制和真实世界拓扑。

image

“一刀切”的神话与共享内存中毒

数据库传奇人物、图灵奖得主 Michael Stonebraker 早在 2005 年那篇著名论文《One Size Fits All: An Idea Whose Time Has Come and Gone》中就预言:如果关系型数据库试图对所有人都面面俱到,它们就会变得臃肿。

当我提到把所有东西都塞进 Postgres 导致性能下降时,我是在呼应 Stonebraker 的论点。今天,许多团队把大量 JSON 负载倾倒进 JSONB 列,只是为了避免管理一个 NoSQL 数据库。但他们忽略了一个可怕的细节:TOAST(超大属性存储技术)。

当你执行一个看似无害的查询,而你的 JSON 负载超过 8KB 页面大小时,Postgres 会把它压缩并分块存入 TOAST 表。在高流量系统中不断读写这些巨大的 JSON,会把你心爱的 shared_buffers 变成垃圾场。需要留在内存中的关键索引会被驱逐(缓存驱逐)。结果呢?仅仅因为你决定把日志存成 JSONB,你核心应用的登录界面突然要好几秒才能加载。

不要再因为糟糕的数据建模而责怪 Postgres。

1. 多区域写入与逻辑复制的黑暗艺术

我在第一篇文章中最大的不满是写入瓶颈。《数据密集型应用系统设计》这本数据工程圣经的作者 Martin Kleppmann 曾就多主复制说过一句名言:多主复制是一门黑暗艺术;冲突解决会让你彻夜难眠。

这正是我们羡慕 CockroachDB 或 TiDB 这类系统的原因:毫不费力的多主能力。但在 PostgreSQL 16 中,这门黑暗艺术被照亮了。我们不再需要依赖 Citus 这类第三方扩展来做 OLTP,因为它们会引入网络开销和分布式锁延迟。我们可以用逻辑复制原生构建 主动 - 主动(Active - Active)架构。

它如何工作?

想象你有两个接收写入流量的主节点:一个在欧洲,一个在美国。在旧式设置中,如果你建立双向复制,从 A 到 B 的数据会从 B 弹回 A,形成无限循环,几秒钟内就会让系统崩溃。

PostgreSQL 16 中的游戏规则改变者是源过滤(Origin Filtering)。数据库现在能识别一条 WAL(预写日志)记录是本地产生的,还是来自订阅的。通过使用 CREATE SUBSCRIPTION ... WITH (origin = none) 参数,我们原生防止了这种无限循环,且没有额外开销。

当然,为了避免主键冲突,我们必须放弃 SERIAL 或标准 IDENTITY 列,采用按时间排序的 UUIDv7 格式。但归根结底,我们是利用 Postgres 自身的原生能力打破了主节点瓶颈。

2. AI 开销:pgvector 数学与 HNSW 演进

让我们谈谈房间里的大象:AI 集成。我前面提到,把 OpenAI 的 1536 维 embedding 嵌入塞进经典表,并和 ACID 事务混在一起,会拖垮性能。

这背后有一个硬件现实。单个 1536 维向量大约占用 6 KB(4 字节 × 1536)。如果你只有 100 万个向量,那大约是 6 GB 原始数据。再加上索引开销,把它全部留在内存中需要巨大的内存 footprint。

逃向 Pinecone 这类黑盒向量数据库是走捷径。真正的工程意味着隔离工作负载。

对于向量索引,IVFFlat 时代(你需要不断重新计算聚类的时代)已经结束。得益于 pgvector 的 HNSW(分层可导航小世界图)支持以及 Postgres 16 的高级 I/O 操作,我们可以实现基于内存的极快相似性搜索。然而,构建 HNSW 索引需要比标准 B-Tree 多得多的内存。如果你不积极调优 maintenance_work_mem(例如根据硬件设为 4GB 到 8GB),构建该索引会花上数小时。

我们在生产中如何解决?我们从不在处理账单或用户注册的主节点上查询向量。我们会启动专用的 AI 只读副本,其 CPU 针对数学计算优化,work_mem 和 shared_buffers 被推到极限,专门用于向量相似性搜索。你不会把马拉松选手和芭蕾舞者放在同一个舞台上;你要给他们不同的舞台。

3. 工程税:现代代理与无服务器地狱

仍有团队因为依赖 pgbouncer 而困在连接池瓶颈中。PgBouncer 曾是其时代的王者,但它有一个致命缺陷:它是单线程的。

你可以租一台 16 核的大型数据库服务器,但坐在它前面的 pgBouncer 只会用单核来路由流量。在当今的无服务器世界(AWS Lambda、Vercel Edge functions)中,当数万条微连接同时涌入(连接风暴)时,那个单核会达到 100% CPU。你的数据库利用率只有 5%,闲置在那里,而 pgBouncer 却在抛连接超时错误。

我们必须迁移到现代的多线程代理层,比如 Odyssey(由 Yandex 开发)或 Supavisor(专为现代无服务器边缘架构构建)。你不能用中世纪的剑去打 2026 年的激光战。

4. 被遗忘的瓶颈:NVMe 时代的 Autovacuum 喜剧

既然我们深入架构,就来谈谈我们大多数人忽略的沉默性能杀手:Autovacuum。

Postgres 清理死元组的机制至关重要。然而,默认的 autovacuum_vacuum_cost_limit(通常是 200)是为 2005 年的旋转磁硬盘(HDD)设计的。它把 I/O 限制到爬行速度(大约 20 - 30 MB/s),以避免憋死磁盘。

今天,你把 Postgres 部署在能每秒读写数 GB 的 NVMe SSD 上,但 Autovacuum 仍然以为自己活在 HDD 时代。结果呢?大规模表膨胀、索引碎片化、查询速度暴跌。在现代架构中,把这些限制乘以 10 倍或 20 倍(提高 cost_limit、降低 cost_delay),是让你的系统喘口气的第一步。

离开键盘,走进厨房

在自媒体讨论概念、架构哲学和识别瓶颈总是很有趣。但在凌晨 4 点,当生产系统故障时,哲学救不了你,配置文件才能。

把这些概念转化为生产是另一头完全不同的野兽:

  • 使用 PostgreSQL 16 源过滤启动主动-主动多主设置的确切配置命令是什么?
  • 你如何编写 Kleppmann 警告过我们的、基于时间戳的冲突解决逻辑?
  • 你如何针对你特定的 OpenAI 嵌入数据集,对 HNSW 参数(m 和 ef_construction)进行基准测试和优化?
  • 你如何通过部署多线程 Odyssey 代理来绕过 max_connections 限制?

我已经把这些深度技术问题的答案,连同完整拓扑图、分步设置命令和经过实战检验的配置文件,做好了整理。

Postgres 没有死。我们只是终于学会如何为一位 40 岁的巨人锻造新的铠甲。技术厨房见。

参考

Is PostgreSQL dead? (Part II)