
上个月一个做技术负责人的朋友跟我聊了一下午核心就一个问题新项目到底用 PostgreSQL 还是 MySQL他团队里前后端各执一词后端说 PG 功能强、查询爽运维说 MySQL 稳、踩坑少、云上便宜。这场景我太熟了几乎每换一次公司、每开一个新项目数据库选型都要吵一轮。我自己的态度很明确选型本质不是 PK 两个数据库哪个更好而是看你的业务形态、团队能力和运维边界落在哪个坐标系里。但这不代表“都行”——真到生产环境两者的差异会以各种奇怪的方式冒出来有些是坑有些是宝藏。这篇就按我实际做过的选型评估、迁移和运维经验把 PostgreSQL 和 MySQL 从定位、功能、性能、运维到迁移完整过一遍。不搞那种“A 强在 XB 强在 Y”的两边讨好只讲实际到了企业环境里该怎么判断、怎么选、怎么落地。1. 先看定位再看功能两个数据库出生的路数就不一样很多团队的选型讨论一上来就比 SQL 语法、比 JSON 支持其实顺序反了。两个数据库的历史背景、社区结构和商业模式完全不同这些“出身”决定了它们在很多关键设计上的取舍也决定了你未来踩坑的方向。1.1 出身与社区学院派 vs 实用派PostgreSQL 的前身是 1986 年加州大学伯克利分校的 POSTGRES 项目后来在 1996 年更名为 PostgreSQL一路发展到现在已经三十多年。它的基因里带着浓重的学术研究色彩追求的是关系模型的完整实现、数据的完整性、标准的严格遵循。所以你会发现 PG 的文档极其详尽新版本的特性往往先于商业数据库很多功能甚至有点“超前”——比如 9.x 时代就引入了 JSONB、并行查询、逻辑复制等能力当时 MySQL 还在为 GTID 发愁。MySQL 则是 1995 年由 MySQL AB 公司开发的最初的设计目标非常朴素快、简单、稳定。它没有被学术包袱束缚很多实现都是“怎么快怎么来”。后来 MySQL AB 被 Sun 收购Sun 又被 Oracle 收购MySQL 的“娘家”变成了商业数据库巨头。幸运的是 MySQL 保留了双协议GPL 和商业许可社区和生态也足够庞大B 站、GitHub、Facebook 这些体量的公司都在大规模使用实战积累非常厚。这两个“出身”直接导致了一个现象PG 社区更像一群追求数据库理想的技术极客MySQL 社区更像一群解决问题的工程实干派。前者在讨论“我们能不能把 SQL 标准支持得更完整”后者在讨论“这个查询能不能再快 10 毫秒”。你如果是个喜欢研究数据库内部原理的人会天然喜欢 PG如果你是个只想快速上线、出了 bug 能找到一万篇解决方案的人MySQL 的社区体量会让你睡得着觉。1.2 协议与商业依赖License 这件事别等法务来提醒企业的数据库选型license 问题是不能回避的。MySQL 是 GPL 协议虽然开源免费但如果你的产品要作为 SaaS 服务对外提供给第三方且你修改了 MySQL 源码并对外分发就存在 GPL 传染的风险。当然绝大多数企业不会改 MySQL 源码所以实际风险可控。但 MySQL 的版权在 Oracle 手里这是实打实的商业事实——你用的开源 MySQL背后控制它的是一家商业公司。Oracle 对 MySQL 的开发方向、版本节奏有绝对话语权历史上也多次出现社区和 Oracle 意见不合导致分裂的事件比如 MariaDB 就是从 MySQL 分支出去的。PostgreSQL 用的是类 BSD 的开源协议没有任何一家公司能“拥有”它任何人都可以自由使用、修改、分发甚至商用。这个协议层面的优势让 PG 在金融、政府、国企这些对合规敏感的场景里非常受欢迎也是国内很多信创项目比如麒麟 V10 上跑 PG选择它的重要原因。我的建议是如果你们是给政企、金融机构做项目PG 的协议优势是实打实的加分项如果是互联网产品MySQL 的 GPL 风险基本可以忽略。但如果你们公司有严格的 license 合规流程选型表格里一定要把这块写上别等法务来问才想起这回事。1.3 设计哲学严谨校验 vs 宽容处理这是我这些年最深的体会PostgreSQL 默认偏向“宁可不让你做也不能让你做错”MySQL 则偏向“你想做就做错了你自己负责”。举几个例子。PG 对数据类型极其严格你往 integer 字段里插一个超出范围的数字直接报错MySQL 在非严格模式下可能只给 warning 然后把数据截断。PG 的主外键约束、检查约束、排他约束是一等公民设计表结构时不好好建约束后面迟早被数据质量反噬MySQL 的很多使用者包括早期的我根本不建外键全靠应用层保证。PG 的GROUP BY严格遵循 SQL 标准select 的列必须出现在 group by 里或聚合函数中MySQL 的ONLY_FULL_GROUP_BY在 5.7 之前默认不开启可以写出 “宽松” 的 SQL但结果可能莫名其妙。这种哲学差异没有绝对好坏。PG 的严谨让数据的正确性有了保障尤其适合强一致性业务MySQL 的宽容让开发上手快、报错少但也给生产环境埋了不少“隐性炸弹”。我见过不少 MySQL 项目同一套代码在测试环境跑得好好的上线后因为 SQL 模式不同、字符集不同数据就”变“了。所以如果你是团队的负责人想清楚一个问题你的团队有多少人有精力和意愿去严格约束数据规范如果没有PG 可以在数据库层面帮你兜底一部分。2. 特性硬碰硬哪些差距是真实存在的哪些只是网上在吵功能对比是选型讨论里最容易“吵起来”的部分因为双方都能举出一堆例子证明自己更优秀。我从实际的开发体验出发挑几个真正影响项目开发效率的差异点把两者掰开揉碎看。2.1 SQL 标准、窗口函数与复杂查询SQL 标准的支持度上PostgreSQL 完胜这一点基本没有争议。PG 的文档里明确写了它支持哪些 SQL 标准特性并且几乎每个版本都在补标准MySQL 直到 8.0 才对窗口函数、公共表表达式CTE有了比较完整的支持。但“支持”和“好用”又是两回事。以 CTE 为例PG 里WITH ... AS可以方便地做递归查询比如查组织架构树、商品分类的层级关系还能把复杂查询拆分成多个可读的中间步骤执行计划优化器会帮你做整体优化。MySQL 8.0 虽然有 CTE 了但我在实际使用中感觉它的递归限制和 PG 不完全一样优化器对 CTE 的处理也更“保守”一些复杂场景下还是得拆成临时表来写。窗口函数的情况类似ROW_NUMBER()、RANK()、LAG()、LEAD()这些在 PG 里用得行云流水排序、去重、取前一行的值都是小菜一碟。MySQL 8.0 的窗口函数能用了但如果你手上有老项目还在用 5.7就完全没有这个能力只能写子查询加变量模拟。我给很多团队做培训时都在强调如果你经常要跑统计报表、数据分析类 SQLPG 的体验比 MySQL 高一个档次——这不是玄学是语法完备度直接的体现。至于FILTER、GROUPING SETS、LATERAL这些高级语法PG 支持得相当完整MySQL 只能说“能用”。如果你的团队里有一个爱写复杂 SQL 的工程师PG 会让他如鱼得水如果团队都是 CURD 选手这些功能对你来说可能只是“听说过”。2.2 索引玩法从 B-Tree 到部分索引、表达式索引索引是最能体现“数据库设计功力”的地方。MySQL 的 InnoDB 用的是 BTree主键索引和二级索引的结构非常清晰覆盖索引Covering Index优化做得很好日常查询命中索引后性能极其稳定。但 MySQL 索引的“可玩性”不高除了普通索引、唯一索引、全文索引、空间索引再加上 8.0 的出现函数索引和降序索引之外基本就这些了。PostgreSQL 在索引上走的是“全家桶”路线B-Tree、Hash、GIN、GiST、SP-GiST、BRIN每一种都有它适用的场景。这里重点提两个实际价值特别高的部分索引Partial Index只对满足条件的行建立索引。比如一张订单表有 1000 万行其中 99% 是已完成的订单只有 1% 是“待支付”。你可以建一个WHERE status pending的部分索引索引文件瞬间缩小几十倍查询“待支付”订单的速度反而更快。MySQL 完全没有这个能力。表达式索引Expression IndexPG 可以对某个表达式的计算结果建索引比如LOWER(email)、DATE(created_at)。MySQL 8.0 也支持函数索引了但 5.7 及以下只能通过“生成列 索引”曲线救国多一步维护成本。还有 BRINBlock Range Index索引对时间序列类数据简直是福音。一张物联网设备每天产生上亿条记录、按时间顺序插入的表BRIN 索引可能只有 B-Tree 的几十分之一大查询近期数据一样飞快。我做物联网项目时用过 BRIN那个磁盘空间节省是真的肉眼可见。所以如果你有复杂的索引需求——比如部分索引、表达式索引、按范围快速扫描PG 更值得考虑。MySQL 更适合那种“主键查询 有限几个二级索引”就能打天下的传统业务。2.3 JSON 与半结构化数据JSONB 是真正的杀手级特性聊到 PG永远绕不开 JSONB。这是我做技术选型时最重要的一张牌。MySQL 8.0 也有 JSON 类型底层是二进制的 JSON 存储可以建虚拟列、可以通过JSON_EXTRACT或-运算符来查询性能也不差。但 PG 的 JSONB 是另一回事——它把 JSON 解析成二进制格式后可以直接建 GIN 索引然后用,?,?|,?这些操作符做高效的“包含”查询。更厉害的是PG 的 JSONB 可以和普通列混合使用比如你有一个data JSONB字段里面存了一堆业务扩展属性你既可以>version: 3.8 services: postgres: image: postgres:15 container_name: pg_test restart: always environment: POSTGRES_USER: pguser POSTGRES_PASSWORD: pgpass POSTGRES_DB: pgtest TZ: Asia/Shanghai ports: - 5432:5432 volumes: - pg_data:/var/lib/postgresql/data mysql: image: mysql:8.0 container_name: mysql_test restart: always environment: MYSQL_ROOT_PASSWORD: rootpass MYSQL_DATABASE: mysqltest TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci ports: - 3306:3306 volumes: - mysql_data:/var/lib/mysql volumes: pg_data: mysql_data:启动命令就是docker compose up -d。这里有 3 个细节容易踩坑MySQL 的字符集一定要在启动参数里指定否则默认是latin1插入中文直接乱码。PG 默认是 UTF8所以没有这个问题。PG 的数据目录在容器内是/var/lib/postgresql/data别挂错路径。不同 PG 镜像版本可能目录结构有细微差别建议看官方文档。如果跑在麒麟 V10 这类国产系统上直接用 docker 官方镜像一般没问题但如果是离线环境要在有网的机器上docker save出镜像包再传到目标机器docker load。国内也有不少镜像源可根据实际网络选择。4.2 高可用方案MySQL 重在成熟稳定PG 靠 Patroni 自动化高可用是企业的硬需求两个数据库在这块的解决方案都已比较成熟但风格不同。MySQL 的高可用方案很“百家争鸣”传统的是主从复制加MHA自动故障切换经典耐用用的人多踩坑资料也多中间演进出了半同步复制Semisync Replication和组复制MGRMySQL Group Replication支持多主写入但 MGR 对网络延迟比较敏感部署复杂度也高云厂商则提供了托管版比如 RDS MySQL 的主备切换开箱即用。其本质逻辑是“靠 binlog 做复制”因为 binlog 的设计足够稳定各种方案基本都能兼容。PostgreSQL 的高可用走的是另一条路线原生支持基于 WAL 的流复制备库可以异步、同步、甚至级联。但“能复制”不等于“能自动故障切换”所以社区涌现了 Patroni、repmgr、pgpool-II 等工具。其中Patroni是当前最主流的高可用方案——它在 PostgreSQL 外面包了一层 Python 可执行程序依赖 etcd / Consul / ZooKeeper 来做集群的分布式共识自动探测主库故障、自动把备库提升为新主库。Patroni 在企业里的优势很明显它把主备切换的决策逻辑从 DBA 手里交给了分布式一致性组件避免了“脑裂”两个节点都认为自己是主库的问题配合 HAProxy 做读写分离可以实现比较完整的“数据库自动故障切换”能力。缺点也明显——它对 DBA 的运维水平要求高你得懂 etcd 集群、懂 Patroni 的配置、懂切换演练。网上搜“postgresql 高可用 patroni 安装”能出来一堆教程如果你想在生产环境用 PGPatroni 是绕不开的课题。这里给一个选型层面的判断MySQL 的方案更“开箱即用”PG 的方案更“自动化、更适合被代码和配置管理”。小团队如果没有专职 DBAMySQL 的 MHA 或云数据库产品会让你省心很多有较强的运维或 SRE 团队PG Patroni 可以做得非常专业。4.3 备份恢复归档日志与时间点恢复PITR备份恢复是日常运维最容易被忽略、但出事时最要命的一环。两者的备份思路高度相通都依赖“物理备份 日志归档”实现任意时间点的恢复。MySQL 走的是mysqldump binlog 归档或xtrabackup 物理备份 binlog 归档。binlog 是 MySQL 的复制和恢复的基石增量备份基本就是备份 binlog 文件恢复时先恢复全量备份然后重放 binlog 到指定时间点。很多自动化运维平台其实就是自动执行这些步骤。PostgreSQL 走的是pg_basebackup 物理备份 WAL 归档配合recovery_target_time可以实现 PITRPoint-In-Time Recovery。PG 社区里也有pgBackRest、barman这类专业的备份工具支持增量备份、并行备份、加密、压缩功能比手工脚本强很多。我个人的习惯是不管哪个库备份一定要做“真实恢复演练”不要只是“备份脚本跑成功了”。有些团队备份文件是每天都在生成但从来没有真正恢复到一台测试机上去验证等机房故障时才发现备份文件损坏或者归档日志缺失这种教训我见得太多了。具体的自动化备份方案网上有很多比如 MySQL 的 Windows 下用.bat脚本自动备份再同步到备份机PG 的 Linux 下用 cron 跑 pg_basebackup 定期清理 WAL都可以按团队习惯去落地。4.4 版本选择别当小白鼠也别停在太老的版本版本选型也是决定后续幸福指数的关键这里给一个保守但实用的建议MySQL 这边生产环境优先选 8.0 的稳定小版本比如 8.0.3x。5.7 虽然在老项目里还很常见但已经进入 EOL 倒计时安全更新和 bug 修复越来越少。8.0 在窗口函数、CTE、哈希连接、直方图、降序索引、函数索引这些能力上有了大幅提升值得迁移。如果你还在跑 5.6建议尽早规划升级因为 5.6 在性能和安全性上差距明显而且很多云厂商的兼容策略也在往 8.0 倾斜。PostgreSQL 这边生产环境优先选 14.0 以上目前 15 和 16 是普遍推荐的选择17 如果是新项目也可以考虑。PG 的版本节奏是每年发布一个大版本每个版本都有性能优化和功能增强。但要注意PG 的大版本升级不是“原地升级”需要pg_upgrade或逻辑导出导入过程相对繁琐所以升级频率不用太高。如果团队不熟悉升级流程选一个大版本后稳定用两三年反而是最佳实践。另外一个容易忽略的点是云厂商提供的托管数据库版本。如果你用的是云 RDS版本选择还受云厂商支持节奏的影响——有些云厂商对 8.0 支持得很积极但 PG 某些大版本的支持会滞后。选型前最好去云厂商的控制台看一眼支持的版本列表免得代码写在高版本特性上云上却没有对应版本可用。5. 迁移与工具链从 MySQL 迁到 PG 的真实路径现实中的数据库选型很少是“绿地项目从零开始”更多时候是在已有系统之间做迁移。尤其是这两年 PG 热度上升越来越多的团队在评估“要不要从 MySQL 迁到 PostgreSQL”。这一部分我把迁移工具和方法论整理一下。5.1 结构比对与数据迁移migra、pgloader、DataX从 MySQL 往 PG 迁移第一步是迁移表结构。直接用 GUI 工具导一遍 DDL 再手工改也是办法但生产环境动辄几百张表纯手工容易漏。这里推荐两个比较好用的工具migra一个用 Python 写的 PostgreSQL 结构对比工具主要用途是“比对两个 PG 数据库之间的 schema 差异并生成增量 SQL”。如果你已经在 PG 上建了新库结构或者从某处同步了一个结构可以用 migra 来检查两边结构是否一致、差哪些索引和约束。它不是 MySQL 到 PG 的专用迁移工具但在“PG 库结构对比与同步”这个场景下非常实用也是热词里提到的工具。pgloader这是从 MySQL 迁移到 PG 的经典利器。它可以直接读取 MySQL 的连接信息自动把 MySQL 的表结构转换成 PG 的 DDL并把数据批量 COPY 到 PG同时还能做类型映射处理。比如 MySQL 的TINYINT(1)会转成 PG 的SMALLINTDATETIME会转成TIMESTAMPENUM会转成 PG 的枚举类型或 VARCHAR以及AUTO_INCREMENT会转成 PG 的SERIAL或IDENTITY列。pgloader 的文档里有一份很详细的类型映射表转换前值得先看一眼。DataX阿里开源的数据同步工具可以跑 MySQL 到 PG、PG 到 MySQL 等各种异构数据同步任务。它在数据搬移场景下更通用但它在“结构转换”方面的能力比较弱——主要解决的是“数据怎么搬过去”表结构还是得你自己先建好。除了工具还有几个迁移过程中必须处理的致命差异点自增主键MySQL 的AUTO_INCREMENT和 PG 的SERIAL/GENERATED AS IDENTITY在 SQL 语法上不一样建表脚本要改。字符串类型MySQL 的VARCHAR长度指的是字符数PG 也一样但要特别注意 MySQL 的TEXT类型在 PG 里虽然叫TEXT但 PG 的TEXT和VARCHAR没有明显性能差异而 MySQL 用TEXT时不能有默认值这个问题在迁移后就不存在了。布尔类型MySQL 的TINYINT(1)在 PG 里对应BOOLEAN但应用层代码里如果直接用 0/1 去赋值到了 PG 就要写TRUE/FALSE很多 ORM 框架会自动处理但原生 JDBC 或老代码要注意。日期时间MySQL 的CURRENT_TIMESTAMP可以直接作为列的默认值PG 也一样但 MySQL 的ON UPDATE CURRENT_TIMESTAMP行更新时自动更新时间戳在 PG 里没有需要用触发器或应用层手动处理。SQL 语法差异MySQL 的LIMIT offset, count在 PG 里要写成LIMIT count OFFSET offsetIFNULL()要换成COALESCE()UPDATE ... JOIN在 PG 里不支持要改成UPDATE ... FROM子查询INSERT ... ON DUPLICATE KEY UPDATE在 PG 里要用INSERT ... ON CONFLICT ... DO UPDATE SET。这些在迁移时都要逐条改工作量不小。5.2 逻辑复制与跨库查询FDW 了解下除了全量迁移企业里还有一种常见需求是双跑阶段一部分业务先切到 PG另一部分还在 MySQL两边需要实时同步。这个场景下除了用上面的 DataX 做“定时或准实时同步”之外PG 有一个 MySQL 无法比拟的利器——外部数据包装器FDWForeign Data Wrapper。通过postgres_fdwPG 可以直接查询另一个 PG 库的表通过mysql_fdwPG 可以直接查询 MySQL 库里的表。这意味着你在迁移过程中可以先把业务库从 MySQL 迁移到 PG 上但某些历史报表 SQL 还依赖 MySQL 里的数据通过 MySQL FDW 临时关联查询不把所有表一次性迁完先迁核心表再用 FDW 把遗留数据映射进来做到平滑过渡做跨库 JOIN 时不需要应用层写两套数据源去拼装直接在 PG 里写一条 SQL 就能关联 MySQL 和 PG 两边的表。FDW 的性能肯定不如原生表不适合高频访问的数据但在迁移过渡期和报表低频场景下它可以省掉一大堆数据同步逻辑。这是 PG 生态比 MySQL 生态更大胆的地方——MySQL 虽然有 FEDERATED 引擎但功能和服务器的自由度远不如 PG 的 FDW。5.3 反向迁移PG 到 MySQL 通常不推荐很多人问“如果先上了 PG 之后不满意能不能迁回 MySQL”。理论上可以用 DataX 或者 pgloader 的反向模式都能搬数据但现实里几乎没有人会这么干——因为从 PG 迁到 MySQL很多东西会“降级”复杂 SQL 的窗口函数、CTE、部分索引、JSONB 这些能力都没了数据模型和 SQL 要重写性能可能还要打折扣。所以在选型阶段我通常会提醒团队MySQL 迁到 PG 是一个“提升能力”的过程PG 迁回 MySQL 是一个“自废武功”的过程。如果你不确定未来要用哪些高级特性宁可先在 PG 上把能力做扎实也别轻易把系统压在一个能力边界更窄的数据库上。6. 站在企业的角度做最终决策前面铺垫了这么多技术差异最终还是要回到“企业选型”四个字。技术选型从来不是“哪个数据库更牛”而是“哪个数据库更适合这个业务、这个团队、这个运维环境”。这一部分我给出一个可落地的判断框架。6.1 四个判断维度拉一张打分表我建议选型团队在讨论前先做一张打分表四个维度各占一定权重维度权重建议MySQL 的典型表现PostgreSQL 的典型表现业务模型匹配度30%适合 OLTP、CURD 密集型、点查为主适合复杂查询、JSON、分析型混合负载团队技术栈与学习成本20%上手快、资料多、问题易查功能多意味着学习曲线更陡运维成本与高可用20%方案成熟、云厂商支持好需要更专业的 DBA / SRE 能力尤其 Patroni生态与扩展性30%云数据库生态庞大、中间件多扩展插件强PostGIS、pgvector社区活跃这张表不解决最终选择但它能让团队从“我更喜欢 PG”这种感性的争论变成“我们的业务更依赖哪一块能力”的理性讨论。6.2 典型场景怎么选下面是我在实际项目里最常遇到的几类场景直接给出倾向性结论场景一互联网电商、社交、SaaS 的后台管理系统核心负载是用户注册、登录、下单、订单查询查询比较固定复杂分析和报表会用独立的数仓或搜索服务。这个场景下MySQL 依然是很稳的选择。云厂商的 RDS MySQL 有完善的监控告警、自动备份、主备切换智能诊断和慢查询分析也做得足够好用。技术团队普遍熟悉 MySQL 的索引设计招聘成本也低。场景二金融、政企、审计类系统对数据一致性、完整性、合规性要求极高统计数据报表多往往还要国产化适配比如跑在麒麟 V10 上。这个场景我更推荐 PostgreSQL。PG 的协议友好、严格的数据校验、对 SQL 标准的完整支持、以及活跃的开源生态都让它更容易通过等保测评和信创验收。国产化环境下安装 PG 也基本是标准流程。场景三地理信息系统GIS如果业务涉及经纬度坐标、地图展示、空间查询不用犹豫直接选 PostgreSQL PostGIS。PostGIS 是事实上的开源 GIS 标准MySQL 的空间功能跟它完全不是一个量级。我做过几个涉及 LBS 的项目用 PostGIS 计算距离、判断点是否在面内、做路网分析真的很顺手换成 MySQL 基本就是噩梦。场景四需要灵活 JSON 结构的业务系统配置中心、CMS、元数据管理、事件溯源类系统一张表的字段经常变动用 MySQL 会频繁改表结构用 PG 的 JSONB 可以省掉大量维护成本。这个场景下 PG 的优势是明显的MySQL 的 JSON 功能虽然也能用但体验和扩展性差不少。场景五高并发、海量写入、需要分库分表的业务日活千万级、订单量亿级的互联网核心业务MySQL 是一个更“常规”的选择不是因为它比 PG 强而是分库分表这条路在 MySQL 上实在太成熟了ShardingSphere、MyCat、各种中间件都是主要围绕 MySQL 生态发展的。PG 虽然也有 Citus 等方案但团队踩坑经验相对少。我的态度是如果业务增长预期明确指向“要分库分表”选择生态更成熟的 MySQL 是更稳妥的工程决策。6.3 云服务商和行业惯例的影响不能被忽视的还有“云服务商”这个因素。现在企业数据库绝大多数都是云托管形态而云厂商对两个数据库的支持力度、定价策略、工具链成熟度是有差异的。有的云平台上 RDS MySQL 很成熟但 RDS PostgreSQL 起步晚、版本滞后、自动备份和迁移工具链不够完善反过来也有些以 PG 起家的云厂商对 PG 的适配非常细致。所以我的建议是选型不只是选数据库还要选“谁在帮你运维这个数据库”。先去你的云平台控制台上把两个数据库的规格、价格、支持版本、备份恢复能力、监控告警能力都拉出来对比一遍再决定。企业上云后的数据库选型本质上是“在某个云平台上选择一个运维质量更好的数据库”。7. 常见问题与避坑实录最后一部分我把自己在选型和运维过程中遇到的典型问题整理成速查表并附上实操中积累的避坑心得。7.1 数据库选型与运维常见问题速查表下面这个表格可以收藏起来遇到问题直接对照着看问题出现场景排查思路与建议连接数被打满应用连接池配置过大或慢查询堆积导致连接释放慢MySQL 检查max_connections和thread_pool_sizePG 优先排查是否因为缺少 PgBouncer把连接数控制在合理范围同时优化慢查询PG 的 vacuum 导致表膨胀、查询变慢大量 UPDATE/DELETE 的表autovacuum 没有及时触发调整 autovacuum 参数autovacuum_vacuum_scale_factor、autovacuum_vacuum_cost_limit合理设置表的 fillfactor并定期监控膨胀率MySQL 中文乱码建库时没有指定 utf8mb4建库/建表时显式指定CHARACTER SET utf8mb4连接串里加上characterEncodingutf8容易漏8.0 驱动默认是 utf8mb4同一个 SQL 在 MySQL 和 PG 上结果不一致隐式类型转换 / 排序规则不同MySQL 的字符串比较默认不区分大小写取决于 collationPG 默认是区分大小写的迁移时把索引和查询条件统一约定好PG 里count(*)很慢PG 的 MVCC 版本链导致全表扫描使用索引扫描或结合分区表、物化视图MySQL 的 InnoDB 做count(*)也不快也需要估算策略MySQL 里子查询更新报错UPDATE ... SET ... WHERE id IN (SELECT id FROM ...)触发“不能同时更新目标表”错误MySQL 需要对子查询再包一层临时表PG 则直接支持这种写法PG 连接空闲占内存大PG 是进程模型空闲连接也有内存开销必须引入 PgBouncer 或连接池中间件并且统一配置idle_session_timeoutMySQL 死锁频繁REPEATABLE READ 下的间隙锁 并发写检查事务隔离级别、是否用到了范围条件更新尽量缩短事务时间、保证更新走索引必要时降到 READ COMMITTEDPG 备份恢复后数据不一致只备份了数据文件没备份 WAL 归档备份要包含 WAL恢复时要配置recovery_target_time指定的时间点恢复后要跑一次一致性校验7.2 避坑心得一些不会写进官方文档的体会第一不要在选型讨论里夹带个人技术偏好。我见过太多团队因为“DBA 是 PG 粉”或“后端主程只会 MySQL”就草率决定了整个系统的技术底座。选型要拿数据说话拿业务模型说话宁可多花几天做 POC也不要靠“我觉得”下结论。第二认真对待 POCProof of Concept概念验证。别只在本地跑个 Hello World 就完事要把核心业务表建出来把两套库的 DDL 写出来把最复杂的几条 SQL 分别跑一遍把备份恢复演练做一遍把高可用切换模拟一遍。这套 POC 的投入产出比比任何评测文章都高。第三PG 的性能调优比 MySQL 更需要“基本功”。MySQL 很多时候靠加索引、调参数就能解决问题但 PG 的调优更像做精细的病理学分析要看执行计划、看统计信息、看锁等待。网上教程很多但这部分能力是手艺活。不会调优就上生产遇到性能问题会很痛苦。第四字符集和排序规则一定要在第一版就确定。MySQL 的数据库一旦用了非 utf8mb4 字符集后面想全库迁移字符集非常痛苦。PG 默认 UTF8 虽然省心但不同操作系统环境下的 locale、collation 也要提前统一否则可能出现 sort 结果和预期不一致。第五PG 的 JSONB 虽好但别把数据库当文档数据库用。SQL 里到处是WHERE>