所有文章

2026-09-24

· Dylan Yu
MySQLPostgreSQLdatabasearchitecture

MySQL vs PostgreSQL:2026 年的实用决策框架

MySQL 和 PostgreSQL 都很成熟、都很出色,也都仍然值得你刻意去选择。这里有一套诚实的框架,帮你在两者之间做选择——各自真正胜在哪里、在生产环境里什么会咬你一口,以及什么时候答案干脆就是"两个都用"。

这场争论的两边我都站过,也亲眼见过它往两个方向都走得很难看。

PostgreSQL 阵营会告诉你这根本不成问题——MySQL 是遗留数据库,Postgres 才是正确选择,别纠结了。MySQL 阵营会告诉你 Postgres 又慢又过度设计,适合业余玩家,一上负载就拉胯,而 MySQL 已经撑起半个互联网二十年了,那还有什么可争的?

这两种说法都很偷懒,而且来自同一个地方:这些人深度用过其中一个数据库,另一个却几乎没碰过。如果你在 Postgres 里泡了五年,MySQL 的怪癖看起来就像设计缺陷。如果你在 MySQL 里泡了五年,Postgres 的繁文缛节看起来就像额外开销。两个人都没说谎,但谁也没给你完整的画面。

诚实的表述是这样的:MySQL 和 PostgreSQL 都很成熟、都经过实战检验,也都能扛起严肃的生产负载。2026 年真正重要的差异比网上暗示的要窄——但它们是真实的,而且对应到你工作负载、团队和部署环境的具体情况上。

所以我们就真的把它们过一遍。不是当作记分牌,而是当作一组你可以针对自己具体情况做出的决策。我会告诉你各自赢在哪里、各自在生产环境里哪里会让你意外,以及如果你是那个必须长期和它相处的人,该怎么思考这个选择。

两种架构,以及为什么它们的差异没你想的那么大

先从它们的共同点说起,因为这比舆论暗示的要多。

MySQLPostgreSQL 都是客户端-服务器的关系型数据库管理系统。两者都作为独立的守护进程运行,监听网络端口,使用一套通信协议,认证客户端,并通过连接提供 SQL 服务。两者都用 MVCC(多版本并发控制)实现 ACID 事务,这意味着读取者不阻塞写入者,写入者也不阻塞读取者。两者都支持外键、索引、触发器、存储过程、视图和复制。两者都是开源的。两者背后都有数十年的生产历史,也都在各大云上提供托管服务。

如果你是从 SQLite 过来的——那里数据库是一个链接进你进程的库——这就是共同的起点。两者都预期自己住在一台保持开机的机器上,按计划备份,并通过网络被连接。你已经知道的关于运维数据库服务器的一切,对两者都适用。

这个共同的基础正是这个问题如此难分伯仲的原因。你不是在两种计算哲学之间选择。你是在同一个理念的两种实现之间选择,而不同的优先级被烘焙进了细节里。

而真正的差异就活在细节里。我会把它们归结为四个方面:

  • 类型系统。 PostgreSQL 自带一套丰富得多的原生类型——数组、范围、几何类型、JSONBUUID、网络地址类型等等——还允许你定义自己的类型。MySQL 的类型系统更窄,历史上对存储的内容也更宽松。
  • 扩展模型。 PostgreSQL 就是为在进程内被扩展而设计的。你可以加载像 PostGIS 或 pgvector 这样的扩展,在数据库层获得全新的能力。MySQL 有一套插件系统,但那不是同一种生态。
  • 默认值。 PostgreSQL 默认严格——类型不匹配会报错,约束被强制执行,你必须主动选择退出正确性。MySQL 历史上默认宽松,你必须主动选择进入严格模式。
  • 生态。 MySQL 是互联网很大一部分的默认数据库——尤其是 PHP、WordPress 和共享主机。PostgreSQL 是另一部分的默认——分析、地理空间,以及想在数据库内部做更多事的团队。

注意,这些没有一条是"其中一个更快"。两者都快。两者的查询规划器都好到让你的 schema 设计和索引比选了哪个引擎更重要。如果你是因为原始性能在两者之间做选择,那你优化错了东西——我稍后会回到这一点。

