CTO Plus技术服务栈

PostgreSQL vs MySQL 深度对比:2026年数据库选型指南

从架构设计、SQL标准支持、并发控制、索引类型、扩展能力、性能基准、运维复杂度等维度全面对比PostgreSQL 17和MySQL 8.4。包含真实场景的选型建议和迁移注意事项。

SteveRocket
北京,中国
4 min read

选型困境:PostgreSQL 还是 MySQL?

“新项目用 PostgreSQL 还是 MySQL?” 这是技术选型中最常被问到的问题之一。

我们团队同时维护着基于 MySQL 和 PostgreSQL 的系统,在不同场景下各有优劣。这篇文章基于 2026 年的最新版本(PostgreSQL 17 vs MySQL 8.4),从多个维度做一次诚实的对比。

先说结论:没有银弹,只有合适的场景。但如果你让我给一个默认选择——2026 年新项目默认选 PostgreSQL,除非有明确的理由选 MySQL。

架构设计对比

存储引擎

MySQL:插件式存储引擎

-- MySQL 可以按表选择存储引擎
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    ...
) ENGINE=InnoDB;  -- 事务支持

CREATE TABLE audit_logs (
    id BIGINT PRIMARY KEY,
    ...
) ENGINE=MyISAM;  -- 无事务,但读性能好(基本已废弃)

MySQL 的多引擎架构是历史优势也是负担。InnoDB 已经是绝对主流,MyISAM 基本退出历史舞台。

PostgreSQL:统一存储引擎

PostgreSQL 只有一个存储引擎,所有表使用相同的底层机制。这意味着:

  • 不需要纠结引擎选择
  • 所有功能(事务、MVCC、全文搜索)对所有表可用
  • 行为一致,没有”这个引擎不支持 X”的问题

我们的看法:统一引擎是 PostgreSQL 的优势。MySQL 的多引擎在今天已经没什么实际价值了。

进程模型

MySQL:线程模型

每个连接一个线程,共享内存空间。线程切换开销小,但一个线程崩溃可能影响其他连接。

PostgreSQL:进程模型

每个连接一个独立进程(fork)。进程隔离性好,一个连接崩溃不影响其他连接。缺点是连接创建开销大,这也是为什么 PostgreSQL 强烈推荐使用连接池(PgBouncer)。

MVCC 实现

这是两者最大的架构差异:

特性MySQL (InnoDB)PostgreSQL
实现方式Undo Log(回滚段)多版本元组直接存储
旧版本存储在 Undo 表空间中在数据页中
清理机制Purge 线程异步清理VACUUM(膨胀问题)
UPDATE 开销写 Undo + 数据页插入新版本 + 标记旧版本
索引影响二级索引需要回表所有索引都存储版本信息

PostgreSQL 的”膨胀”问题:UPDATE 频繁的表会不断增长,需要 VACUUM 回收空间。这是 PostgreSQL 运维中最需要注意的问题。

-- PostgreSQL 查看膨胀情况
SELECT 
    schemaname || '.' || relname AS table_name,
    pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
    n_dead_tup AS dead_tuples,
    round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_ratio
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;

SQL 标准支持

PostgreSQL 在 SQL 标准支持方面明显领先:

-- PostgreSQL 支持的特性(MySQL 不支持或不完善)

-- 1. CTE 的 UPDATE/DELETE
WITH deleted AS (
    DELETE FROM old_orders WHERE created_at < '2025-01-01' RETURNING *
)
INSERT INTO order_archive SELECT * FROM deleted;

-- 2. DISTINCT ON(保留每组第一条)
SELECT DISTINCT ON (category_id) 
    category_id, name, price
FROM products
ORDER BY category_id, price DESC;

-- 3. 窗口函数更丰富
SELECT 
    date,
    revenue,
    array_agg(revenue) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS last_7_days
FROM daily_revenue;

-- 4. 全文搜索(内置,不需要外部引擎)
SELECT * FROM articles 
WHERE to_tsvector('english', content) @@ to_tsquery('english', 'database & performance');

-- 5. 数组和 JSON 深度支持
SELECT * FROM products 
WHERE tags @> ARRAY['electronics', 'sale'];  -- 数组包含

-- 6. CHECK 约束强制执行
ALTER TABLE users ADD CONSTRAINT age_check CHECK (age >= 0 AND age < 150);
-- MySQL 8.0.16 之前不强制执行 CHECK!

索引能力

