我劝你别再无脑用 MySQL:PostgreSQL 这 5 个底层能力,正在拉开架构师差距
📌 导读:MySQL 和 PostgreSQL,别急着站队
MySQL 和 PostgreSQL 的争论,评论区能吵 100 楼。
但多数人吵的是喜好,不是架构。
一个统治互联网高并发 OLTP,一个在复杂查询、GIS、数据分析、AI/RAG 场景疯狂上分。
真正决定系统上限的,不是“谁更流行”,而是:架构、MVCC、索引体系、扩展能力、迁移成本。
这篇文章不站队,只拆底层。
从插件式存储引擎 vs 一体化内核,到 undo log MVCC vs 行级多版本 + VACUUM;从 JSONB + GIN、数组、范围类型,到递归 CTE 和生产迁移 6 大坑。最后给你一张可直接落地的选型决策图。
一句话先放这:
MySQL 是让你快速落地的小轿车,PostgreSQL 是能处理复杂地形的全能越野车。只会开轿车,在架构师道路上可跑不远。
🔥 本文核心看点
- 架构差异:为什么 MySQL 能换发动机,PG 出厂即顶配?
- MVCC 对决:undo log 回滚段 vs 行级多版本 + VACUUM,谁更稳?
- 索引代差:B+树 vs GIN / GiST / BRIN / 表达式索引 / 部分索引
- 代码实战:JSONB、数组、范围类型、递归 CTE 到底怎么用?
- 迁移踩坑:类型、大小写、自增、COUNT(*)、索引锁、VACUUM
- 选型三问:团队只会 MySQL,新业务要不要上 PG?两者能不能混用?
🧠 读完你能带走什么
- 面试时讲清 MySQL 和 PG 的本质差异,不再只答“看场景”
- 做技术选型时,能按业务类型、团队栈、运维成本做决策
- 从 MySQL 迁 PG 时,提前避开 6 个生产级大坑
- 理解 PG 在 JSON、GIS、分析、AI/RAG 场景为什么越来越香
- 明白 MySQL 为什么仍是高并发简单 CRUD 的王者
👀 适合谁看
Java 后端、架构师、DBA、技术负责人、准备跳槽面试的人,以及正在纠结数据库选型的团队。
✅ 选型结论提前看
- 高并发简单 OLTP、电商、社交、常规 CRUD:优先 MySQL
- GIS、复杂分析、企业级系统、强 SQL 标准、扩展需求:优先 PostgreSQL
- 中大厂常态:两者混用,核心交易 MySQL + 分析/GIS 用 PG
💬 互动一下
你现在公司用的是 MySQL 还是 PostgreSQL?有没有被迁移坑过?评论区聊聊。
觉得有用,先点赞、收藏、转发三连,别等选型踩坑才想起这篇。
关于这个问题的底层原理和更多实战细节,我整理了一份《大厂面试手册》,包含大厂高频面试题、源码解析和性能调优案例。
关注公众号【Rain的Java大神之路】,回复“Java”即可免费领取,持续更新中。
PostgreSQL vs MySQL 深度对比:从架构到选型的全面解析
互联网高并发场景下,数据库选型直接决定系统上限。MySQL 和 PostgreSQL 作为两大开源数据库巨头,各自拥趸无数。本文将从架构设计、核心差异、代码实战、生产踩坑四个维度,带你彻底搞懂两者的本质区别,并给出可直接落地的选型建议。
一、架构差异:一切区别的根源
MySQL 和 PostgreSQL 的底层架构设计哲学完全不同,这决定了它们后续所有能力差异的走向。

一句话总结:MySQL 像一台可以换发动机的汽车——灵活但各引擎能力参差不齐;PostgreSQL 像一辆一体式赛车——引擎不可换,但出厂就是顶配。
二、核心差异速查表
| 对比维度 | MySQL | PostgreSQL | 通俗理解 |
|---|---|---|---|
| 开源协议 | GPLv2,Oracle 主导,商用有合规风险 | 类 BSD 协议,完全开源,商用无限制 | PG 用起来更"安心" |
| 架构设计 | 插件式存储引擎,默认 InnoDB | 原生集成式架构,单一存储引擎 | MySQL 能换发动机,PG 是一体式赛车 |
| SQL 标准兼容 | 部分支持,自有语法较多(反引号、LIMIT) |
高度兼容 SQL:2023 标准 | 写 PG 的 SQL 像看标准文档 |
| 数据类型 | 基础类型为主,JSON 功能有限 | JSONB、数组、枚举、几何、GIS、范围类型 | PG 的类型系统是个"百宝箱" |
| 索引能力 | B+树、哈希、基础全文索引 | B树、GIN、GiST、BRIN、表达式索引、部分索引 | PG 索引能力存在代差优势 |
| MVCC 实现 | 基于 undo log 回滚段 | 基于多版本行标记,旧版本留在数据页 | MySQL 原地多版本,PG 需要定期"垃圾回收" |
| 并发模型 | 线程池 + 连接线程 | 进程模型,每连接一个进程 | 短连接 MySQL 轻量,长连接 PG 稳定 |
| 复杂查询优化 | 简单查询快,多表关联优化较弱 | 优化器能力强,复杂 SQL 性能更优 | 无脑读写选 MySQL,有脑查询选 PG |
| 扩展生态 | 内核扩展难,生态偏业务中间件 | 扩展能力极强,CREATE EXTENSION 即可开挂 |
PG 是插件天堂 |
| 典型场景 | 互联网 OLTP、电商、社交 | GIS、数据分析、企业级系统 | 各有所长,看菜下饭 |
三、3 个面试加分的深层差异
3.1 MVCC 实现思路完全不同
这是两者最本质的内核区别,也是面试高频考点。