这篇文章剩下的部分,就是把这四个方面变成一个你站得住脚的决策。

MySQL 胜在哪里

先从 MySQL 说起,因为"MySQL 是遗留的"这种说法抹掉了很多真实的优势。

无处不在与托管

这是 MySQL 最大的结构性优势,而且甩开对手很远。如果你租一台服务器或一个共享主机方案,MySQL(或它的分支 MariaDB)几乎肯定已经装好并配置好了。如果你在大多数云服务商上开一个托管数据库,MySQL 是列表里最靠前的选项之一。如果你在用某个框架或 CMS,它的默认数据库十有八九就是 MySQL。

这种无处不在有复利效应。每个主机商都知道怎么跑它,每个系统管理员都运维过它,每一篇"怎么把我的应用连到数据库"的教程都有一个 MySQL 标签页。当你选择 MySQL,你是在海量部署环境里选择了阻力最小的那条路——而这种摩擦的减少值真实的时间和金钱。

PHP 和 WordPress 生态

WordPress 跑在 MySQL 上。Drupal、Joomla、Magento,以及支撑着互联网相当一部分的一长串 PHP 应用也一样。如果你的工作涉及其中任何一个——写插件、维护网站、迁移内容、调试主题——不管你选没选它,你都会和 MySQL 打交道。

这不是小众。单是 WordPress 就跑了惊人比例的网站,围绕它的整个生态——主机、插件、代理公司、自由职业者——都建立在 MySQL 之上。如果那就是你工作的世界,选择 Postgres 意味着在每个项目上都要和生态对着干。这里选 MySQL 是对的,不是因为它在技术上更优越,而是因为它就是你所工作平台的原生语言。

简单的读多工作负载

对于一个传统的 web 应用——请求进来,你读一些行,渲染一个页面,偶尔写入——MySQL 非常出色,而且它已经为这种模式调优了几十年。它是一匹干活的老黄牛。读多的 CMS、产品目录、会话存储、内容站点:MySQL 处理这些全都不在话下。

MySQL 赢得"快"这个名声的原因大体如此:它很早就被激进地针对主导互联网的读多、高连接数的 web 工作负载做了优化。并不是说 MySQL 本质上比 Postgres 快——在很多工作负载下并非如此——而是它在构建时就把常见的 web 场景放在心上,这在默认值里看得出来。

熟悉度与工具

会 MySQL 的人比会 Postgres 的人多。这是一个招聘考量、一个入职考量,也是一个"凌晨三点它崩了我该问谁"的考量。MySQL 的工具生态极其庞大——每个 GUI 客户端都支持它,每个 ORM 都支持它,每个云服务商都提供它,而且你碰到的每一个错误信息背后都有二十年的 Stack Overflow 答案存档。

对于没有专职数据库专家的小团队,MySQL 的熟悉度是一个真正的运维优势。最好的数据库就是你的团队能自信运维的那个。

PostgreSQL 胜在哪里

现在说另一边,同样诚实。

严格性与正确性

PostgreSQL 的默认姿态是:数据库应当保护你的数据不受你的应用伤害。类型被强制执行。约束不可商量。如果你试图把字符串插入整数列,你会得到一个错误,而不是一次静默的强制转换。如果你声明了外键,它就会被强制执行。如果你声明了 NOT NULL,它就真的意味着 NOT NULL

这比它听起来应有的分量更重要。在 MySQL 里,历史上的默认是宽松的——数据库会接受有问题的数据,以后再想办法。这已经变了;现代 MySQL 的默认严格得多,你也可以配置严格程度。但文化上的继承依然存在:MySQL 有很长一段应用依赖数据库宽容的历史,也有一段很长的数据因此被悄悄搞坏的历史。

如果你想让数据库成为数据完整性的最后一道防线——而不是信任每一条应用代码路径都正确——PostgreSQL 的严格就是一种功能。那是数据库在履行它的职责。

更丰富的类型

