MySQL与PostgreSQL新特性对比 - 2024年最新功能分析

引言

数据库选型是项目初期最重要的技术决策之一。MySQL 和 PostgreSQL 作为两款顶级开源relational-database,在 2024 年都进化出了强大的新特性。本文将以实际开发场景为线索,对比它们在 JSON 支持、分区表、并行处理、安全性等方面的差异,帮你快速判断哪一款更适合你的项目。

本文旨在帮助开发者根据具体需求快速判断哪款数据库更适合当前项目。

1. 版本发展概述

1.1 MySQL版本演进

MySQL 在 8.0 版本迎来了一次“觉醒式”更新,补齐了之前缺失的许多现代 SQL 功能,之后的小版本持续优化性能和稳定性。

  • 8.0:窗口函数、递归 CTE、角色管理、不可见索引、直方图统计等
  • 8.0.x 后续版本:持续增强 JSON 操作、安全性、查询优化器

1.2 PostgreSQL版本演进

PostgreSQL 则一直保持着高速迭代节奏,每年一个大版本,功能不断堆叠,尤其在advanced-features和扩展性方面持续领先。

  • 10+:声明式分区、逻辑复制、更强的并行查询
  • 14+:JSONB 性能飞跃、即时压缩、更丰富的窗口函数
  • 15-16:性能全面提升、逻辑复制增强、新增更多企业级安全特性

2. JSON支持对比

现代应用中,半结构化数据存储已成为刚需。两款数据库都支持 JSON 类型,但实现方式和能力存在显著差距。

2.1 MySQL JSON特性

MySQL 8.x 提供了基础的 JSON 数据类型和一组内置函数,能满足简单读写需求。但在高级查询和索引方面,需要借助“虚拟列”间接实现。

-- 创建包含 JSON 列的表
CREATE TABLE user_profiles (
    id INT PRIMARY KEY,
    profile JSON
);

-- 插入 JSON 数据
INSERT INTO user_profiles VALUES (
    1, 
    JSON_OBJECT(
        'name', 'John Doe',
        'skills', JSON_ARRAY('Python', 'JavaScript')
    )
);

-- 基本查询:提取 JSON 字段
SELECT 
    id, 
    JSON_EXTRACT(profile, '$.name') AS name
FROM user_profiles
WHERE JSON_EXTRACT(profile, '$.age') > 25;

-- 索引实现:通过生成列构建 B-Tree 索引
ALTER TABLE user_profiles 
ADD COLUMN primary_skill VARCHAR(50) 
GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(profile, '$.skills[0]'))) STORED;
CREATE INDEX idx_primary_skill ON user_profiles(primary_skill);

MySQL JSON 小结:

  • 使用 JSON_EXTRACT 函数提取值,语法较为传统
  • 原生不支持 JSON 内部字段索引,必须通过虚拟生成列“曲线救国”
  • 适合 JSON 存储与轻度查询,复杂 JSON 路径操作较繁琐

2.2 PostgreSQL JSON特性

PostgreSQL 提供 jsonjsonb 两种类型。其中 jsonb 是二进制优化版本,支持高效查询和索引,是推荐使用的类型。

-- 创建表并选择 jsonb 类型
CREATE TABLE user_profiles (
    id SERIAL PRIMARY KEY,
    profile_jsonb JSONB
);

-- 插入数据,使用 ::jsonb 进行类型转换
INSERT INTO user_profiles (profile_jsonb) VALUES (
    '{
        "name": "John Doe",
        "skills": ["Python", "JavaScript"]
    }'::jsonb
);

-- 丰富的操作符查询
SELECT 
    id,
    profile_jsonb->>'name' AS name,          -- 提取文本值
    profile_jsonb->'skills' AS skills       -- 提取 JSON 数组
FROM user_profiles
WHERE profile_jsonb @> '{"skills": ["Python"]}'::jsonb;  -- 包含查询

-- 原生 GIN 索引,直接加速 JSON 内部元素检索
CREATE INDEX idx_profile_gin ON user_profiles USING GIN (profile_jsonb);

PostgreSQL JSON 小结:

  • 采用 ->->>@> 等简洁操作符进行路径查询和包含查询
  • 原生 GIN 索引可在 JSON 内部字段上直接建立,查询效率极高
  • 适合需要频繁查询、更新 JSON 内部数据的场景,尤其是复杂文档模型

2.3 JSON功能对比总结