- MySQL(InnoDB):旧版本存在
undo log回滚段,通过事务 ID + ReadView 判断可见性。更新不修改原数据页,写入性能好;缺点是大事务容易导致 undo 日志膨胀。 - PostgreSQL:每行数据自带版本标记(xmin/xmax),旧版本直接存在数据块中。读操作直接访问对应版本,无需回滚;缺点是频繁更新会产生大量死元组,导致表膨胀,依赖
VACUUM定期清理。
3.2 索引能力存在代差
MySQL 的索引体系围绕 B+树扩展,能力相对基础。而 PostgreSQL 支持多种索引类型,在复杂业务场景下的优化空间极大:
| 索引类型 | MySQL | PostgreSQL | 适用场景 |
|---|---|---|---|
| B 树 / B+树 | 支持 | 支持 | 等值、范围查询 |
| 哈希索引 | 支持(有限) | 支持 | 等值查询 |
| GIN 倒排索引 | 不支持 | 支持 | JSONB、全文检索、数组包含 |
| GiST 空间索引 | 不支持 | 支持 | 地理空间、范围重叠检测 |
| BRIN 索引 | 不支持 | 支持 | 时序数据、顺序写入大表 |
| 表达式索引 | 8.0 有限支持 | 原生支持 | 函数结果建索引(如 lower(name)) |
| 部分索引 | 不支持 | 支持 | 只给满足条件的行建索引 |
这也是 PG 适合数据分析和复杂业务的核心原因——索引灵活性直接决定了查询优化的上限。
3.3 扩展自由度天差地别