PostgreSQL 自带一些 MySQL 要么没有、要么处理得没那么好的原生类型。最值得一提的几个:

  • JSONB — 带索引支持的二进制 JSON。你可以存一个文档,并用 GIN 索引深入查询它,让嵌套查找变快。MySQL 也有 JSON 类型,而且它能力不弱,但配上恰当索引的 PostgreSQL JSONB 对文档形状的数据来说,是另一个层级的工具。
  • 数组 — 一个真正的数组类型,不是逗号分隔的字符串。你可以把列表存在一列里并给它建索引。
  • 范围int4rangetsrange 之类。对预订时间窗、有效期、排期这类场景真的有用,在这些场景里"这个区间是否和那个区间重叠"是你想自然表达的一个查询。
  • 几何类型和网络类型 — 点、线、多边形、inetcidrmacaddr。如果你的领域碰到其中任何一个,把它们做成原生类型是真正的便利。
  • UUID — 原生,带生成函数。

而且关键在于,PostgreSQL 允许你定义自己的类型和复合类型。如果你的数据有内置类型捕捉不到的结构,你可以把它建进 schema,而不是把它编码成字符串、再在应用里解析。

扩展

这是 PostgreSQL 最与众不同的优势。因为它是为在进程内被扩展而设计的,一整套能力生态以可加载扩展的形式生长了出来:

  • PostGIS — 地理空间工作的黄金标准。如果你要认真做任何和地图、距离或地理查询相关的事,PostGIS 就是选择 Postgres 的理由,句号。
  • pgvector — 向量存储和相似度搜索,它已经成为 AI 和基于 embedding 的应用的基础。
  • pg_trgm — 基于 trigram 的模糊文本匹配。
  • hstoreuuid-ossppgcrypto,以及更多。

这里的模式是:数据库本身可以长出新的能力,而无需你改动架构。如果你的应用需要一种活在数据库内部的能力——这么做的应用比人们以为的要多——PostgreSQL 更有可能已经有了它,或者有一个成熟的扩展来提供它。

高级索引

两个数据库都很好地支持 B-tree 索引。PostgreSQL 走得更远。它为复合数据和全文数据提供 GIN 和 GiST 索引,为大型顺序排列表提供 BRIN 索引,还有部分索引(只索引匹配某个条件的行)、表达式索引(索引一个计算值)以及 bloom filter。你可以在一个函数上、一个 JSON 路径上,或一个行子集上建索引。

实际效果是:更多的查询形状可以被变快,而无需重构你的 schema。当你撞上性能墙时,PostgreSQL 给了你更多可以拉的杠杆,然后才需要去反规范化。

窗口函数和 CTE

两个数据库在现代版本里都支持窗口函数和公共表表达式(CTE)。PostgreSQL 的实现历史上更完整、被使用得更广,而且 Postgres 周围的文化更用力地倾向"用 SQL 表达它"。递归 CTE、LATERAL 连接和高级窗口框架,是 Postgres 用户经常伸手去用的东西。

如果你在数据库内部做大量分析——而不是导出到一个单独的数仓——这很重要。在你规模大到用不了它之前,Postgres 长期同时充当你的操作数据库和分析数据库都很舒服。

生态的广度

除了扩展之外,PostgreSQL 已经成为某类团队的默认选择:那些想把逻辑推进数据库、在乎正确性、并在把它作为一等产品提供的服务商的托管 Postgres 上构建的团队。那个社区产出了一个深厚的工具、指南和模式生态。

如果你想让数据库不只是一个笨存储——如果你想让它成为你应用逻辑的积极参与者——Postgres 就是为那种世界观而建的。

在生产环境里真正咬你的是什么

这是大多数对比文章都会跳过的一节,也是最重要的一节,因为生产环境里咬你的差异,很少是营销里的那些。

字符集与排序规则

MySQL 的字符集和排序规则的故事历史上一直是真实痛苦的来源。较老的 MySQL 版本默认用 latin1;向 utf8mb4 的过渡(让人困惑的是,它才是真正处理完整 Unicode 的编码——MySQL 的 utf8 历史上是一个不完整的实现)花了数年时间,也弄坏了不少东西。排序规则决定字符串如何排序和比较,而 MySQL 有一大堆令人困惑的排序规则,它们的名字并不总能让行为变得一目了然。