特性MySQLPostgreSQL
存储方式JSON / JSON_BINARYJSON(文本) / JSONB(二进制)
索引支持需通过虚拟生成列建立 B-Tree 索引原生 GIN / GiST 索引,无需额外处理
查询语法JSON_EXTRACT 等函数->->>@> 等操作符,直观简洁
复杂查询较有限,嵌套路径拼接繁琐强大的路径表达式与包含查找,适合文档查询

3. 窗口函数与CTE

3.1 MySQL窗口函数(8.0+)

MySQL 8.0 终于支持了窗口函数和递归 CTE,让很多复杂报表和层次查询不再需要自连接或存储过程。

-- 窗口函数:部门内按薪水排名
SELECT 
    employee_id,
    department_id,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dept_rank
FROM employees;

-- 递归CTE:组织架构层次查询
WITH RECURSIVE org_hierarchy AS (
    SELECT employee_id, manager_id, 1 AS level
    FROM employees 
    WHERE manager_id IS NULL
    
    UNION ALL
    
    SELECT e.employee_id, e.manager_id, oh.level + 1 
    FROM employees e 
    JOIN org_hierarchy oh ON e.manager_id = oh.employee_id
)
SELECT * FROM org_hierarchy;

3.2 PostgreSQL窗口函数与CTE

PostgreSQL 的窗口函数支持更全面,CTE 还可以指定为 MATERIALIZEDNOT MATERIALIZED 以控制优化行为,这对复杂查询的性能调优非常有用。

-- 更丰富的窗口函数:分位数和分组编号
SELECT 
    employee_id,
    salary,
    PERCENT_RANK() OVER (ORDER BY salary) AS salary_percentile,
    NTILE(4) OVER (ORDER BY salary) AS salary_quartile
FROM employees;

-- 物化CTE:将子查询结果暂时固化,避免重复计算
WITH large_dataset AS MATERIALIZED (
    SELECT * FROM very_large_table WHERE condition = 'value'
)
SELECT * FROM large_dataset WHERE other_condition = 'other_value';

4. 分区表功能

当单表数据量达到千万甚至亿级别时,分区表是管理大数据的利器。

4.1 MySQL分区(8.0+)

MySQL 支持范围、列表和哈希分区,通过 PARTITION BY RANGE 等语法声明。缺点是分区维护操作(如拆分、合并)相对繁琐。

-- 按年份范围分区
CREATE TABLE sales (
    id INT NOT NULL,
    sale_date DATE NOT NULL,
    amount DECIMAL(10,2)
) PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

4.2 PostgreSQL分区(10+)

PostgreSQL 使用声明式分区,父表定义分区方式,子表通过 PARTITION OF 绑定。这种做法让分区之间的边界更清晰,且索引可以自动从父表传播到子表。

-- 声明式分区父表
CREATE TABLE sales (
    id SERIAL,
    sale_date DATE NOT NULL,
    amount DECIMAL(10,2)
) PARTITION BY RANGE (sale_date);

-- 创建具体分区子表
CREATE TABLE sales_2022 PARTITION OF sales
    FOR VALUES FROM ('2022-01-01') TO ('2023-01-01');

CREATE TABLE sales_2023 PARTITION OF sales
    FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');

-- 在父表上创建索引,会自动应用到所有分区
CREATE INDEX idx_sales_amount ON sales (amount);

分区对比小结:

  • MySQL 分区语法简单,适合固定规则的大表拆分
  • PostgreSQL 声明式分区逻辑更清晰,支持自动索引传播,后期维护更方便

5. 并行处理能力

并行查询在处理大数据集聚合、排序、扫描时能显著提升性能。

  • MySQL:并行处理能力相对保守,主要依赖查询优化器的自动判断,用户可控参数较少。
  • PostgreSQL:拥有成熟的并行执行框架,支持并行顺序扫描、并行聚合、并行哈希连接等,并可通过 max_parallel_workers_per_gather 等参数灵活控制。
-- PostgreSQL 并行查询执行计划查看
EXPLAIN (ANALYZE, BUFFERS) 
SELECT department_id, COUNT(*), AVG(salary)
FROM employees 
GROUP BY department_id;

在 PostgreSQL 的 EXPLAIN 输出中,如果看到 Parallel Seq ScanParallel Hash 等字眼,说明查询正在有效利用多核 CPU。

6. 安全特性

6.1 MySQL安全增强

MySQL 8.0 加强了账户管理和密码安全策略,有效防止暴力破解。

-- 安装密码强度校验组件
INSTALL COMPONENT 'file://component_validate_password';
SET GLOBAL validate_password.policy = MEDIUM;