索引类型PostgreSQLMySQL
B-Tree✅(默认)
Hash✅(8.0+ 支持,仅等值查询)
GIN(倒排索引)
GiST(空间索引)❌(用 R-Tree)
BRIN(块范围索引)
部分索引❌(8.0 不支持)
表达式索引✅(8.0.13+ 函数索引)
覆盖索引✅(INCLUDE)✅(二级索引自动覆盖主键)

PostgreSQL 的杀手锏——部分索引:

-- 只为活跃订单建索引,节省 90% 空间
CREATE INDEX idx_active_orders ON orders (user_id, created_at)
WHERE status = 'ACTIVE';

-- 只为未删除的记录建唯一约束
CREATE UNIQUE INDEX idx_active_email ON users (email)
WHERE deleted_at IS NULL;

GIN 索引的实际应用:

-- 标签搜索(替代 Elasticsearch 的轻量场景)
CREATE INDEX idx_article_tags ON articles USING GIN (tags);

-- 查询瞬间完成
SELECT * FROM articles WHERE tags @> ARRAY['postgresql', 'database'];

-- JSONB 查询
CREATE INDEX idx_user_preferences ON user_profiles USING GIN (preferences jsonb_path_ops);
SELECT * FROM user_profiles WHERE preferences @> '{"theme": "dark"}';

复制和高可用

特性PostgreSQLMySQL
物理复制✅(WAL 流复制)✅(Binlog 复制)
逻辑复制✅(PG 10+)✅(Binlog 本身就是逻辑的)
同步复制✅(半同步)
级联复制
自动故障转移需要 Patroni/Pgpool需要 Orchestrator/MGR

PostgreSQL 流复制配置:

# postgresql.conf(主库)
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1024    # 保留 1GB WAL

# standby.signal 文件(备库,PG 12+)
# 在数据目录创建此文件即可
# 然后在 postgresql.conf 中配置:
primary_conninfo = 'host=primary_host port=5432 user=replicator'

MySQL 组复制(MGR):

-- MySQL 8.0 的 Group Replication 提供了内置的高可用
-- 单主模式
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

-- 查看集群状态
SELECT * FROM performance_schema.replication_group_members;

扩展生态

PostgreSQL 扩展

-- 时序数据
CREATE EXTENSION timescaledb;

-- 地理空间
CREATE EXTENSION postgis;

-- 向量搜索(AI 应用)
CREATE EXTENSION pgvector;

-- 列式存储(OLAP)
CREATE EXTENSION citus;

-- 全文搜索(中文分词)
CREATE EXTENSION zhparser;

PostgreSQL 的扩展机制允许在不修改核心代码的情况下增加功能。pgvector 尤其值得关注——在 AI 时代,用 PostgreSQL 做向量数据库比引入新的向量数据库更简单。

MySQL 插件

MySQL 也有插件机制,但生态远不如 PostgreSQL 丰富:

  • Clone 插件(8.0.17+):快速克隆实例
  • Keyring 插件:加密密钥管理
  • 半同步复制插件

性能基准

注意:性能测试高度依赖场景,以下数据仅供参考

简单读写(Sysbench oltp_read_write)

并发数PostgreSQL 17MySQL 8.4
1612,000 TPS14,000 TPS
6435,000 TPS38,000 TPS
12842,000 TPS45,000 TPS
25640,000 TPS43,000 TPS

MySQL 在简单 OLTP 场景下略有优势(得益于线程模型和 InnoDB 的优化)。

复杂查询(TPC-H 类查询)

查询类型PostgreSQLMySQL
多表 JOIN(5 表)快 2-3x基准
子查询快 1.5-2x基准
窗口函数快 3-5x基准
聚合 + GROUP BY相近相近

PostgreSQL 在复杂查询上有明显优势,得益于更先进的查询优化器(支持更多 JOIN 算法)和并行查询能力。

插入性能

场景PostgreSQLMySQL
单行插入慢 20-30%基准
批量插入(1000 行)相近相近
COPY/Bulk Load快 2x基准

PostgreSQL 的 COPY 命令在批量导入上远超 MySQL 的 LOAD DATA

运维复杂度

日常运维对比

任务PostgreSQLMySQL
安装apt install postgresql-17apt install mysql-server-8.4
备份pg_dump / pg_basebackupmysqldump / xtrabackup
时间点恢复PITR(WAL 归档)Binlog 恢复
升级大版本pg_upgrade(需停机)In-place upgrade
监控pg_stat_* 视图丰富performance_schema
连接池必需(PgBouncer)可选(ProxySQL)

PostgreSQL 的运维痛点

1. VACUUM 管理

-- 查看 VACUUM 进度
SELECT * FROM pg_stat_progress_vacuum;