PostgreSQL 的故事更简单、更可预测。你在创建数据库时选一个编码和一个排序规则,它就行为一致。它也有自己的微妙之处——排序规则行为可能随操作系统的 locale 库而变化,这在升级时制造过意外——但它没有那么多雷区。

如果你要存储多语言的用户生成文本,这值得在你下定决心之前搞清楚。

标识符大小写与引号

MySQL 在很多平台上对标识符大小写不敏感,取决于底层文件系统,它允许你写 SELECT * FROM Users,无论表叫 users 还是 Users 都能用。PostgreSQL 把不加引号的标识符折叠成小写,所以 SELECT * FROM Users 会去找一张名为 users 的表——如果你的表实际叫 "Users"(用引号创建的),这个不加引号的查询就找不到它。

这是一个经典的迁移陷阱。对 MySQL 运行良好的代码——自由混用大小写——到了 Postgres 上就崩,因为标识符不会按开发者预期的方式解析。到处都要自律地使用小写、不加引号的标识符,但如果你在移植一个已有的应用,这一条你会切实感受到。

事务与 DDL 行为

两个数据库都支持事务。但历史上,MySQL 的存储引擎在 DDL——数据定义语言,比如 CREATE TABLEALTER TABLE——方面的行为不同:很多 DDL 语句会引发隐式提交,无法回滚。现代 MySQL(用 InnoDB)改进了事务性 DDL,但遗留行为塑造了一代迁移工具和期望。

PostgreSQL 很早就支持事务性 DDL:你可以在一个事务里运行一系列 schema 变更,如果中途某处失败,整个事情都会回滚。对迁移来说,这是一个重要的安全属性。在 Postgres 里一个失败的迁移会让你的 schema 保持原样;在较老的 MySQL 里,它可能让你迁移到一半就卡住。

如果你的部署流程把 schema 迁移作为发布的一部分来运行,就要搞清楚当迁移中途失败时,每个数据库的行为如何。这类事情是要吃过大亏才学得会的。

JSON 处理

两个数据库都支持 JSON,能力都不弱。但模型在重要方面有所不同。MySQL 的 JSON 类型以二进制格式存储 JSON,支持基于路径的查询和生成列。PostgreSQL 的 JSONB 存储一种分解后的二进制表示,直接用 GIN 支持索引,而且它的 JSON 操作符与查询规划器集成得更深。

实际差别是:在 Postgres 里,深入查询一个 JSON 列可以通过索引作为一等操作变快。在 MySQL 里,你经常最终要创建生成列并给它们建索引,这能行,但更繁琐。如果 JSON 是你数据模型的核心,这是人体工程学上一个有意义的差别。

连接上限与连接池

两个数据库的连接数都是有限的,把连接用尽都会让它们趴下。失败模式在风味上不同,但本质上一样:连接太多,你就会得到错误、延迟飙升,或者一个不再接受新客户端的数据库。

这不是选这个而非那个的理由——而是无论哪种情况都要规划连接池的理由。两个生态都有成熟的池化器,而且两者通常都部署在一个池化器后面。如果你从高并发的 serverless 环境连接,不管你选哪个数据库,你都会想要一个池化器。不要把默认连接上限当作生产设置。

升级与迁移路径

两个数据库都有被充分理解的升级路径,而且跨大版本跳跃时都需要规划。两者都不是"跑一下然后祈祷"的情况。读一读你要跨越的版本的发布说明,用你数据的副本测试升级,并为说明里警告你的事情预留时间。

两个数据库之间迁移的方向也值得想一想。从 MySQL 迁到 Postgres 是一条被踩得很熟的路,有成熟的工具和记录在案的坑——但它不是一个"点导出、点导入"的操作。类型、排序规则、大小写,以及厂商特定的 SQL 都需要关注。如果你在考虑迁移,把它当作一个项目,而不是一个任务。

对比表

这是并排对比,附带一句提醒:任何单独一行里的"胜出"很少能决定整个问题。

