MySQL用惯了?PostgreSQL这3个亮点,高并发项目更胜一筹

大促当天,订单系统的吞吐从原来的 8000 TPS 跳到 2.4 万,平均响应从 800 毫秒降到 250 毫秒,死锁率从 12% 降到 0.3%。用户感知上几乎没有波动,运维也不用半夜叫醒来解锁表。这一切是把核心库从 MySQL 8.0 换成 PostgreSQL 16 后实现的。

MySQL用惯了?PostgreSQL这3个亮点,高并发项目更胜一筹

说白了,这次换库不是一时冲动,而是被一堆老毛病逼出来的。高并发下的问题有迹可循:复杂统计跑得忽快忽慢,涉及 JSON 的查询把 QPS 拖垮,写操作一堆死锁。查了好几轮,都指向同一类瓶颈,所以团队决定试一次完整迁移,目标很清楚——零停机切换,在大促峰值能稳稳扛住流量。

走的路子比较保守:灰度逐步放量。先把 10% 流量导到 PostgreSQL,观察 24 小时没问题就上 50%,最后到 100%。灰度期间做了三轮压测和故障演练。压测用 JMeter:单接口拉上限,看极限吞吐;混合场景按真实流程混跑,创建、支付、查询都来一遍;还有专门断库、催发死锁的故障注入,所有情况都得通过才能进下一步。这样把上线风险一点点摊开,用户那头基本感觉不到波动,业务也能平滑过渡。

压测暴露了一个常见误区:把数据搬过去并不等于性能自动变好。迁移后有几条查询竟然在 PostgreSQL 上更慢,问题找到了——索引不合适。MySQL 常用的索引套路放到 PostgreSQL 不必定管用。举个例子:MySQL 里靠单列索引加 LIMIT 的场景,到了 PostgreSQL 更需要用包含字段的覆盖索引(INCLUDE),这样能避免回表。JSON 查询也不一样,PostgreSQL 用 JSONB 配合 GIN 索引,不能照搬 MySQL 那套函数索引做法。针对订单系统十几条核心 SQL,我们重做了索引策略:为订单表的创建时间和订单状态建复合索引,给扩展信息字段上 GIN 索引。结果是查询响应平均下降了 65%。

数据搬迁那块用的是 pgloader 做全量导入,但细节挺多。数据类型和函数兼容是两大坑。MySQL 的 datetime 在 PostgreSQL 是 timestamp,要把时区统一,最后系统统一改成 UTC,避免跨时区数据出错。自增主键在 PostgreSQL 要用 serial 或者更推荐的 identity,不能直接搬 MySQL 的 auto_increment,否则会遇到主键冲突。SQL 函数也不一样:MySQL 的 DATE_FORMAT 在 PostgreSQL 要改成 to_char,我们写了脚本批量替换这些不兼容函数,花了两周跑完,之后再人工抽查和回归测试,省下不少手工活。

回头说为啥要思考换库,问题可以从业务痛点说起。我们有一条常跑的复杂统计 SQL,用来算“近 7 天各地区已支付订单的商品品类分布”,这条要连四张表,聚合、子查询套着子查询。MySQL 的 InnoDB 在并发超过 500 后,响应从 200 毫秒飙到 1.2 秒,系统负载立刻上来。PostgreSQL 在同等条件下,靠物化视图配合多元索引,把响应稳定在 300 毫秒左右。差别的根儿在查询规划器:MySQL 的优化器在简单场景很高效,但碰到多表复杂查询,PostgreSQL 的规划更稳。

JSON 的处理也是关键,目前许多表把可变扩展字段往 JSON 里塞,列如商品规格、地址额外字段什么的。MySQL 支持 JSON,但在索引和查询灵活性上有局限,想对 JSON 内部字段建立索引,常常得靠函数索引,既难写又不够快。PostgreSQL 的 JSONB 支持 GIN/GiST 索引,可以直接对内部字段做模糊或范围查询。我们把一些 JSON 密集型查询迁到 PostgreSQL 后,相关查询的 QPS 提升了大约 2.3 倍,这对那些常常变结构的场景特别管用。

写并发控制上也有差别。MySQL 用行锁加 MVCC,但高并发写时容易触发间隙锁、死锁。去年大促我们基于 MySQL 的库存扣减模型,死锁率高到 12%,运维夜间频繁被叫醒处理锁问题。PostgreSQL 的 MVCC 实现和快照隔离让锁竞争降低,而且它原生支持的一些语法组合更方便做乐观并发控制。我们把库存扣减迁到 PostgreSQL,配合可串行化隔离级别和 SELECT … FOR UPDATE SKIP LOCKED 的模式,死锁率降到 0.3%。这个数字让运维压力明显减轻,峰值期间系统表现更稳。

选型上没有把数据库分成好坏两类来硬碰硬。我们看的是业务场景和技术特性怎么对上号。读多写少且 SQL 复杂的统计类任务,列如订单统计、路径检索、对账这些,PostgreSQL 的查询规划器和丰富索引能带来好处。需要存灵活属性的地方,列如内容管理、IoT 设备管理、用户画像,JSONB 能省下频繁改表的麻烦。写并发高且必须强一致的场景,列如秒杀、支付、病历类数据,PostgreSQL 在隔离和 MVCC 行为上更容易保证正确性。

迁移不是盲目全量上手。先做了小范围验证服务,把部分订单查询从 MySQL 拉出来到 PostgreSQL 做压测对比。这个试点帮我们确认瓶颈,也验证了索引策略。试验通过后才推进全库迁移。整个流程从准备到切换大致用了一个月,包含数据迁移、索引重建、SQL 兼容调整、压测、灰度放量和运维演练。

技术以外,团队配合也关键。换库不是一个人能做完的事,前端、后端、运维、测试、产品都得参与。为了让大家尽快熟悉 PostgreSQL,我们把知识传递放到日常:每周一次分享,三个月后,核心成员对 PostgreSQL 的常用操作、索引策略、性能调优都有了实战经验。遇到问题时排查效率上去了不少。

迁移过程里还有许多零碎的事儿。列如发现老 SQL 用了 MySQL 专有语法,迁过去会报错,于是把这些 SQL 做了分级清单,优先改影响面最大的那批。做灰度时,监控告警规则也要同步调整,TPS、平均响应、95 百分位、死锁率这些指标都设了阈值,一旦触发就会走回滚流程。回滚流程提前演练过,确保极端情况下还能把业务拉回来。数据一致性的改动也不轻松:主键策略、事务边界、幂等设计都得复查。把自增主键换成 identity,支付相关表加了幂等校验,避免重复写入。并发扣减库存的逻辑改造后,配合 SKIP LOCKED 的队列消费模型,高并发时处理更顺畅。

团队把遇到的问题和可复用的解决办法都记录下来,保存在内部文档库,作为后面项目的参考。最后一步是把运维自动化脚本和监控看板交接给 SRE,确保日常运行和突发问题能被快速定位。这里不仅有技术细节,还有流程、人员协同上的投入。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
拾闻的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容