-- 配置自动 VACUUM
ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.05;  -- 表 5% 变化时触发
ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.02;

-- 对特定表手动 VACUUM
VACUUM ANALYZE large_table;
VACUUM FULL large_table;  -- 回收空间给 OS(锁表!)

2. 事务 ID 回卷

-- 监控事务年龄
SELECT 
    datname,
    age(datfrozenxid) AS xid_age,
    pg_size_pretty(pg_database_size(datname)) AS db_size
FROM pg_database
WHERE age(datfrozenxid) > 100000000
ORDER BY age(datfrozenxid) DESC;

当事务 ID 接近 20 亿上限时,PostgreSQL 会强制进入只读模式。需要定期执行 VACUUM FREEZE

MySQL 的运维痛点

1. 在线 DDL 限制

-- 有些 DDL 不支持 ALGORITHM=INPLACE
-- 比如:修改列类型(大部分情况)、删除主键等
ALTER TABLE orders MODIFY COLUMN amount DECIMAL(12,2), ALGORITHM=INPLACE;
-- 如果报错,说明需要 COPY(锁表),需要借助 pt-online-schema-change

2. 主从延迟

MySQL 的主从延迟问题比 PostgreSQL 更常见(因为 Binlog 是逻辑复制,而 PostgreSQL 的流复制是物理复制)。

场景选型建议

场景推荐理由
新项目(无历史包袱)PostgreSQL功能更完善,SQL 标准支持好
复杂查询/分析PostgreSQL查询优化器更强
地理空间数据PostgreSQL + PostGIS生态最好
AI 向量搜索PostgreSQL + pgvector减少组件
高并发简单 OLTPMySQL线程模型有一定优势
与现有 MySQL 生态集成MySQL避免引入新技术栈
WordPress/常见 CMSMySQL生态兼容
金融/强一致性要求PostgreSQL更严格的标准遵循
时序数据PostgreSQL + TimescaleDB一个数据库搞定
简单的 KV 存储两者皆可甚至可以考虑 Redis

从 MySQL 迁移到 PostgreSQL 的注意事项

如果决定迁移,以下是我们总结的关键点:

语法差异

-- 反引号 vs 双引号
-- MySQL
SELECT `user_id`, `created_at` FROM `users`;

-- PostgreSQL
SELECT "user_id", "created_at" FROM "users";
-- 或者(推荐):不用引号
SELECT user_id, created_at FROM users;

-- LIMIT OFFSET
-- MySQL
SELECT * FROM users LIMIT 10 OFFSET 20;

-- PostgreSQL(两种语法都支持)
SELECT * FROM users LIMIT 10 OFFSET 20;
SELECT * FROM users OFFSET 20 LIMIT 10;

-- UPDATE JOIN
-- MySQL
UPDATE orders o JOIN users u ON o.user_id = u.id
SET o.user_name = u.name;

-- PostgreSQL
UPDATE orders o
SET user_name = u.name
FROM users u
WHERE o.user_id = u.id;

数据类型映射

MySQLPostgreSQL
TINYINTSMALLINT
DATETIMETIMESTAMP
TEXTTEXT
BLOBBYTEA
ENUM自定义 TYPE 或 CHECK 约束
JSONJSONB(推荐)/ JSON
AUTO_INCREMENTSERIAL / BIGSERIAL / GENERATED AS IDENTITY

迁移工具

# pgloader:最成熟的 MySQL → PostgreSQL 迁移工具
pgloader mysql://user:pass@host/dbname postgresql://user:pass@host/dbname

# 配置文件方式
# migration.load
LOAD DATABASE
  FROM mysql://user:pass@localhost/source_db
  INTO postgresql://user:pass@localhost/target_db
WITH 
  create_tables,
  create_indexes,
  reset_sequences,
  disable_triggers
SET 
  maintenance_work_mem to '128MB',
  work_mem to '12MB'
CAST 
  type datetime to timestamptz drop default drop not null using zero-dates-to-null;

总结

PostgreSQL 和 MySQL 都是成熟的开源数据库,选择哪个取决于:

  1. 默认选 PostgreSQL:除非有明确理由选 MySQL
  2. MySQL 的优势:简单 OLTP、现有生态、运维人员熟悉度
  3. PostgreSQL 的优势:复杂查询、扩展能力、SQL 标准支持、数据完整性
  4. 不要纠结”哪个更好”:两个都足够好,选你团队最熟悉的
  5. 性能差异在大多数场景可以忽略:应用层优化比数据库选型重要得多

Comments

Link copied to clipboard!