-- 创建带密码过期和登录失败锁定的账户
CREATE USER 'app_user'@'%' 
IDENTIFIED BY 'StrongPassword123!'
PASSWORD EXPIRE INTERVAL 90 DAY
FAILED_LOGIN_ATTEMPTS 3;

6.2 PostgreSQL安全特性

PostgreSQL 的一大亮点是行级安全 (Row-Level Security, RLS),可以在数据库层面强制实行细粒度的数据隔离,多租户 SaaS 应用中非常实用。

-- 开启行级安全
ALTER TABLE employees ENABLE ROW LEVEL SECURITY;

-- 创建策略:每个租户只能看到自己部门的数据
CREATE POLICY employee_isolation_policy ON employees
    FOR ALL
    TO app_user
    USING (department_id = current_setting('app.current_dept')::integer);

此外,PostgreSQL 还支持 GSSAPI、SCRAM-SHA-256 等多种强认证方式,安全体系更为完整。

7. 性能优化特性

7.1 MySQL性能优化

MySQL 8.0 提供了两个实用工具:不可见索引可安全地测试索引有效性,直方图统计则帮助优化器更准确地估算查询成本。

-- 创建不可见索引,优化器默认不使用
CREATE INDEX idx_invisible ON employees (hire_date) INVISIBLE;

-- 更新直方图统计信息
ANALYZE TABLE employees UPDATE HISTOGRAM ON salary, department_id;

7.2 PostgreSQL性能优化

PostgreSQL 的索引机制更加丰富:

-- 部分索引:只索引满足条件的行,节省空间并提高速度
CREATE INDEX idx_active_employees ON employees (hire_date) 
WHERE status = 'active';

-- 覆盖索引:包含额外列,避免回表查询
CREATE INDEX idx_covering ON employees (department_id, salary) 
INCLUDE (first_name, last_name);

8. 扩展性和插件系统

PostgreSQL 真正的杀手锏之一就是其插件生态,允许开发者以插件形式添加 GIS、全文搜索、时序数据等各种能力。MySQL 的扩展则相对有限,通常需要依靠第三方工具或存储引擎。

-- PostgreSQL 常用扩展
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;   -- 查询性能统计
CREATE EXTENSION IF NOT EXISTS pg_trgm;              -- 三元组文本相似度
CREATE EXTENSION IF NOT EXISTS postgis;               -- 地理空间数据支持

9. 新特性对比总结

特性MySQL 8.xPostgreSQL 14+
JSON支持基础存储与查询,虚拟列索引高效 JSONB,原生 GIN 索引,强大操作符
窗口函数8.0+ 支持常用函数原生支持,函数更丰富,支持物化 CTE
分区表范围/列表/哈希分区声明式分区,自动索引传播,维护方便
并行处理有限,依赖优化器强大并行框架,支持并行扫描、聚合、连接
行级安全企业版功能开源标准特性,策略灵活
扩展系统有限存储引擎与插件丰富生态系统,可无限拓展能力

10. 选择建议

选择MySQL的场景

  • 经典的 Web 应用、CRUD 型项目,与 PHP、Python Web 框架集成开发效率高
  • 依赖云服务商的托管方案(如 Amazon RDS、Google Cloud SQL),运维成本低
  • 团队已有大量 MySQL 运维经验,或项目预算对高级 DBA 投入有限
  • 数据关系相对简单,不需要频繁使用复杂 SQL 特性

选择PostgreSQL的场景

  • 需要处理复杂的数据关系,依赖高级 SQL 功能(窗口分析、递归查询、物化视图等)
  • 应用涉及半结构化数据(JSONB)或地理空间数据(PostGIS),需要高效的专用查询
  • 多租户 SaaS 系统,对数据隔离与行级安全有强需求
  • 企业级应用,追求极高的数据完整性和可靠性保障
  • 希望利用丰富的扩展生态,不断演进数据库能力

相关教程

在选择数据库时,不仅要考虑当前需求,还要考虑未来的发展潜力。PostgreSQL在功能丰富性方面领先,而MySQL在易用性和生态方面有优势。

总结

2024 年,MySQL 8.x 和 PostgreSQL 14+ 的差距正在逐步缩小,但各自依然保持着鲜明特色:

  • MySQL:简单、易上手、生态成熟,在 Web 应用和云托管场景中表现稳健
  • PostgreSQL:功能完备、扩展性强,在复杂业务、数据分析和对数据质量要求高的领域优势明显

最终的选择取决于你的项目需求、团队技术栈以及未来扩展规划。没有绝对的“最好”,只有最适合你当下和未来目标的数据库。希望本文的对比能帮你做出更清晰的判断。