
1.概述
1.1.为什么越来越多公司选择 PostgreSQL?
如今,越来越多的企业选择 PostgreSQL 而非传统的 MySQL 。这背后并非偶然,而是两种数据库设计哲学的体现。
我们可以通过设计目标、场景选择建议、典型场景三个方面与MySQL进行对比,快速建立起对 PostgreSQL 的认知。
1.2.设计目标对比
| 数据库 | 设计目标 | 取舍 |
|---|---|---|
| MySQL | 做最流行的 Web 数据库,追求足够快、足够稳定、足够简单 | 为了易用性和性能,舍弃了部分高级功能 |
| PostgreSQL | 做功能最强大的数据库,支持多样化的数据类型与复杂场景 | 不介意牺牲一点性能或增加一些复杂度 |
🧠 简单来说:
MySQL 是"轻骑兵",适合常见业务快速落地
PostgreSQL 是"重装部队",专为复杂需求而生
1.3.场景选择建议
-
简单场景用 PostgreSQL 可能显得"杀鸡用牛刀",配置和语法略重
-
复杂场景用 MySQL 则可能"力不从心",功能和边界受限
1.4.实例对比:三个典型差异
例 1:用户状态字段
MySQL 实现:
sql
-- 简洁直接 status ENUM('online', 'offline', 'away') -- 或 TINYINT
PostgreSQL 实现:
sql
-- 更严谨:先创建自定义枚举类型 CREATE TYPE user_status AS ENUM ('online', 'offline', 'away'); CREATE TABLE users ( id SERIAL PRIMARY KEY, name VARCHAR(100), status user_status DEFAULT 'offline' );
可以看到,postgresql虽然功能强大,但新手可能需要先理解"枚举类型"和"自定义类型"的概念。如果只想快速使用,这个步骤就略显繁琐。
例 2:IP 地址范围查询
场景:在 IP 地址列上进行范围检索
PostgreSQL 实现
sql
-- 使用 GiST 索引支持高效网络类型查询 CREATE INDEX idx_ip_range ON logs USING gist (ip inet_ops);
这里可以看到postgresql功能虽然强大,但是当logs表里数据不大时,这无疑是增加了学习成本。
例 3:窗口函数与 JSON 查询
-
窗口函数 :MySQL 到 8.0 版本才支持,PostgreSQL 早已原生支持且更成熟
-
JSON 数据处理 :PostgreSQL 支持 GIN 索引,可在 JSON 字段上实现高效查询,效果接近 Elasticsearch,MySQL 则相对薄弱
sql
-- PostgreSQL JSON 查询 + GIN 索引 CREATE INDEX idx_doc_data ON logs USING gin (doc); SELECT * FROM logs WHERE doc @> '{"level": "error"}';
1.5.总结
| 如果你更在意 | 推荐选择 |
|---|---|
| 快速开发、简单稳定、社区广泛、速度快 | MySQL |
| 数据类型丰富、查询复杂、未来扩展性强 | PostgreSQL |
2.类比 MySQL学习 PostgreSQL
学习新事物最快的方式,莫过于从熟悉的事物出发。如果你已经了解 MySQL,那么掌握 PostgreSQL 的核心差异,只需要关注三个关键词:数据类型、索引、事务。
2.1.数据类型
相同的基础类型:
MySQL 和 PostgreSQL 的基础数据类型高度一致:
INT、VARCHAR、TIMESTAMP、TEXT、BOOLEAN等
这些直接复用已有知识即可,无需重复学习。
PostgreSQL 的特色高级类型:
PostgreSQL 的"功能强大"首先体现在数据类型上,以下几种值得重点关注:
| 数据类型 | 用途场景 | 典型应用 |
|---|---|---|
| JSONB | 存储半结构化数据,支持高效查询和索引 | 配置信息、日志、动态表单 |
| 数组 | 一对多关系的简化存储,避免建关联表 | 标签系统、多项选择 |
| 地理空间 | 配合 PostGIS 扩展,支持地理位置计算 | 地图应用、附近搜索 |
| 范围类型 | 表示连续区间,支持重叠、包含等判断 | 时间段预订、价格区间 |
2.2.索引
核心差异:聚集索引 vs 非聚集索引
MySQL 和 PostgreSQL 在索引上的最大不同,源于两种设计哲学:
| MySQL (InnoDB) | PostgreSQL | |
|---|---|---|
| 设计追求 | 极致性能 | 功能丰富、扩展性强 |
| 索引方式 | 聚集索引 | 非聚集索引 |
| 数据与索引关系 | 数据即索引,索引即数据 | 数据与索引分离 |
MySQL 的聚集索引
数据和索引存储在一起,主键索引的叶子节点直接存放整行数据。优势是避免"回表"------无需二次查询即可拿到完整数据,减少了磁盘 IO,性能更优。
┌─────────────────┐
│ B+ Tree 索引 │
│ 叶子节点存放整行数据 │ ← 无需回表,一次到位
└─────────────────┘
PostgreSQL 的非聚集索引
索引和数据分开存储,索引叶子节点存放的是数据的物理位置(CTID)。查询时需要先查索引,再根据位置去取数据------即"回表"。
text
┌──────────┐ ┌──────────┐
│ B+ Tree │ ──→ │ 数据表 │
│ 索引 │ 回表 │ 堆表 │
└──────────┘ └──────────┘
为什么 PostgreSQL 要这样做?
非聚集索引牺牲了一点性能(回表开销),但换来了极大的灵活性:
-
数据可以按任意方式组织,无需和索引树绑定
-
可以创建多种不同类型的索引,针对不同数据类型量身定制
-
支持更丰富的查询场景
B-Tree:共同的基石
对于字符串、数值等基本数据类型 ,两者都采用 B-Tree 索引。这是关系型数据库的最佳实践,MySQL 和 PostgreSQL 在这方面是一致的。
PostgreSQL 的特色索引类型
PostgreSQL 的真正独特之处,在于为高级数据类型量身打造的索引类型:
GIN(通用倒排索引)
-
适用场景:JSONB、数组、全文检索
-
原理:将复合数据中的元素拆开建立索引
-
示例:查找包含某个标签的所有文章,GIN 索引会非常高效
text
-- 为 JSONB 字段创建 GIN 索引
CREATE INDEX idx_log_data ON logs USING gin (data);
GiST(通用搜索树)
-
适用场景:地理空间数据(PostGIS)、范围类型、最邻近搜索
-
特点:支持"包含""相交""相邻"等复杂查询条件
-- 为 IP 范围创建 GiST 索引
CREATE INDEX idx_ip_range ON ip_table USING gist (ip_range);
BRIN(块范围索引)
-
适用场景:超大规模、天然有序的表(如时序日志、传感器数据)
-
优势:占用的空间极小(通常只有几 KB),却能极大加速范围查询
-- 为时序数据创建 BRIN 索引
CREATE INDEX idx_created_at ON logs USING brin (created_at);
| 索引类型 | 适用场景 | 特点 |
|---|---|---|
| B-Tree | 数值、字符串等基本类型 | 默认索引,通用 |
| GIN | JSONB、数组、全文检索 | 拆解元素,倒排 |
| GiST | 地理空间、范围类型 | 支持复杂空间查询 |
| BRIN | 超大规模时序数据 | 空间极小,范围查询快 |
2.3.事务
两边隔离级别一样,使用起来没有区别,但是底层实现不一样。postgresql只有一种日志 wal log,回滚靠的是标记,标记事务失效,而不是真正意义上的撤销操作。
使用体验
-
隔离级别一致(读已提交、可重复读、串行化)
-
使用语法相同(
BEGIN、COMMIT、ROLLBACK) -
日常开发中,事务的使用几乎没有区别
底层实现差异
| MySQL (InnoDB) | PostgreSQL | |
|---|---|---|
| 日志机制 | Redo log(重做)+ Undo log(回滚) | WAL log(预写日志) |
| 回滚方式 | 通过 Undo log 执行物理撤销 | 标记事务失效,不实际撤销数据 |
| 设计哲学 | 主动回滚,恢复数据 | 被动标记,通过 MVCC 隔离 |
PostgreSQL 的事务机制
PostgreSQL 只有一种日志------WAL(Write-Ahead Log)。回滚操作并不是真正撤销数据,而是:
-
将事务中修改的数据行标记为"无效"
-
通过 MVCC(多版本并发控制)机制,让其他事务看不到未提交的修改
-
后续的
VACUUM进程会清理无效数据
这种设计的优势是回滚速度极快------无论事务多大,回滚几乎是瞬时完成。代价是需要定期清理(VACUUM)来回收空间。