我劝你别再无脑用 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 是能处理复杂地形的全能越野车。只会开轿车,在架构师道路上可跑不远。


🔥 本文核心看点

  1. 架构差异:为什么 MySQL 能换发动机,PG 出厂即顶配?
  2. MVCC 对决:undo log 回滚段 vs 行级多版本 + VACUUM,谁更稳?
  3. 索引代差:B+树 vs GIN / GiST / BRIN / 表达式索引 / 部分索引
  4. 代码实战:JSONB、数组、范围类型、递归 CTE 到底怎么用?
  5. 迁移踩坑:类型、大小写、自增、COUNT(*)、索引锁、VACUUM
  6. 选型三问:团队只会 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 写法

sql 复制代码
-- 建表,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 写法

sql 复制代码
-- 建表,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 实战示例

java 复制代码
// 实体定义:商品扩展属性用 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 写法

sql 复制代码
-- 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 写法

sql 复制代码
-- 表达式索引:直接对运算结果建索引,无需冗余字段
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 写法

sql 复制代码
-- 原生数组类型,无需中间关联表
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 的优化器在多层嵌套、递归深度上表现更稳定。

sql 复制代码
-- 递归查询部门层级,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 实战封装

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"。

建议学习路线

  1. 先在本机用 Docker 起一个 PG,执行 EXPLAIN ANALYZE 跑复杂 SQL,感受执行计划差异
  2. 啃一啃《PostgreSQL 即学即用》,重点看执行计划、JSONB 和扩展机制
  3. 用 Spring Boot + JPA/Hibernate 连接 PG,对比 MySQL 下的行为差异(DDL 生成、事务行为等)

总结

维度 结论
高并发简单 CRUD MySQL 久经互联网验证,首选
复杂查询 / 分析 / GIS PostgreSQL 优化器 + 索引体系碾压级优势
扩展性 PG 插件生态完胜,CREATE EXTENSION 一键开挂
运维成熟度 MySQL 生态更完善,PG 运维门槛略高
学习价值 不会 PG 不影响找工作,但懂 PG 能让你在同层级开发者里更有竞争力

一句话:MySQL 是让你快速落地的"小轿车",PostgreSQL 是能处理复杂地形的"全能越野车"。只会开轿车,在架构师道路上可跑不远。


如果本文对你有帮助,欢迎关注我的公众号【Rain的Java大神之路】。

专注 Java 面试、源码、高并发实战,回复"Java"领取《大厂面试手册》,持续更新。

相关推荐
Rain的Java大神实战圈8 天前
RabbitMQ、Kafka、RocketMQ 三选一?这篇把消息队列选型彻底讲透了
场景设计题
Rain的Java大神实战圈9 天前
Elasticsearch从0-1部署成功实战
场景设计题
Rain的Java大神实战圈10 天前
100亿订单号如何去重
场景设计题
Rain的Java大神实战圈10 天前
Git 冲突全攻略:从原理到实战,一文打通所有场景
场景设计题
Rain的Java大神实战圈10 天前
保证线程安全的方法有哪些
场景设计题
Rain的Java大神实战圈16 天前
DTO、VO、PO 到底要不要拆?一篇讲透 Java 实体分层的底层逻辑
场景设计题
Rain的Java大神实战圈17 天前
百万数据Excel如何快速导入导出
场景设计题
Rain的Java大神实战圈18 天前
如何快速上传10G文件
场景设计题
Rain的Java大神实战圈21 天前
数据脱敏是怎么做的
场景设计题