MySQL 千疮百孔,为什么没人能替代它
MySQL 的问题不在于它没有坑,而在于它已经成了太多团队的默认值。工程世界里,最难替代的从来不是软件,而是习惯、经验和风险定价。

技术评审会上,最尴尬的不是没人知道 MySQL 有坑。
最尴尬的是,所有人都知道它有坑,最后还是选它。
有人说 PostgreSQL 更严谨,有人说 MySQL 的外键、触发器、枚举、事务边界都要小心,有人甚至能现场背出几个历史遗留问题。会议开到最后,大家把迁移成本、线上经验、云厂商支持、团队熟练度、历史 SQL、ORM 方言、排障手册一摊开,气氛就变了。
问题从“它好不好”,变成“出了事谁负责”。
这才是 MySQL 最有意思,也最残酷的地方。
它当然不完美。甚至可以说,它身上有一堆非常不优雅的历史包袱。但它仍然是开源关系数据库世界里最强的默认选项之一。MySQL 官网到今天还把自己称为 “The world’s most popular open source database”,DB-Engines 这类榜单上,它也长期站在最靠前的位置。
所以真正值得问的,不是“MySQL 到底烂不烂”。
这个问题太容易变成情绪表态。
真正值得问的是:一个到处有补丁、有边界、有反直觉行为的软件,为什么还能变成事实标准?
MySQL 赢的不是优雅,而是默认值。
坑不是最可怕的,悄悄返回结果才可怕
数据库最让人害怕的 bug,通常不是直接报错。
报错至少诚实。它告诉你:这里不行,停下来处理。
更危险的是那种看起来能跑、能查、能返回结果,但行为和你脑子里的模型不一致的东西。你以为自己在和一个严肃的系统打交道,结果它在某些地方像脑筋急转弯。
比如 ENUM。
很多团队会用它表示状态、尺码、等级:
| |
按直觉,S、M、L、XL 是一组字符串。你排序的时候,也许会以为它们会按字母顺序走。但 MySQL 官方文档明确说,ENUM 的排序默认基于它在定义里的索引位置。也就是说,ENUM('b', 'a') 里,b 会排在 a 前面。
这不是没人告诉你。文档里写了。
问题是,业务开发者每天脑子里装的是订单状态、库存、优惠券、用户权限,不是每个数据库类型的历史脾气。一个字段明明长得像字符串,很多时候也像字符串一样被使用,但在排序时又按定义顺序来。你当然可以说“这是文档行为,不是 bug”。
可工程里的危险,恰恰经常藏在这种地方。
它不是错到跑不起来,而是对到需要你记住一条额外规则。
规则一多,团队就会变聪明。
然后聪明人会说:少用 ENUM。
Trigger 的问题,是它动摇了“我以为”
更典型的是 trigger。
触发器本来是一个很好理解的东西:某张表发生 INSERT、UPDATE、DELETE,我让数据库自动做一件事。比如记审计日志、同步状态、更新计数。
直觉上,只要这张表的数据被改了,trigger 就应该触发。
但 MySQL 官方文档里有一句非常硬的说明:
| |
级联外键动作不会激活 trigger。
这句话的杀伤力不在术语,而在直觉。
假设你有一个员工表,里面有部门 ID。部门被删除后,外键规则把员工表里的 department_id 设为 NULL。数据确实变了,行也确实被改了。但这个修改不是你直接 UPDATE employees 产生的,而是外键级联动作带来的。
如果你的审计逻辑依赖员工表上的 trigger,你可能会以为这次变化也会被记录。
MySQL 说:不会。
这不是我脑补的案例。MySQL Bug #11472 早在 2005 年就被提交,标题就是 Triggers not executed following foreign key updates/deletes。原始报告里,删除父表记录后,子表外键列被设为 NULL,但 after update trigger 的计数仍然是 0;直接更新子表时,trigger 又能正常触发。
更讽刺的是,页面后续评论里出现过“我们会在 5.1 修复”之类的说法。多年之后,这件事更像是被文档化成了行为边界。
你当然可以说:既然文档写清楚了,那就按文档来。
没错。
但一个功能一旦需要你不断提醒自己“这里不是你以为的那个语义”,它就不再只是功能。它变成了团队规范、代码评审提醒、事故复盘和新人培训的一部分。
最后大家会得到一句更短、更容易传播的话:
Trigger 别用。
这句话未必公平,但很有效。
为什么这么多坑,大家还是不走
到这里,很容易得出一个痛快结论:既然 MySQL 这么别扭,那就换 PostgreSQL。
我也喜欢这个结论。
它干净,正义,听起来很懂数据库。
可工程世界不是审美投票。工程世界更像一张债务表:你每做一个选择,都在未来某个地方付利息。
MySQL 最大的护城河,不是它没有替代品。
恰恰相反,替代品很多。PostgreSQL 更严谨,扩展能力更强,SQL 语义更舒服;SQLite 在嵌入式和本地场景里非常漂亮;云厂商也在推各种兼容层、新型分布式数据库、Serverless 数据库。
但替代品存在,不等于替代会发生。
因为你要替代的从来不只是一个数据库进程。
你要替代的是:
- 团队十几年攒下来的排障经验;
- 一堆没人敢动的历史 SQL;
- DBA 和后端之间形成的默契;
- 云服务、监控、备份、迁移、审计的整套工具链;
- 面试、教程、外包团队、开源项目默认支持的生态;
- 以及最麻烦的东西:出了事故时,大家知道该找谁、该看哪、该怎么止血。

