资讯动态

MySQL vs PostgreSQL:数据库选型、迁移成本与真实技术差异解析

发布时间:2026/10/5 11:12:01 来源:尧图企业网站定制
先说结论那些“大厂彻底抛弃MySQL、全面转投PostgreSQL”的说法一半是事实一半是想象。事实是PostgreSQL确实在快速吃掉新增业务的市场想象的是“存量系统搬家”这件事在绝大多数大厂内部根本没发生或者只发生在个别边缘业务上。作为一个搞了多年数据库选型和迁移的人我这两年接到最多的咨询就是“我们在评估要不要从MySQL迁到PostgreSQL值不值” 问的人多了我就觉得有必要把背后的技术原因、商业原因、以及迁移成本老老实实讲一遍。这篇文章不站队也不劝退谁只讲我看到的真实情况和踩过的坑。适合正在做数据库选型的架构师、DBA、后端负责人以及想弄明白“为什么朋友圈天天有人吹PG”的开发同学。1. “弃坑”的真相大厂根本没有搬家而是在“多挖一口井”1.1 存量系统不是不想搬是搬不动我参与过一个内容平台的核心库迁移评估当时动议是“把订单和账户体系从MySQL搬到PostgreSQL”理由是PostgreSQL功能强、性能也不差。方案评审会开了三轮最后放弃了。为什么订单域光表就超过500张存储过程、定时任务、消息表、数据归档逻辑全部耦合在一起。这些代码不是一个人写的是几代人迭代出来的谁都说不清楚哪些字段被哪些老接口隐式依赖。MySQL的AUTO_INCREMENT、ON DUPLICATE KEY UPDATE、REPLACE INTO这些方言在业务代码里到处都是真要迁移等于把所有数据访问层重写一遍。团队的结论是迁移风险高于继续维护MySQL的成本不值得。所以你看很多人说的“弃坑”真实含义是“新项目不用MySQL了”而不是“老项目从MySQL搬走了”。大厂里的存量MySQL通常还在而且跑得好好的。这就像一个人换了新车但不代表他会把老房子拆了重盖。1.2 从MySQL分支到PG内核大厂更愿意换个引擎发新副本另一个现象更微妙。很多大厂并没有直接“用PostgreSQL”而是基于PostgreSQL内核做了自研数据库。你可以把PostgreSQL当成一个“数据库操作系统”——因为它的License宽松插件体系成熟团队拿来做二次开发很方便。国内你能听到的不少数据库产品都有PostgreSQL的血统有的是直接基于PG改的有的是拿PG生态做兼容层的。这就是为什么我说“阵营”这个词很准确——厂商不是真的弃坑MySQL而是发现PostgreSQL这个内核更容易长出新的东西。如果团队规划的是“下一代数据库”选PG内核做底盘比从零写一个存储引擎现实得多。MySQL那边呢也不是没有分支像Percona、MariaDB都很成熟但Oracle主导的社区节奏让很多做产品的团队心里没底。相比之下PostgreSQL的社区治理更开放、版本迭代更规律这让那些想“长线投入”的厂商更安心。2. MySQL真正透支团队精力的地方从单机到分布式之间的软肋2.1 单机写扩展和复制延迟这两堵墙MySQL最成熟的使用姿势是“一主多从、读写分离”。这个模式在小规模下非常稳但一旦业务量上来第一个被压垮的往往是主库的写能力和复制链路。MySQL的复制本质上是把binlog从主库拉到从库回放。只要主库上有大事务、DDL或者一批批量更新从库延迟就会肉眼可见地往上飙。我自己见过最夸张的一次高峰期主库一个UPDATE影响了几百万行从库延迟直接到了半小时线上读请求全部打到主库主库CPU被打满。PostgreSQL也有复制同样是基于WAL日志的流复制但在两个地方体验不同一是PG的逻辑复制可以只同步指定表二是PG在复制链路上有更细的控制比如可以单独设置同步级别。当然我不能说PG完全没延迟问题但至少在设计上是把“延迟敏感”这件事当成一等公民来处理了。另一个MySQL的老问题是DDL。早期版本ALTER TABLE会锁表即使后来支持了INPLACE算法对大表做字段变更依然风险重重。8.0以后加了INSTANT ADD COLUMN算好了一点但存量老库的DDL习惯一旦形成团队还是会养成“凌晨三点上线改表”的肌肉记忆。2.2 分库分表一时爽跨片查询火葬场MySQL单机撑不住的时候大家第一反应就是分库分表。ShardingSphere、MyCat或者大厂自研的中间件本质上都是同一套思路把数据按某个键散到多个实例上。这套路在小规模时很爽但分完之后麻烦一个接一个跨分片的JOIN没了、分布式事务要用柔性方案、全局主键要靠雪花算法或者号段模式、聚合查询要从多个分片拉数据再汇总。为了绕开这些限制业务团队被迫做“宽表”把本来可以靠数据库关联解决的问题变成应用层拼装。PostgreSQL给了一个不同的选择与其分库分表不如把单库做大、把分区表用起来。PG的原生声明式分区、分区裁剪、以及能处理大事务的能力让很多业务在单库层面就能扛住。当然PG在极致规模下也需要分片方案Citus就是干这个的但它的使用方式比MySQL中间件那一套要更规范、更接近“原生扩展”一些。我的意思不是“分库分表这技术没用”而是它应该被当成最后的兜底手段而不是一开始就默认要拆。很多团队MySQL用不下去不是MySQL本身多差而是被分库分表后的一地鸡毛拖垮的。换到PostgreSQL很多中等规模的业务根本不需要走到分片那一步成本自然就下来了。3. PostgreSQL“顺手”的底气那些能打的功能点掰开讲3.1 JSONB、部分索引和生成列数据建模时省掉的弯MySQL 5.7开始支持JSON类型但说实话用起来总觉得差点意思。PostgreSQL的JSONB是二进制格式可以直接建GIN索引、支持各种操作符性能和处理便利性都明显更好。比如你想在用户表里存一份扩展属性判断“某个用户是不是VIP”MySQL里你得写SELECT * FROM users WHERE JSON_CONTAINS(meta, vip, $.tier);PostgreSQL的JSONB可以写得更直接SELECT * FROM users WHERE meta ? vip;这个差别看似只是语法习惯但它背后是JSONB在PG里是一等公民操作符、索引、函数都配套齐全你可以把JSONB当成一个“半结构化列”来建模而不是当成一个不好查的文本字段。再比如部分索引。MySQL不支持只能通过虚拟列绕。PG直接给你CREATE INDEX idx_users_active ON users (id) WHERE status active;这个索引只维护活跃用户体积小、查询快。对那种“每天只看当前有效订单”的业务来说效果立竿见影。3.2 复杂查询CTE、LATERAL、FILTER子句分析报告不绕路MySQL 8.0虽然补上了窗口函数和CTE但身边做数据分析和报表的同事给我的反馈是PG的优化器对复杂查询的处理更老练递归CTE、LATERAL子查询、FILTER聚合这些高级特性用起来非常顺手。举一个场景你要按部门算平均薪资同时只看超过平均线的人。PG里可以这么写WITH dept_avg AS ( SELECT dept_id, avg(salary) AS avg_salary FROM employee GROUP BY dept_id ) SELECT e.name, e.salary, d.avg_salary FROM employee e JOIN dept_avg d ON e.dept_id d.dept_id WHERE e.salary d.avg_salary;这段SQL在MySQL 8也能跑但如果在老MySQL 5.7上你就得写嵌套子查询性能还容易崩。对有大量分析需求、又要兼顾在线业务的系统来说PG的综合体验更接近Oracle那种“复杂查询也能优化得很好”的感觉。3.3 生态插件PG不仅是个数据库更像数据库操作系统PostgreSQL最让我觉得“恐怖”的地方是它的扩展能力。你想要时空数据装PostGIS想要时序能力有TimescaleDB想要向量检索有pgvector。几乎每过一阵PG生态里就会冒出一个新的“官方级解决方案”。这一点对于大厂太重要了。以AI应用为例现在很多团队做RAG检索需要向量数据库。如果你的业务量不是特别夸张完全可以在PostgreSQL里装一个pgvector插件同时管好业务数据和向量数据少维护一个组件。MySQL这边有自己的HeatWave之类的能力但落地路径和生态丰富度差距还是明显的。可以说PostgreSQL在逐渐扮演“数据库操作系统”的角色核心负责事务、存储、并发扩展负责各种专用场景。这种架构让团队有一种“我的数据库还能再长一截”的安全感。4. 从MySQL迁到PostgreSQL的48小时演练工具和坑我都踩过4.1 先选对版本和工具别从第一步开始就输先说版本。如果你是想学习或者搭个小项目PostgreSQL 16、17都可以直接选网上那些“postgresql 16便携版”、“postgresql下载哪个版本”的疑问不用纠结当前稳定版就是答案。生产环境更建议用16及以上社区支持周期长新增特性能吃到。迁移的第一步不是直接灌数据而是选工具。我推荐先看pgloader它能把MySQL的数据结构转换成PG的结构支持全库迁移遇到不认识的数据类型会报错方便你逐个解决。命令行大概是这样的pgloader mysql://user:pass192.168.1.100:3306/app \ postgresql://user:pass192.168.1.101:5432/app如果你的环境装不了pgloader备选方案是用mysqldump导出SQL再把数据转成CSV用PG的COPY命令导入。后一种方式适合表结构差异很大的时候因为你可以先手工建好PG的表再绕过结构转换只导数据。4.2 类型映射与“自增主键”最容易炸迁移过程里最容易翻车的不是数据量而是类型方言。MySQL和PG看着都是SQL类型系统却有不少差异我把常见的对应关系列一下MySQLPostgreSQL注意点TINYINTSMALLINT/BOOLEAN如果只存0/1可考虑booleanINT UNSIGNEDINTEGERCHECK或BIGINTPG没有unsigned概念DATETIMETIMESTAMP注意是否有默认值、时区TIMESTAMPTIMESTAMPTZ推荐用带时区的类型ENUMCREATE TYPE需要手动建枚举类型TEXTTEXT基本无坑JSONJSONB建议直接用JSONBAUTO_INCREMENTSERIAL或IDENTITY迁移后必须重置序列最经典的一个问题MySQL里用AUTO_INCREMENT迁移到PG后你用SERIAL但忘了把序列的当前值设置到max(id)1结果新插入的数据直接主键冲突因为序列从1开始。解决办法很简单SELECT setval(your_table_id_seq, (SELECT max(id) 1 FROM your_table), false);这个细节我在多个项目里都碰到过每次都会有人中招。你可以在迁移脚本里统一处理所有序列别等上线报错了再手忙脚乱。4.3 SQL方言差异反引号、ON DUPLICATE KEY UPDATE 和存储过程MySQL的反引号写久了到了PG会非常难受。PG的标识符默认是小写如果你想保留大写或特殊字符需要用双引号。所以从MySQL带过来的建表语句基本要重写一遍。更麻烦的是INSERT冲突处理的写法不同。MySQL是INSERT INTO t (id, name) VALUES (1, a) ON DUPLICATE KEY UPDATE name VALUES(name);PG是INSERT INTO t (id, name) VALUES (1, a) ON CONFLICT (id) DO UPDATE SET name EXCLUDED.name;看着像但如果你的业务习惯用REPLACE INTO那就更麻烦——PG没有这个语法你必须改成先查后写或者用ON CONFLICT加上删除再插入的组合逻辑上要仔细核对。存储过程的差异也很大。MySQL 5.7的存储过程语法和PG的PL/pgSQL基本不相容迁移时完全重写工作量不小。我的建议是如果你现在还在用MySQL存储过程做复杂业务逻辑迁移PG的同时顺手把逻辑搬到应用层。数据库少写点过程式逻辑对未来的运维来说是好事。5. 换了PG不等于躺着睡运维、版本和生态里藏着的坑5.1 PG的内存默认值真不适合高端机器很多人第一次装PostgreSQL会用默认配置直接上线然后发现性能不理想。最典型的就是shared_buffers默认只有128MB这对一台几十GB内存的服务器来说相当于暴殄天物。我有一次帮一个团队优化PG他们机器是64GB内存shared_buffers还是默认值连查询缓存都覆盖不了热数据一半以上的读请求都走了磁盘。常规调参思路是这样的shared_buffers 16GB # 一般给物理内存的25%左右 effective_cache_size 40GB # 让优化器认为系统有足够缓存 work_mem 64MB # 单个排序/哈希操作能用的内存 maintenance_work_mem 2GB # 维护操作如vacuum、create index max_connections 300但这里有个隐藏坑work_mem是每个排序操作都可能用到的如果300个连接同时做复杂排序最坏情况下可能吃满几十GB内存直接OOM。所以work_mem别拍脑袋给一个很大的值要结合连接数和业务并发去算。5.2 autovacuum和表膨胀不盯着就会出事PG的多版本并发控制机制决定了更新一行数据时旧版本不会立刻删除而是留在表里等一个叫VACUUM的过程清理。自动清理就是autovacuum它是PG的“扫地机器人”。问题在于如果某个长事务一直不结束autovacuum就清不掉对应的事务版本表就会膨胀。膨胀到一定程度查询要走很多无用页面性能断崖式下跌。我接手过一个慢查询排查一张只有几千万行的表查询耗时从几十毫秒变成几秒。一查pg_stat_user_tables发现n_dead_tup已经过了千万而last_autovacuum是两小时前——原因是有一个后台上报任务的连接长期持有快照导致vacuum一直推进不了。解决手段有几个把autovacuum_vacuum_scale_factor调低比如0.05让autovacuum更勤快对超大表单独设置更低的阈值认真治理长事务监控pg_stat_activity里超过10分钟的事务。PG不是装了就能不管的数据库它比MySQL更依赖日常维护纪律。如果你团队没有专职DBA这部分成本要想清楚。5.3 高可用方案Patroni这套体系要重新学MySQL团队一般很熟悉MHA、Orchestrator或者MySQL Shell那套高可用方案。到了PG这边最主流的是Patroni etcd这套体系逻辑完全不同。Patroni负责把数据库的实例状态注册到etcd里然后通过分布式锁决定谁是主库。配置本身不复杂但你要理解“主节点挂了之后怎么让应用自动切换”这个链路比MySQL那套要更依赖分布式协调组件。我在一个迁移项目里就遇到过团队把PG搭成了主从但没有上Patroni连手动切换脚本都没写结果主库宕机时从库没自动提升业务整整停了四十分钟。所以从MySQL迁到PG不只是SQL方言的事运维体系也要跟着换。如果你公司已有成熟的etcd集群用Patroni会很顺手如果没有得提前评估搭建成本。6. 版本、版权和人才结构选PG不只是技术洁癖6.1 MySQL 5.7 EOL8.0的兼容性让旧应用瑟瑟发抖MySQL 5.7官方支持到期这事让不少国内团队被迫面对“升级还是替换”的问题。升级到MySQL 8.0听着简单实际上很多老应用的驱动、连接参数、时区设置和认证方式都变了。比如MySQL 8默认使用caching_sha2_password认证老版本驱动连不上会报错SQL写法和默认字符集排序规则也变了。不少团队评估一圈之后发现升8.0的改造量和迁PG差不多那还不如直接去体验一下PG的功能优势。热搜里一直有“mysql 5.7.44 官方为什么之后是5.7.43”这种问题其实就是大家都在纠结5.7系列的补丁归宿。版本生命周期一到安全感就会下降这是很多迁PG决策的导火索。6.2 许可和生态的战略考量MySQL是GPL/商业双协议商业版要花钱社区版虽然能用但有条款限制很多做企业服务的团队对协议合规非常敏感。PostgreSQL用的是类BSD的宽松协议你可以自由使用、修改、商用甚至不用把改动开源出去。对厂商来说这意味着拿PG做二次开发、做嵌入式存储、做云数据库托管法律风险都低得多。你可以看到很多商业数据库产品选择PG作为内核这不是偶然是License给的底气。对普通业务团队来说协议影响更直接的是“能不能放心用”。MySQL社区版在商业环境里本身也没有大问题但一旦你遇到性能问题想打补丁、想改源码GPL的传染性会让法务部门皱眉而PG就没有这个困扰。6.3 招聘和人才Oracle老司机转PG更顺还有一个容易被忽略的因素人。很多大厂的核心业务原本是从Oracle迁移来的团队里那些经验丰富的老DBA对Oracle的窗口函数、WITH AS、CONNECT BY这类语法非常熟。PostgreSQL在高级SQL上的设计理念更接近Oracle老DBA转过去几乎无缝衔接。反观MySQL如果不用中间件和分库分表高级特性的使用场景天然少老Oracle人反而觉得憋屈。而且看高校和社区的教学方向PostgreSQL在很多数据库理论课程里的出镜率已经在赶超或超过MySQL。新招进来的年轻人读的是PostgreSQL的文档写的是PostgreSQL的SQL自然更愿意把新技术栈往生产里推。7. 最后一个真心建议别被标题带节奏拿自己的业务当尺子如果让我给正在纠结的人一个朴素建议我会这样说如果你有一堆跑了好几年的老MySQL业务且团队已经形成了一套稳定的运维体系别因为看了几篇“弃坑”文章就强行搬。先把新业务、新服务切到PostgreSQL小步试错。如果你的系统还在方案选型阶段业务又有复杂查询、半结构化数据、空间或向量检索的潜在需求那PostgreSQL的性价比大概率比继续MySQL更高。如果你要在一个商业公司内部做数据库级产品License和扩展体系这两个点很可能直接帮你把答案锁定在PG侧。我自己经历过的感受是选型不是追星不是看谁嗓门大更不是看哪边的布道师写文章多。你得拿自己的数据量、团队水平、业务增长速度和运维承接能力一起算。大厂之所以能“弃坑”不是因为PostgreSQL更潮而是他们手里的资源和人才储备足够支撑这种迁徙。你没有这个条件的话抄作业要小心。最后分享一个特别小的技巧不管你是想试MySQL还是PostgreSQL本地用Docker拉起一个最新稳定版把业务里最复杂的那几条SQL拿过去跑一遍对比一下执行计划和调优难度比看任何分析和对比文章都管用。我当年就是这么被PG折服的——一张要跑三秒的分页查询SQL迁移过去之后只用了三百毫秒。那一刻你才会明白阵营之争背后的差距是真的能让你在加班到凌晨两点的时候多睡两个小时。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