为什么我最终放弃了 MySQL 8.0?一次真实的性能对比
说实话,我用了七八年 MySQL,从 5.6 一路用到 8.0,自认对它了如指掌。但上个月从零搭建一个数据密集型平台时,技术群里争论用 MySQL 还是 PostgreSQL,两边各执一词。我干脆在两台规格完全一样的服务器上做了压测,原以为 MySQL 靠 InnoDB 的缓冲池在写密集场景会领先,结果却彻底改变了我的偏见。这次压测不是简单的跑分,而是直接模拟我们的业务负载:订单表更新、JSON 查询、还有几十个并发事务。过程里遇到很多坑,所以记录下来供大家参考。
测试环境与方法
- 服务器:双路 E5-2680 v4,32 核 64G,NVMe SSD(Intel P4610)
- 系统:Ubuntu 22.04.2 LTS,内核 5.15
- MySQL 8.0.32,PostgreSQL 16.2,均使用官方 apt 源
- 客户端:sysbench 1.0.20(oltp_read_write.lua),每库 10 张表,每表 100 万行
- 数据量:约 15G
- 调优参数:MySQL 设
innodb_buffer_pool_size=24G,innodb_log_file_size=4G,innodb_flush_log_at_trx_commit=2,binlog_format=ROW;PG 设shared_buffers=16G,effective_cache_size=48G,work_mem=64M,maintenance_work_mem=2G,并启用了max_wal_size=32G,synchronous_commit=off。压测时间为 10 分钟,冷却 5 分钟后再跑下一轮。
第一轮压测结果:MySQL 平均 TPS 6820,PG 为 7160,PG 稍微领先。这里的差距在误差范围内,但后续场景让我彻底改了想法。
JSON 和索引:MySQL 的隐痛
我们的业务大量使用 JSON 存灵活的字段,比如用户画像。MySQL 的 JSON 类型虽然实现了 data->>'name' 这种语法,但如果你想给这个字段建索引,必须得像这样先生成虚拟列:
ALTER TABLE customers ADD COLUMN name VARCHAR(50) GENERATED ALWAYS AS (data->>'name') STORED;
CREATE INDEX idx_customers_name ON customers(name);
这样不仅改变了表结构,而且每个新字段都要手动加一列。PostgreSQL 可以直接建表达式索引:
CREATE INDEX idx_customers_name ON customers ((data->>'name'));
不需要改表结构。我在 100 万行数据上跑相同查询,MySQL 热查询平均 12 毫秒,PG 只有 8 毫秒。更关键的是,PG 的 jsonb 支持 GIN 索引,可以直接索引整个 JSON 文档,然后用 @> 操作符做包含查询。MySQL 要做一个全 JSON 的索引就没有这么直接。实际写查询时,PG 的 jsonb 配合 @> 是真正的杀手锏,性能差距从 2 倍到 10 倍都有可能。
并发和锁:80% 的人都忽略的坑
很多人以为 InnoDB 的行级锁比 PostgreSQL 的多版本控制更加“高性能”,但我用 100 个并发客户端跑了一个混合事务:每个事务先 SELECT 后 UPDATE 加一个 WHERE 条件。运行 5 分钟,MySQL 的 deadlock 次数到了 67 次,而 PostgreSQL 一次都没出现。原因是 MySQL 的 RR(可重复读)隔离级别下间隙锁导致锁范围扩大,而 PG 的 MVCC 实现天然避免读写相互阻塞。当然,这不是说 PG 没有锁冲突,但确实在高并发下更稳。另外要注意 PG 的 vacuum 需要持续维护,如果 autovacuum 设置不当,会导致表膨胀和性能回退,这个运维成本被很多文章选择性忽略了。
备份恢复:一条命令的差距
备份是生产环境最容易翻车的地方。以前用 MySQL 时,我习惯用 xtrabackup 做物理备份。200G 的数据,全量备份大约 25 分钟,恢复要 15 分钟,而且恢复时还得处理 binlog 的 position 对齐。PostgreSQL 自带 pg_basebackup,同样数据量备份 18 分钟,恢复 8 分钟。更重要的是,PG 的 pg_rewind 可以快速将老主库拉回集群,而 MySQL 在这个场景下要么重建实例,要么手工跳过错,整个过程繁琐且容易出问题。
我的独立选择建议
说实话,如果你只是做简单的 Web 应用,用 MySQL 还是 PG 根本感觉不出来差别。但如果你是做数据密集型项目、面对大量 JSON 存储、或者对未来扩展性有要求,我强烈建议考虑 PostgreSQL 16。它的并行查询、分区表、逻辑复制都比 MySQL 8.0 更成熟。当然,MySQL 也有它的优势,比如生态工具更丰富,云厂商的托管优化一般更激进,而且 MGR(MySQL Group Replication)在多主写入场景下比 PG 的原生复制更容易上手。所以我的结论不是“PG 是神”,而是:在 2024 年的节点上,如果你没有历史包袱,选 PG 的性价比更高。回看那些“MySQL 写得快”的传说,在 TPS 差不到 5% 的情况下,PG 在类型系统、索引能力和标准 SQL 支持上的领先,足以让我这个老 MySQL 用户改名换姓了。
(这有点滑稽,但确实是我真实的心路历程。)