PG 的内核设计极度开放,可以自定义数据类型、操作符、聚合函数,甚至自定义索引方法。而 MySQL 的内核扩展门槛很高,生态更多集中在业务侧中间件。
四、核心代码实战对比
以下 4 个场景是实际开发中体感差异最大的地方,直接看代码感受两者的能力差距。
场景 1:JSON 数据操作与索引(PG 核心优势)
业务背景:存储用户扩展属性,需要对 JSON 内字段进行查询、筛选。
MySQL 写法
-- 建表,JSON 仅支持基础存储
CREATE TABLE t_user_extra (
id BIGINT PRIMARY KEY,
extra JSON
);
-- 查询 JSON 内字段,函数运算默认无法走索引
SELECT * FROM t_user_extra
WHERE JSON_EXTRACT(extra, '$.age') > 25;
-- 8.0 后支持函数索引,但仅支持单字段,能力有限
CREATE INDEX idx_age ON t_user_extra((CAST(extra->>'$.age' AS UNSIGNED)));
PostgreSQL 写法
-- 建表,JSONB 支持高效索引和丰富运算
CREATE TABLE t_user_extra (
id BIGINT PRIMARY KEY,
extra JSONB
);
-- 原生操作符查询,语法简洁符合 SQL 习惯
SELECT * FROM t_user_extra
WHERE extra ->> 'age' > '25';
-- GIN 索引直接支持 JSONB 全字段检索,多属性筛选性能碾压 MySQL
CREATE INDEX idx_extra_gin ON t_user_extra USING GIN (extra);
-- 原生支持包含判断,适合多标签、多属性组合筛选
SELECT * FROM t_user_extra
WHERE extra @> '{"city":"beijing","vip":true}'::jsonb;
技术亮点:PG 的 JSONB + GIN 索引是复杂业务的利器,无需提前规划字段,动态属性也能走索引;MySQL 的 JSON 功能更偏向"存储",查询和索引能力弱很多。
Java / Spring Boot 实战示例
// 实体定义:商品扩展属性用 JSONB 存储
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
private Map<String, Object> attributes; // 动态属性
}
// Repository 中使用原生 PG 查询,精准走 GIN 索引
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query(value = """
SELECT p.* FROM product p
WHERE p.attributes @> :filter::jsonb
AND p.attributes ->> 'brand' = :brand
ORDER BY p.id
""", nativeQuery = true)
List<Product> findByAttributes(@Param("filter") String filter,
@Param("brand") String brand);
}
// 实际调用 —— 一秒内搜出"品牌为A、内存16G、颜色银色"的商品
String filterJson = """
{"内存": "16GB", "颜色": "银色"}
""";
List<Product> result = productRepository.findByAttributes(filterJson, "A品牌");
场景 2:高级索引优化(表达式索引 + 部分索引)
业务背景:需要按用户名小写模糊查询,且只查询有效状态的用户。
MySQL 写法
-- 8.0 前完全不支持函数索引,只能新增冗余字段
ALTER TABLE t_user ADD COLUMN name_lower VARCHAR(50);
CREATE INDEX idx_name_lower ON t_user(name_lower);
-- 查询时必须依赖冗余字段,增加维护成本
SELECT * FROM t_user WHERE name_lower LIKE 'zhang%' AND status = 1;
PostgreSQL 写法
-- 表达式索引:直接对运算结果建索引,无需冗余字段
CREATE INDEX idx_lower_name ON t_user (lower(name));
-- 查询时写 lower(name) 自动命中索引
SELECT * FROM t_user WHERE lower(name) LIKE 'zhang%';
-- 部分索引:只给有效行建索引,体积小、性能高
CREATE INDEX idx_valid_user ON t_user (name) WHERE status = 1;
-- 带 status=1 条件时自动命中部分索引,大幅减少索引扫描量
SELECT * FROM t_user WHERE name LIKE 'zhang%' AND status = 1;
技术亮点:PG 的索引灵活性极强,能针对复杂业务场景做精细化优化,减少冗余字段和数据量;MySQL 在这方面能力差距明显。
场景 3:数组与范围类型(PG 独有特性)
业务背景:存储用户标签、时间段预约,需要做包含、重叠判断。
PostgreSQL 写法
-- 原生数组类型,无需中间关联表
CREATE TABLE t_user_tag (
id BIGINT PRIMARY KEY,
tags TEXT[] -- 文本数组类型
);
-- 查询包含 "VIP" 标签的用户,GIN 索引可加速数组包含查询
SELECT * FROM t_user_tag WHERE tags @> ARRAY['VIP'];
-- 原生范围类型:存储时间区间,判断重叠
CREATE TABLE t_reservation (
id BIGINT PRIMARY KEY,
time_range TSTZRANGE -- 带时区的时间范围类型
);
-- 查询和指定时间段有重叠的预约,一行 SQL 搞定复杂逻辑
SELECT * FROM t_reservation
WHERE time_range && TSTZRANGE('2024-01-01 10:00', '2024-01-01 12:00');
技术亮点:原生支持数组、范围、几何等高级类型,大幅简化业务代码,避免中间表和复杂关联;MySQL 需要通过关联表、字符串拼接实现,性能和可维护性差。
场景 4:递归层级查询
两者都支持递归 CTE,但 PG 的优化器在多层嵌套、递归深度上表现更稳定。
-- 递归查询部门层级,PG 执行效率更高,支持更深层级递归
WITH RECURSIVE dept_tree AS (
SELECT id, name, parent_id, 1 AS level
FROM t_dept WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.name, d.parent_id, dt.level + 1
FROM t_dept d
JOIN dept_tree dt ON d.parent_id = dt.id
)
SELECT * FROM dept_tree ORDER BY level, id;
Java 实战封装
// 纯 SQL 递归,一次查询返回完整树,无需 N+1 递归调用
public List<DeptTreeDTO> getSubDeptTree(Long rootDeptId) {
String sql = """
WITH RECURSIVE dept_tree AS (
SELECT id, parent_id, dept_name, 1 AS level
FROM department WHERE id = :rootId
UNION ALL
SELECT d.id, d.parent_id, d.dept_name, dt.level + 1
FROM department d
INNER JOIN dept_tree dt ON d.parent_id = dt.id
)
SELECT * FROM dept_tree ORDER BY level, id
""";
Query query = entityManager.createNativeQuery(sql, Department.class);
query.setParameter("rootId", rootDeptId);
return query.getResultList();
}
技术亮点:
WITH RECURSIVE一次查询返回完整树,结合level伪列轻松拼装前端树形控件数据,性能远超 MySQL 的自连接或应用层递归。
五、生产踩坑:6 大迁移难点与解决方案
从 MySQL 迁移到 PostgreSQL 并在生产中稳定运行,以下是真实踩过的坑:

