都 2025 年了,还不试试 PostgreSQL?
一、前言:MySQL 已经成了惰性的象征,而非技术的选择
MySQL 曾经是互联网时代的功臣,它简单、便宜、够快。但到了 2025 年,它更多代表的是行业的惯性与保守。人们继续用它,不是因为它先进,而是因为它“还活着”。然而,如果你把目光放到数据库设计层面,就会发现:MySQL 根本不是一个完整的关系数据库,而是一个带 SQL 外壳的存储系统。
它的事务,只覆盖了一部分数据; 它的 ACID,是有注脚的; 它的优化器,是随缘的; 它的触发器,是残废的; 它的约束,是可关闭的。
MySQL 靠“够用”生存; PostgreSQL 靠“正确”延续。
两者之间的区别,不是实现多少特性, 而是——是否尊重数据库科学本身。
二、优化器:MySQL 靠猜,PostgreSQL 靠算
MySQL 的查询优化器还停留在上个世纪。它基于规则,不基于代价。
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
status VARCHAR(20),
created_at DATETIME,
INDEX idx_user_status (user_id, status)
);
EXPLAIN SELECT * FROM orders WHERE user_id = 42 AND status = 'done';
EXPLAIN SELECT * FROM orders WHERE status = 'done' AND user_id = 42;逻辑上完全相同的两条 SQL:
- 第一条走索引;
- 第二条却全表扫描。
因为 MySQL 优化器死板地按索引定义顺序匹配列,不会重排谓词,也不计算组合代价。稍不注意写法,性能直接爆炸。
而 PostgreSQL 的优化器是真正的代价模型(Cost-Based Optimizer)。它通过列统计与数据分布做决策,执行计划稳定、可预测。
PostgreSQL 的计划是理性计算; MySQL 的计划是随机猜测。
当你的系统需要确定性性能,MySQL 这个优化器就成了炸弹。
三、ACID 的幻觉:回滚之后,MySQL “干净地”抹掉历史
MySQL 的最大谎言,是那句印在官网上的口号:
“InnoDB fully supports ACID transactions.”
这句话听上去像承诺,实际上是选择性履行。遇见 DDL,MySQL 立刻撕毁契约。
看看这一段再平常不过的表迁移事务:
START TRANSACTION;
-- 1. 改名原表
RENAME TABLE metrics TO metrics_1;
-- 2. 按旧结构创建新表
CREATE TABLE metrics LIKE metrics_1;
-- 3. 导入旧数据
INSERT INTO metrics SELECT * FROM metrics_1;
-- 4. 删除旧表
DROP TABLE metrics_1;
-- 5. 写入日志(假设这里出错)
INSERT INTO system_events(event_type, description)
VALUES ('table_upgrade', 'metrics table upgraded');
ROLLBACK;你以为 ROLLBACK 是“回到从前”?
在 MySQL 里,它意味着“请擦干净尸体再走”。
执行之后你会得到:
- 原表
metrics被改名再删除; - 新表
metrics还在,但空无一物; - 数据彻底消失;
- 那条日志既没写入,也没留下痕迹。
你手动发起了回滚,MySQL 则帮你干净地抹掉了历史。
更荒谬的是,这不是 bug,而是官方设计。 任何 DDL(RENAME、CREATE、DROP)都会触发隐式提交。事务还没结束,它就自作主张地写死文件。
结果是:回滚只是摆设,原子性形同虚设。你的事务,不是安全网,而是引爆器。出错时,它炸的不是异常,而是数据本身。
PostgreSQL 的对比:真正的事务,是一次数据库级时间穿越
在 PostgreSQL 中,同样的操作是完全事务化的:
BEGIN;
ALTER TABLE metrics RENAME TO metrics_1;
CREATE TABLE metrics (LIKE metrics_1 INCLUDING ALL);
INSERT INTO metrics SELECT * FROM metrics_1;
DROP TABLE metrics_1;
INSERT INTO system_events(event_type, description)
VALUES ('table_upgrade', 'metrics table upgraded');
ROLLBACK;一旦执行 ROLLBACK:
- 所有表、结构、数据、索引全部回退;
- WAL 写前日志完整重放;
- 系统状态恢复如初,连指针都不漏一页。
这不仅仅是“支持 DDL 事务”,这是数据库哲学分界线的实证:
PostgreSQL 的事务作用于整个数据库状态; MySQL 的事务只作用于部分数据文件。
前者是有意识的系统,它理解什么叫“一致性”“原子性”“时间线”;后者只是一个文件操作堆栈——连“状态”都不懂。
毫不夸张地说:
PostgreSQL 的事务是一个原子行为; MySQL 的事务是一个概率事件。
前者以逻辑守护数据; 后者赌机器别出错。
PostgreSQL 管整个宇宙;MySQL 只管几张文件。 这是工程实现的鸿沟,更是理念上的耻辱。
四、触发器与逻辑:MySQL 的断肢,PostgreSQL 的神经系统
MySQL 到今天的触发体系依旧严重残缺:
- 每个事件只能定义一个触发器;
- 不支持语句级触发;
- 不支持条件触发;
- 调试如地狱,复用无可能。
2025 年,你依然能看到这样的报错信息:
ERROR 1235 (HY000): This version of MySQL doesn't yet support multiple triggers for the same table.而 PostgreSQL 的触发体系,是成熟的事务内逻辑层:
- 行级与语句级触发并存;
- 支持条件判断(
WHEN); - 可指定顺序、共存多个;
- 可用多语言编写(PL/pgSQL、Python、C)。
在 PostgreSQL,触发器能形成业务层逻辑护城河; 在 MySQL,它只是挂件。
五、约束与一致性:MySQL 允许造假,PostgreSQL 不允许撒谎
MySQL 允许你在一行命令里掐死完整性保障:
SET FOREIGN_KEY_CHECKS = 0;然后导入任意垃圾数据。再恢复检查,它也不会重验。 完美的“装死”机制。
PostgreSQL 没有这种妥协选项。 约束永远生效、永远执行。 它不会假装一致 —— 它要么成功,要么拒绝。
更离谱的是,MySQL 在处理外键与复合索引时的行为,完全超出直觉。
假设你有一个主表与子表结构如下:
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
name VARCHAR(50),
INDEX idx_user (user_id, name),
FOREIGN KEY (user_id) REFERENCES users(id)
);理论上,这里的外键只约束 user_id,与 name 无关。但在 MySQL 中,只因为你建立了联合索引 (user_id, name),它会在某些版本(尤其 5.x 与早期 8.0)中无端把第二列 name 一并纳入外键校验逻辑。任何 name 冲突、甚至非唯一匹配,都会触发外键报错。这意味着你根本没定义外键到 name,却被 MySQL 的外键机制“顺手管了”。
PostgreSQL 不会做这种事。 它认得标准:外键依赖主键或唯一约束列,不会跨字段、不会超范围、不会瞎猜。
MySQL 的问题在于:它将“联合索引”视作“外键实现细节”,把数据约束混在索引选型里,最终让行为变得既不确定又难以调试。
PostgreSQL 则严格区分:索引是优化器的工具,外键是一致性的契约。MySQL 把契约和工具绑死在一起。
当你加个索引时,你同时改变了逻辑规则——这不是数据库,这是雷区。
MySQL 教你撒谎;PostgreSQL 逼你诚实。
六、“数据库只是大号 Excel”?
不,那只是被 MySQL 教坏的一代人
有人说:
“逻辑不要放数据库,数据库只是存储。”
这句话听上去理性,其实是悲哀。
悲哀的是:MySQL 把开发者养成了“不信任数据库”的人。
因为:
- 事务不完整;
- 约束能关;
- 触发器假死;
- 优化器靠缘分。
于是程序员被迫把一致性逻辑搬到应用层,再对自己说:“逻辑不应在数据库中做。”
结果就是 ORM、幂等、防重复、补偿机制满天飞。只是因为 MySQL 从未完成应尽职责。
而 SQL 标准在 1992 年就定义过事务 DDL、触发、外键。PostgreSQL、Oracle、DB2 均完全遵守。只有 MySQL 选择自我阉割。
这是 MySQL 的技术原罪。 它不只是“限制”,而是在概念上把数据库降格成表格工具。
当他们说“数据库只是 Excel”,真正的意思是:
“我们用的 MySQL 就像 Excel——能存点数据,却没灵魂。”
七、有人说“事务里做 DDL 不科学”?
不,那只是被 MySQL 吓坏的工程师自我安慰
会有人批评文中这一点,说:
“在事务里执行 DDL 是不科学的设计。”
这句话听起来高冷,实际上是MySQL 弱点导致的错觉。他们不是在阐述数据库原理,而是在为 MySQL 找借口。
事实是:事务中执行 DDL 并不反常,这本应是关系数据库的标准语义。 但因为 MySQL 拉跨,他们被迫认为‘不能做’,然后自我合理化。
他们记住的是 MySQL 的崩溃方式, 于是开始以为数据库就该如此; 于是把“不敢”包装成“原则”; 把数据库降级成“存储层”; 再用代码去弥补数据库应有的功能。
PostgreSQL 没这个问题。 你可以在事务里安全地改表、建表、删表、回滚。 因为它的 DDL 就是事务的一部分, 它不装模作样,不偷偷提交。
而 SQL 标准在 1992 年就已完整确立:
- 标准事务语义(包含 DDL);
- 外键与约束机制;
- 触发器与存储过程;
- 视图、模式、授权体系。
换句话说,在 MySQL 出生之前,SQL 世界已经有了成熟、完整、被各大数据库验证过的科学定义。
但到了 1995 年,MySQL 出场时,它做了一个惊人的选择: 它没有遵守科学,不继承标准,而是自创一套残废版 SQL,命名为——“My SQL”。注意那个 “My”:它不是对个人的归属表达,而是一个注脚
“我只承认属于我自己的 SQL 规则。”
于是它扭曲了语言逻辑、删掉了事务 DDL、阉割了触发器、弱化了外键,再把这些残缺包装成“轻量”“高性能”“互联网友好”。 事实上,那不过是权衡实现复杂度后的技术懒惰与规范背叛。
这就是 MySQL 的原罪:当全世界都在遵守《SQL-92》, 它却在实现 “Only My SQL”。
结果,MySQL 教坏了一代开发者:大家开始认为数据库就是 CRUD 容器;开始把一致性逻辑扔回应用代码;一边疲于修补,一边自诩解耦优雅。
MySQL 不止是技术问题,它是一种文化病毒 ——它让人们习惯无标准、无事务、无尊严的数据库生态, 并且还为此自豪。
所以请停止把 MySQL 当作数据库的定义。 它只是数据库的简化版、阉割版、历史遗迹。
真正科学的,是 PostgreSQL; 真正错误的,是被 MySQL 教坏的世界。
要正确性,就别怕高级。 要数据库,就别怕事务中的 DDL。
MySQL 的恐惧,不该成为你的惯性。 真正的数据库,不需要妥协。
MySQL 教人屈服;PostgreSQL 教人尊重。
八、结语:要正确性,不要幻觉
MySQL 的哲学是:差不多就行。
PostgreSQL 的哲学是:必须正确,否则失败。
| 对比项 | MySQL | PostgreSQL |
|---|---|---|
| 事务原子性 | 仅 DML,DDL 会隐式提交 | 全局原子性 |
| 优化器 | 规则匹配,性能漂移 | 成本模型,推理稳定 |
| 外键与约束 | 可关闭或跳过 | 严格强制执行 |
| 触发器 | 功能残缺 | 事务级完整 |
| DDL 事务 | 不支持,必隐式提交 | 完全支持,可回滚 |
| SQL 标准遵循 | 方言化实现 | 完全兼容 ANSI SQL |
到了 2025 年如果你还在忍受一个:
ROLLBACK会删光你表的数据库;- 允许你关闭外键检查的数据库;
- 靠猜测计划运行的数据库;
那你不是在用数据库,而是在赌数据库别出事故。
MySQL 属于过去 —— 它是“差不多”的象征; PostgreSQL 属于未来 —— 它是“正确性”的代名词。
要么 PostgreSQL,要么幻觉。要么正确,要么坍塌。