维度MySQLPostgreSQL
架构客户端-服务器 RDBMS客户端-服务器 RDBMS
默认姿态历史上宽松,现在更严格默认严格
类型系统常规类型,支持 JSON丰富类型:JSONB、数组、范围、UUID、几何
扩展模型插件系统进程内扩展(PostGIS、pgvector……)
索引B-tree、全文、空间(因引擎而异)B-tree、GIN、GiST、BRIN、部分、表达式
窗口函数 / CTE支持支持,广泛使用,更完整
JSONJSON 类型,生成列JSONB 带原生 GIN 索引
标识符大小写在很多平台上不区分大小写把不加引号的折叠成小写
事务性 DDL用 InnoDB 有所改进,历史上受限长期支持
字符集历史上很乱(latin1utf8mb4更简单、一致
复制内置,广泛部署内置流式 + 逻辑
生态PHP、WordPress、共享主机、web 应用分析、地理空间、注重正确性的团队
托管服务到处都有到处都有
工具庞大、通用大、成熟
最适合web 应用、PHP/WordPress、无处不在的托管复杂数据、扩展、严格完整性
Basevolt 支持

对那张表的诚实解读:MySQL 赢在无处不在、托管,以及和 web 的生态契合度。PostgreSQL 赢在类型丰富度、可扩展性、严格性和高级查询能力。其他一切都很接近,你的团队的熟悉度应该能决定胜负。

一个实用的决策框架

与其给你一个你会无视的流程图,不如看这些真正决定答案的问题。针对你的情况诚实地回答它们。

1. 你现有的技术栈默认用什么?

如果你在用 WordPress、Drupal、Magento,或某个生态建立在 MySQL 上的 PHP 框架,默认就是 MySQL,和它对着干会在每个项目上耗费你的时间。如果你的技术栈的工具倾向 Postgres——或者你是没有任何强牵引的绿地项目——那就倾向 Postgres。阻力最小的路是一个正当的因素,不是逃避。

2. 你需要一种活在数据库里的能力吗?

地理空间查询?AI 功能的向量搜索?带恰当索引的丰富文档查询?用窗口函数和递归 CTE 做的高级分析?如果其中任何一个答案是肯定的,那种能力大概就应该决定你的选择——而且十有八九它指向 PostgreSQL。

3. 你有多需要数据库来强制正确性?

如果你想让数据库成为数据完整性的最终权威——不管应用做什么都强制执行类型、约束和关系——PostgreSQL 的严格默认是一个有意义的优势。如果你的应用层是事实来源、数据库只是存储,MySQL 更宽容的姿态就没那么让人担心(而且现代 MySQL 反正对大多数用途已经够严格了)。

4. 谁来运维它?

如果你有一个深度了解 MySQL 的团队、却没人了解 Postgres,那就是切换的真实成本。如果你的团队对两者都自在,技术上的优点就更有分量。你的团队能自信运行、备份和调试的数据库,比一个边际的功能优势更值钱。

5. 你到底是在选择,还是在继承?

很多问这个问题的人其实是在继承一个数据库——它已经在运行、已经在生产里,问题实际上是"我该不该迁移?"迁移昂贵、有风险,而且很少仅凭功能羡慕就能被证明合理。如果 MySQL 在正常工作,你的工作负载也不需要 Postgres 独有的东西,就留下。如果你撞上了一堵只有 Postgres 能翻过的墙,那就刻意地迁移,并为它预留预算。

如果你对问题 1、4、5 回答了"MySQL",对 2 和 3 回答了"不",就用 MySQL。如果你对 2 或 3 回答了"Postgres",就用 PostgreSQL。如果确实打平了,就选你团队更懂的那个,一年后再回来看——两者都好到"错误但熟悉"的选择胜过"正确但陌生"的选择。

两个都用

很多有经验的团队实际上是这么做的:他们两个都用,而这个选择并不是互联网把它说成的非此即彼。

我见过的常见真实世界配置:

  • PostgreSQL 作为主操作数据库,MySQL 用于遗留组件或 WordPress 组件。 从 WordPress 站点成长起来的公司常常为 CMS 保留 MySQL,把新服务跑在 Postgres 上。两个数据库,两份工作,一个团队。
  • MySQL 用于事务型 web 应用,Postgres 用于分析。 MySQL 服务请求路径;一个副本或 ETL 管道喂给 Postgres,分析查询和像 PostGIS 或 pgvector 这样的扩展住在那里。
  • MySQL 用于应用,SQLite 用于本地缓存和边缘。 如果你把读取分发到边缘,你可能让 MySQL 作为事实来源,让 SQLite 副本靠近用户——这个模式我在 SQLite vs PostgreSQL 里写过。
  • Postgres 用于所有新的,MySQL 用于所有旧的。 务实的避免迁移策略:不要拆掉还能用的东西,但别再往上加。

这些配置里的摩擦从来都不是数据库本身——而是工具。你最终会有一个 MySQL 的管理面板和一个完全不同的 Postgres 面板,两套连接信息,而且没办法同时看两边。开发者仅仅是在数据库工具之间切换上下文,就浪费掉惊人量的时间。

这也是我们构建 BaseVolt 让它通过同一个界面处理两个引擎的原因之一。把它指向一个 MySQL 连接或一个 Postgres 连接,无论哪种你都会得到相同的管理面板——网格、画廊、看板和 dashboard 视图、关系可视化,以及不改动你 schema 的行内编辑。对同时跑两者的团队,这意味着一个工具而不是两个,以及在你试图理解数据时只需看一个地方。

如果你在专门为其中一个引擎权衡管理工具,我另外写过 MySQL 管理工具对比,而如果你是从一个通用客户端过来的,值得了解一下继续用 DBeaverTablePlus 相比一个专门构建的面板,你会放弃什么。

常见错误

一个简短的清单,来自我看过的那些出问题的事。

为一个 WordPress 项目选 Postgres。 你会把整个项目花在和一个假定 MySQL 的生态对着干上。用 MySQL,或者明知代价地接受这笔税。

为一个需要 PostGIS 或 pgvector 的工作负载选 MySQL。 如果那种能力是你产品的核心,别试图用 MySQL 里的变通办法来假冒它。选拥有那个工具的数据库。

因为 Postgres "更好"而迁移。 功能羡慕不是迁移计划。当你撞上一堵具体的墙时再迁移,并为它实际所需的项目量预留预算。

假定 MySQL 宽松的默认值还是默认值。 现代 MySQL 比它的名声严格得多。检查你的实际配置,而不是重复 2012 年的建议。

把任一数据库的默认连接上限当作生产就绪。 两者在真实负载下都需要连接池。这不是一个区分点——而是一个共同的坑。

根据你在网上找到的基准测试做选择。 基准测试测量的是基准测试的工作负载,不是你的。你的 schema、索引和访问模式才是主导。

直到迁移那天才理会大小写和排序规则的差异。 如果你将来有可能在两者之间移动,现在就写小写、不加引号的标识符,并想想编码。现在做是免费的,事后修是昂贵的。

结论

MySQL 和 PostgreSQL 都很出色、都很成熟,也都能在 2026 年运行严肃的生产系统。真正重要的差异比争论暗示的要窄,而争论通常是被那个人用得更多的那个所驱动的。

简短版:

  • MySQL:当你身处 PHP/WordPress/web 托管的世界,当无处不在和生态契合度重要,当你想在传统的读多 web 工作负载上走阻力最小的路,以及当你的团队很懂它时。
  • PostgreSQL:当你需要更丰富的类型、像 PostGIS 或 pgvector 这样的扩展、在数据库层强制执行的严格完整性、高级索引,或依赖窗口函数和 CTE 的分析时。

这两个清单都不是"正确答案"。它们是对不同问题的不同答案,而真正的技能是知道你到底在问哪个问题。

而对很多团队来说,诚实的答案是你会最终两个都有——MySQL 用在生态牵引你的地方,Postgres 用在能力牵引你的地方——这正是为什么一个两者都懂的管理面板值得拥有。BaseVolt 直接连接 PostgreSQL、MySQL、SQLite 和 Cloudflare D1,完全在你的机器上运行,并把你的凭据留在本地。demo.basevolt.app 有在线演示,或者试试免费档——两个数据源,无需账号。


在用 MySQL 或 Postgres 做东西?我很好奇你选了哪个、是什么让它倾斜——在 X 上找我。

BasevoltBasevolt

试试 Basevolt —— 免费的本地优先数据库管理面板,支持 PostgreSQL、MySQL、SQLite 和 Cloudflare D1。

免费下载 Basevolt