| 难点 | 详细表现 | 解决方案 |
|---|---|---|
| 数据类型不匹配 | TINYINT 不存在,DATETIME 变 TIMESTAMP,TEXT 性能差 |
Hibernate 5+ 自动方言适配;建表时手动指定 SMALLINT、TIMESTAMPTZ |
| 大小写折叠陷阱 | 不带引号的表名/列名会被折成小写,原 MyBatis 映射报错 | 一律使用小写+下划线命名;遗留系统加 naming.physical-strategy 配置 |
| 自增主键 ID 耗尽 | SERIAL 是 32 位,高并发有上限 |
使用 BIGSERIAL 或 GENERATED BY DEFAULT AS IDENTITY(PG10+) |
| COUNT(*) 极慢 | MVCC 多版本导致全表 count 必须顺序扫描 | 业务允许时用 reltuples 估算;建立汇总表或物化视图 |
| 索引锁竞争 | 创建索引默认阻塞写操作 | 使用 CREATE INDEX CONCURRENTLY 在线创建 |
| VACUUM 导致 IO 飙高 | 未及时回收死元组,查询变慢,事务 ID 回卷风险 | 设置 autovacuum_vacuum_scale_factor = 0.01;低峰期手动 VACUUM ANALYZE |
六、选型决策指南

选型决策三问
| 问题 | 决策思路 |
|---|---|
| 团队只会 MySQL,新业务要不要上 PG? | 核心原则:人才栈优先于技术特性。如果团队没有 PG 运维能力,优先用 MySQL 扛业务;只有当业务特性(GIS、复杂分析、强 SQL 需求)MySQL 确实无法满足,且有足够技术储备时,再引入 PG |
| 什么时候该从 MySQL 迁到 PG? | 触发条件:业务出现大量 JSON 动态属性、地理空间、复杂聚合场景,MySQL 优化成本极高;有开源二开、自定义扩展需求;规避 MySQL 商用协议风险。迁移建议双写灰度,逐步切流 |
| 两者能不能混用? | 完全可以,也是中大厂常态:核心交易链路用 MySQL 保证高并发稳定性,GIS、数据分析、中台底层用 PG 发挥特性优势,通过数据同步工具做互通 |
七、Java 开发者还需要学 PostgreSQL 吗?
不搞"必学"的绝对化结论,分三种情况看:
7.1 普通 Java 业务开发:MySQL 是基本功,PG 是加分项
国内互联网 90% 以上的业务还是以 MySQL 为主,它是吃饭的家伙必须吃透。但现在中大厂的复杂业务、中台、数据分析场景里 PG 用得越来越多,面试时能讲清两者差异和选型逻辑,绝对是拉开差距的亮点。
7.2 特定领域开发:强烈建议深入学
如果你做地理信息、物联网时序数据、企业级系统、数据中台、开源二开,PG 几乎是更优解,这些场景下 MySQL 会非常吃力。很多公司去 Oracle 拥抱开源,普遍首选 PG,因为它堪比 Oracle 的特性集。Supabase、TimescaleDB 等明星开源产品也都基于 PG。
7.3 职业长期发展:学了稳赚不亏
数据库的核心思想是相通的,学 PG 不会白费功夫。它能帮你理解 SQL 标准、理解数据库内核的不同设计思路(比如 MVCC 的快照隔离无幻读、B+树以外的 GiST/BRIN 索引),反过来也能加深对 MySQL 的理解,做技术选型时不会只会说"用 MySQL"。
建议学习路线
- 先在本机用 Docker 起一个 PG,执行
EXPLAIN ANALYZE跑复杂 SQL,感受执行计划差异 - 啃一啃《PostgreSQL 即学即用》,重点看执行计划、JSONB 和扩展机制
- 用 Spring Boot + JPA/Hibernate 连接 PG,对比 MySQL 下的行为差异(DDL 生成、事务行为等)
总结
| 维度 | 结论 |
|---|---|
| 高并发简单 CRUD | MySQL 久经互联网验证,首选 |
| 复杂查询 / 分析 / GIS | PostgreSQL 优化器 + 索引体系碾压级优势 |
| 扩展性 | PG 插件生态完胜,CREATE EXTENSION 一键开挂 |
| 运维成熟度 | MySQL 生态更完善,PG 运维门槛略高 |
| 学习价值 | 不会 PG 不影响找工作,但懂 PG 能让你在同层级开发者里更有竞争力 |
一句话:MySQL 是让你快速落地的"小轿车",PostgreSQL 是能处理复杂地形的"全能越野车"。只会开轿车,在架构师道路上可跑不远。
如果本文对你有帮助,欢迎关注我的公众号【Rain的Java大神之路】。
专注 Java 面试、源码、高并发实战,回复“Java”领取《大厂面试手册》,持续更新。

浙公网安备 33010602011771号