技术选型里有个常被低估的事实:
最难替代的不是软件,是习惯。
一个团队用 MySQL 用了很多年,它会形成自己的安全区。哪些功能别碰,哪些 SQL 慢,哪些版本有坑,哪些参数要调,哪些告警不用慌,哪些错误一出现就该先看连接池。
这些东西不优雅,但值钱。
PostgreSQL 可能在很多地方更好。可一旦你真要迁移,问题就不是“新数据库好不好”,而是“我们有没有能力把旧世界里的所有隐性知识搬过去”。
很多团队没有。
或者说,有,但不值得。
事实标准通常不是最好用出来的
技术圈有个浪漫误会:好技术会赢。
现实通常没这么体面。
真正赢下来的技术,常常是刚好出现在正确时间、绑定了正确生态、足够便宜、足够容易部署、足够多人会用,然后一路靠惯性滚大。
MySQL 早期吃到了 Web 爆发、LAMP 生态、虚拟主机、开源教程和低门槛部署的红利。对大量网站和业务系统来说,它不是被严肃比较后胜出的,它是“默认就在那里”。
默认值的力量很恐怖。
默认值会进入教程。教程会进入面试。面试会进入招聘。招聘会影响团队能力模型。团队能力模型又会反过来决定下一次选型。
这条链路一旦闭合,一个技术就不再只是技术了。
它变成行业语言。
这也是为什么很多 MySQL 的坑没有真的把它赶下桌。因为用户不是每次都在问“这是不是最优雅的数据库”。更多时候,他们问的是:
这套东西我们能不能招到人维护?
云厂商有没有成熟服务?
线上出了问题,搜索引擎能不能找到答案?
我们现有系统迁过去,要停多久?
老板问风险,谁敢签字?
这些问题听起来没那么技术,但它们决定了技术命运。
数据库选型不是审美投票,是风险定价。
MySQL 的真正危险:把妥协伪装成常识
所以我不想把这篇写成“MySQL 垃圾,大家快逃”。
这太省事,也太不负责任。
MySQL 能用。大量业务系统也确实用得很好。很多团队选择 MySQL,并不是愚蠢,而是现实:项目规模不复杂,团队熟悉,云服务成熟,工具链齐全,成本可控。
这叫工程判断。
真正危险的是另一种东西:把针对 MySQL 的妥协,包装成数据库世界的普遍真理。
比如:
“数据库别管太多,业务逻辑都放应用层。”
“外键没必要,应用代码保证就行。”
“Trigger 都是坑,懂行的人不用。”
“数据库就是存数据,别搞那些花活。”
这些话有时是对的。复杂 trigger 确实会制造隐式副作用;滥用外键确实会让写入路径变重;把业务逻辑全塞进数据库,后期维护也可能很痛苦。
但它们不应该变成宗教。
如果你不用外键,是因为你评估过当前业务、当前团队、当前数据库实现的代价,那没问题。
如果你不用外键,只是因为“我们一直这么干”,那就要警惕了。
你以为自己在做最佳实践。
也许你只是在复读旧伤口。
MySQL 最麻烦的地方,不是它有坑,而是它的坑太普及了。普及到很多人已经分不清:哪些是关系数据库本来就该有的边界,哪些只是 MySQL 训练出来的恐惧。
不要轻易换,也不要轻易信
那到底该怎么办?
我的答案可能不够爽:不要因为讨厌 MySQL 就迁移,也不要因为大家都用 MySQL 就停止怀疑。
如果你在做新项目,团队又有足够能力,认真评估 PostgreSQL 是很值得的。它在类型、约束、事务、索引、扩展能力上,确实给了开发者更多可以信任的东西。
如果你在维护一个已经跑了多年的 MySQL 系统,也别因为几篇文章就热血上头。迁移数据库是大手术,不是换编辑器主题。你要评估数据规模、停机窗口、SQL 方言、索引差异、事务语义、监控备份、回滚预案,还要评估团队有没有足够多的人真的理解新数据库。
更现实的做法,是先把自己的判断从 MySQL 的默认值里拆出来。
下次团队里再有人说“这个功能别用”的时候,我建议先问四个问题:
- 我们不用它,是因为这个能力本身危险,还是因为 MySQL 的实现让它危险?
- 如果约束不放数据库,应用层靠什么保证?代码、流程,还是大家记得?
- 这个经验离开 MySQL 还成立吗?换 PostgreSQL、SQLite、SQL Server 后还成立吗?
- 我们是在做工程取舍,还是在把历史创伤讲成技术真理?
这四个问题,比“支持 MySQL 还是支持 PostgreSQL”更有价值。
因为真正成熟的技术判断,不是骂一个工具,也不是崇拜另一个工具。
而是你知道自己在为什么付钱。
MySQL 千疮百孔,但它不会很快消失。
工程世界里,坏东西不会因为坏就消失。它只会在足够便宜、足够熟悉、足够多人会修的时候,继续活得很好。
所以别把 MySQL 的流行误读成完美。
也别把替代 MySQL 想得太轻。
一个系统最难换掉的部分,往往不是代码,也不是数据。
是人脑里那套默认设置。
如果你喜欢这种不只看热闹、也看工程代价的技术观察,可以关注我。下一次我们继续聊那些“明明不完美,却很难被替代”的东西。