我对数据库的第一印象,来自大学的数据库课。
教学用的是 Oracle。Schema、Sequence、Procedure,一套完整的概念体系,从一开始就刻进了脑子里——数据库应该是这样的,严谨的,有层次的,每一个概念都有它明确的边界。
后来工作,第一个项目用的是 PostgreSQL。和 Oracle 同源的设计哲学,schema 隔离、标准 SQL、事务严谨,顺理成章,没有任何认知冲突。我以为所有的数据库大概都是这个样子。
直到接手了那套 MySQL 系统。
那是 2016 年,广告公司,接过来一个已经开发了第一版的业务系统。前任技术负责人是 PHP 背景,数据库用的 MySQL。
打开表结构,第一眼就愣了一下。
所有的表堆在同一个 database 里,靠命名前缀来区分业务——crm_customer、bi_report、pay_order。在 PostgreSQL 里,这件事本来的写法是:
CREATE SCHEMA crm;
CREATE TABLE crm.customer (...);
表名干净,边界清晰,权限可以按 schema 粒度控制。MySQL 没有这个概念,或者说,schema 和 database 在 MySQL 里是同一个东西。于是大家发明了命名约定,用前缀来模拟本来应该由数据库提供的能力。
这只是第一眼。
接着发现存储引擎的问题。
MySQL 的存储引擎不是开箱即用的最佳状态——或者说,"最佳"这件事需要你自己去配置、去选择。MyISAM 不支持事务,InnoDB 才支持,但默认不一定是 InnoDB,取决于版本和配置。一套系统里可能同时存在两种引擎的表,各有各的行为,各有各的坑。
来自 PostgreSQL 背景看到这个,感受很难描述。PG 就是 PG,没有"存储引擎"这个概念需要你操心,事务是默认的,行为是一致的。MySQL 这边,你需要先搞清楚自己在用哪个引擎,才能知道它会怎么表现。
那段时间我脑子里反复出现一个词:杂牌军。
说出来不是贬低。MySQL 支撑了无数企业、无数产品,它的普及本身就是一种伟大——让原本需要专业团队才能驾驭的东西,变成了任何人都能上手的基础设施。这份功劳不应该被轻描淡写。
只是 PostgreSQL 和 MySQL,是不同的产品,不同的品味。
那时候听说豆瓣用 Python 作为主力语言,在一片 Java 和 PHP 的世界里显得有点特立独行——但用 Python 的人之间有一种心照不宣的认同,不需要解释,懂的人懂。用 PostgreSQL 大概也是这种感觉。MySQL 是大众选择,没什么好说的,用就是了。PG 用户相对小众,但彼此之间有一种隐隐的共鸣——不是优越感,是品味上的相认。
和 MySQL 共事了几年,我始终保持着这种隐隐的格格不入。
但赢的逻辑,其实一点都不难理解。
MySQL 到今天依然是世界上用户基础最广的开源关系型数据库之一。不是因为它在技术上最好,而是因为它在正确的时间出现在了正确的地方。
答案在 LAMP 里。
Linux + Apache + MySQL + PHP,这四个字母拼在一起,在 2000 年代初做了一件了不起的事:让"搭起一个 web 应用"这件事,第一次变得触手可及。
在此之前,搭环境是一道高墙。Oracle + WebLogic,没有专职 DBA 和运维,根本别想。LAMP 把这道墙拆了——Windows 上装个 XAMPP,Linux 上几行命令,一个下午,环境好了,数据库好了,可以开始写代码了。
MySQL 就是这样进入了无数人的第一个项目,第一家公司,第一行 SQL。它不是因为最好而被选择,是因为在那个时间点,它就在那里,免费,能用,够用。
这和 2009 年我们用 PostgreSQL 的原因恰好相反——那次是架构师一人钦点,是自上而下的专业判断。MySQL 的流行是自下而上的,是无数个"先跑起来再说"累积出来的用户基础。
两条完全不同的路,都走到了今天。
LAMP 还有一个更深远的影响,是我后来才意识到的。
它降低了开发门槛,让很多人顺利迈过了"搭环境"这道坎——但"在我电脑上能跑"不等于"在所有人的电脑上能跑"。PHP 版本差一点,MySQL 小版本差一点,行为可能就不一样了。环境一致性是个长期的噩梦。
Docker 出现的时候,解决的正是这件事——不只是"让你能跑起来",而是"让所有人在同一个环境里跑起来"。把环境本身变成可以版本控制、可以分发、可以复现的东西。
LAMP 是第一次民主化,Docker 是第二次。
用"伟大"来形容 Docker,我觉得给得起。
那几年用 MySQL,我始终没有真正习惯它。每次写 SQL,脑子里都有一个 PostgreSQL 的影子在比较。
但我也慢慢理解了一件事:技术选型从来不是在真空里做的判断。MySQL 的用户不是选择了一个残缺的产品,他们是在一个具体的时间点,选择了一个能让他们立刻开始的东西。
这和当年在 Mac 笔记本上磕磕绊绊跑 PG 做分析是同一个道理。
不是最好的方案,是当时能用的方案。
还有一件事,是用 MySQL 之后才真正感受到对比的。
2016 年做广告系统,生产库的数据要实时摄入到数仓里。在 MySQL 里,当时最高级的方案是 binlog——解析 MySQL 的二进制复制日志,从里面提取数据变更,再接一套中间件(Canal、Debezium 之类)把它转成可用的格式,送到下游。
这套东西能用,也确实被广泛使用。但整条链路很长,你要理解一个复制协议的内部格式,维护额外的组件,出了问题排查起来费劲。
和 Oracle 比,小儿科。和 PostgreSQL 比,一点都不优雅。
PostgreSQL 里做同样的事,今天叫 CDC(变更数据捕获),但这个能力在 PG 里早就是标配——逻辑复制、流式复制,想用随时用。更让我喜欢的是 pg_notify:给你关心的表加一个触发器,把变更按照你想要的格式组装好,直接 NOTIFY 出去,监听的客户端收到,完事。
消息队列?PG 自己就是。KV cache?PG 的 hstore 和 jsonb 早就在那里。OLTP、实时 CDC、消息通知,一个数据库全包了。
只有你想不到,没有 PG 做不到。
这不是说说而已。就拿那时候的程序化广告系统来说——ad-server 端需要大量的 metadata 近实时同步过来,投放逻辑依赖这些数据,延迟意味着偏差。config server 这个问题有一千种解法,Redis、消息队列、定时轮询,每种都能凑合。
但在广告公司那几年,用的是 MySQL,这件事始终是个悬而未决的工程债。
后来到了金融公司,同样是程序化广告系统,业务库换成了 PG。那个悬了几年的问题,几行代码就解决了:给关键的业务表加触发器,变更发生时用 pg_notify 推出来,ad-server 端监听,收到立刻更新本地缓存。
整套方案没有引入任何额外的中间件,没有 Kafka,没有 Redis,没有另一个需要维护的服务。PG 自己把消息送出去了。
同一个问题,等了几年,等到了合适的工具。
插曲结束,我还是回到了 PostgreSQL。
有些东西,用过之后就回不了头了。
