PostgreSQL与MySQL对比小结
一、发展历史、社区生态与定位
1.1 MySQL 的发展简史
- 1995年:MySQL 由瑞典 MySQL AB 公司发布,主打“轻量、高速、易用”。
- 2008年:被 Sun 收购(后并入 Oracle)。
- 当前版本:主流为 8.0 系列,支持 CTE、Window Function、JSON 等现代特性。
- 定位:互联网应用首选,适合高并发读写、架构简单的场景。
1.2 PostgreSQL 的发展简史
- 1986年:起源于加州大学伯克利分校 POSTGRES 项目,是 Ingres 的后继者。
- 1996年:正式命名为 PostgreSQL,全面支持 SQL 标准。
- 当前版本:主流为 15/16 系列,支持并行查询、逻辑复制、JSONB、窗口函数等。
- 定位:企业级数据库,强调标准化、一致性、可扩展性,被誉为“开源界的 Oracle”。
1.3 生态与社区对比
对比维度 | MySQL | PostgreSQL |
---|---|---|
官方维护方 | Oracle | 由全球开发者组成的 PostgreSQL Global Development Group 维护 |
分支版本 | MariaDB、Percona | Citus、TimescaleDB、CockroachDB |
社区生态 | 商业化生态成熟 | 开源生态繁荣、可扩展性强 |
特点总结 | 性能优先、简洁易用 | 标准化优先、功能全面 |
二、数据类型与SQL语法对比
数值类型
-- MySQL 数值类型
CREATE TABLE numeric_demo_mysql (id INT AUTO_INCREMENT PRIMARY KEY,tiny_num TINYINT, -- 1字节整数 (-128 到 127)small_num SMALLINT, -- 2字节整数medium_num MEDIUMINT, -- 3字节整数 (-8388608 到 8388607)integer_num INT, -- 4字节整数big_num BIGINT, -- 8字节整数decimal_num DECIMAL(10,2), -- 精确小数float_num FLOAT, -- 4字节浮点数double_num DOUBLE, -- 8字节浮点数bit_num BIT(8) -- 位字段类型
);
-- PostgreSQL 数值类型
CREATE TABLE numeric_demo_pg (id SERIAL PRIMARY KEY,small_num SMALLINT, -- 2字节整数 (-32768 到 32767)integer_num INTEGER, -- 4字节整数 (标准选择)big_num BIGINT, -- 8字节整数decimal_num DECIMAL(10,2), -- 精确小数,推荐使用numeric_num NUMERIC(8,4), -- 精确小数,与DECIMAL相同real_num REAL, -- 4字节浮点数 (6位精度)double_num DOUBLE PRECISION, -- 8字节浮点数 (15位精度)serial_num SERIAL, -- 自增整数 (1 到 2147483647)bigserial_num BIGSERIAL -- 自增大整数
);
关键差异:
- PostgreSQL 没有 TINYINT、MEDIUMINT,但提供 SERIAL 自增类型
- MySQL 提供更多整数变种,BIT 类型更完善
- PostgreSQL 的 DECIMAL 和 NUMERIC 完全等价,都提供精确计算
字符串类型
-- MySQL 字符串类型
CREATE TABLE string_demo_mysql (id INT AUTO_INCREMENT PRIMARY KEY,fixed_char CHAR(10),variable_char VARCHAR(255), -- 实际长度依赖字符集tiny_text TINYTEXT, -- 最大255字节text_field TEXT, -- 最大65,535字节medium_text MEDIUMTEXT, -- 最大16,777,215字节long_text LONGTEXT, -- 最大4,294,967,295字节binary_data BLOB, -- 二进制大对象enum_field ENUM('active', 'inactive') -- 内置枚举类型
);-- PostgreSQL 字符串类型
CREATE TYPE USER_DEFINED_ENUM AS ENUM ('111','222','333');CREATE TABLE string_demo_pg (id SERIAL PRIMARY KEY,-- 定长字符串,不足补空格fixed_char CHAR(10),-- 变长字符串,推荐使用variable_char VARCHAR(255),-- 无长度限制文本unlimited_text TEXT,-- 支持特殊字符的文本byte_data BYTEA,-- 枚举类型(需要先创建类型)status USER_DEFINED_ENUM
);
关键差异:
- PostgreSQL 的 TEXT 类型性能与 VARCHAR 相当,推荐使用 TEXT
- MySQL 提供多种 TEXT 变种,各有长度限制
- PostgreSQL 枚举需要先定义类型,MySQL 枚举直接内联定义
时间与时区
-- MySQL 时间类型
CREATE TABLE timestamp_demo_mysql (id INT AUTO_INCREMENT PRIMARY KEY,birth_date DATE,alarm_time TIME,created_at TIMESTAMP, -- 带时区转换updated_at DATETIME, -- 不带时区year_field YEAR -- MySQL特有
);INSERT INTO timestamp_demo_mysql
(birth_date, alarm_time, created_at, updated_at, year_field)
VALUES ('1990-05-15','08:30:00','2023-10-01 10:00:00','2023-10-01 10:00:00',2023
);-- PostgreSQL 时间类型
CREATE TABLE timestamp_demo_pg (id SERIAL PRIMARY KEY,-- 日期(无时间)birth_date DATE,-- 时间(无日期)alarm_time TIME,-- 时间戳(无时区)created_at TIMESTAMP,-- 时间戳(带时区,推荐使用)updated_at TIMESTAMPTZ,-- 时间间隔duration INTERVAL,-- 时间范围active_period TSRANGE
);INSERT INTO timestamp_demo_pg
(birth_date, alarm_time, created_at, updated_at, duration, active_period)
VALUES ('1990-05-15','08:30:00','2023-10-01 10:00:00','2023-10-01 10:00:00+08','2 hours 30 minutes','[2023-01-01 00:00:00, 2023-12-31 23:59:59)'
);
时间类型差异:
- PostgreSQL 提供 TIMESTAMPTZ(带时区)和丰富的区间类型
- MySQL 的 TIMESTAMP 会进行时区转换,DATETIME 不转换
- PostgreSQL 支持 INTERVAL 和范围类型,更适合复杂时间计算
高级数据类型对比
JSON 支持
-- MySQL JSON 支持 (5.7+)
CREATE TABLE json_demo_mysql (id INT AUTO_INCREMENT PRIMARY KEY,json_data JSON
);INSERT INTO json_demo_mysql (json_data)
VALUES ('{"name": "王五", "age": 35, "tags": ["mysql", "db"]}');-- JSON查询操作
SELECTjson_data->>'$.name' as name,json_data->'$.age' as age,json_data->>'$.tags[0]' as first_tag
FROM json_demo_mysql;-- PostgreSQL JSON 支持 (9.2+)
CREATE TABLE json_demo_pg (id SERIAL PRIMARY KEY,-- 文本格式JSON,保持空格和键顺序json_data JSON,-- 二进制JSON,推荐使用,支持索引jsonb_data JSONB,-- JSON数组json_array JSONB
);-- 插入JSON数据
INSERT INTO json_demo_pg (json_data, jsonb_data, json_array)
VALUES ('{"name": "张三", "age": 30, "tags": ["java", "pg"]}','{"name": "李四", "age": 25, "active": true}','[{"product": " laptop", "price": 5000}, {"product": "mouse", "price": 100}]'
);select * from json_demo_pg;-- JSON查询操作
SELECTjsonb_data->>'name' as name, -- 获取文本值jsonb_data->'age' as age, -- 获取JSON值json_array -> 0 -> 'price' as first_price -- 数组访问
FROM json_demo_pg;
JSON支持差异总结:
- PostgreSQL 提供 JSON 和 JSONB 两种类型,JSONB 性能更好且支持索引
- MySQL 只有一种 JSON 类型,使用二进制存储
- PostgreSQL 的 JSON 操作符更丰富直观
- MySQL 使用 ->> 和 -> 操作符,语法略有不同
数组类型
-- PostgreSQL 数组类型(MySQL不支持原生数组)
CREATE TABLE array_demo_pg (id SERIAL PRIMARY KEY,-- 整数数组user_ids INTEGER[],-- 文本数组tags TEXT[],-- 多维数组matrix INTEGER[][],-- JSONB数组preferences JSONB[]
);-- 插入数组数据
INSERT INTO array_demo_pg (user_ids, tags, matrix, preferences)
VALUES ('{1, 2, 3, 4}', -- 整数数组'{"java", "postgresql", "spring"}', -- 文本数组'{{1,2,3}, {4,5,6}}', -- 多维数组'{"{\"theme\": \"dark\"}", "{\"notifications\": true}"}' -- JSONB数组
);-- 数组查询操作
SELECTtags[1] as first_tag, -- 数组索引(从1开始)array_length(tags, 1) as tag_count, -- 数组长度user_ids || 5 as extended_ids, -- 数组连接array_position(tags, 'java') as java_position -- 元素位置
FROM array_demo_pg
WHERE 'postgresql' = ANY(tags); -- 数组包含检查
SQL语法对比
标识符
- Postgres:双引号 " 引用标识符
- MySQL:反引号 ` 引用标识符
-- MySQL
SELECT `id`, `user_name` FROM `users`;-- PostgreSQL
SELECT "id", "user_name" FROM "users";
字符串拼接与类型转换
- Postgres 支持 || 操作符和 CONCAT();MySQL 通常使用 CONCAT()。
- Postgres 支持 ::type 快捷类型转换(等价 CAST());MySQL 使用 CAST()。
示例:
-- Postgres
SELECT 'a' || 'b' AS s; -- 'ab'
SELECT '123'::int + 1; -- 124-- MySQL
SELECT CONCAT('a','b') AS s; -- 'ab'
SELECT CAST('123' AS UNSIGNED) + 1; -- 124
UPSERT(冲突处理)
Postgres:INSERT ... ON CONFLICT (cols) DO UPDATE/DO NOTHING
(可使用 EXCLUDED 表示拟插入行)。
MySQL:INSERT ... ON DUPLICATE KEY UPDATE
。
示例:
-- Postgres
INSERT INTO t(id, v) VALUES (1,10)ON CONFLICT (id) DO UPDATE SET v = t.v + EXCLUDED.v;-- MySQL
INSERT INTO t(id, v) VALUES (1,10)ON DUPLICATE KEY UPDATE v = v + VALUES(v);
基本建表语法结构
两者的基本语法格式类似:
-- PostgreSQL
CREATE TABLE table_name (column_name data_type [constraint],...
);-- MySQL
CREATE TABLE table_name (column_name data_type [constraint],...
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
但 PostgreSQL 没有 ENGINE、CHARSET 概念,因为它本身只有一种核心存储机制(MVCC 引擎),而 MySQL 可以切换存储引擎和字符集。
窗口函数(Window Functions)
两者都支持标准窗口函数,但 PostgreSQL 支持更早、更完整。
-- 计算每个用户的订单排名
SELECT user_id,order_amount,RANK() OVER (PARTITION BY user_id ORDER BY order_amount DESC) as rank
FROM orders;-- 计算累计求和
SELECT order_date,amount,SUM(amount) OVER (ORDER BY order_date) as running_total
FROM daily_sales;
CTE(公用表表达式)
两者都支持 WITH
子句,但 PostgreSQL 的 CTE 性能优化更好(尤其是递归CTE)。
-- 计算部门层级结构(递归CTE)
WITH RECURSIVE org_tree AS (SELECT id, name, parent_id, 1 as levelFROM departments WHERE parent_id IS NULLUNION ALLSELECT d.id, d.name, d.parent_id, ot.level + 1FROM departments dJOIN org_tree ot ON d.parent_id = ot.id
)
SELECT * FROM org_tree ORDER BY level;-- 递归示例2
WITH RECURSIVE cte AS (SELECT 1 AS nUNION ALLSELECT n+1 FROM cte WHERE n < 5
)
SELECT * FROM cte;
三、索引机制
MySQL 的索引类型
- BTREE(默认)
- FULLTEXT (全文索引)
- HASH(仅 MEMORY 引擎)
- SPATIAL(空间索引)
PostgreSQL 索引类型
- BTREE:通用索引
- HASH:哈希索引
- GIN:适用于 JSON、数组全文搜索
- GiST:适用于空间/模糊匹配
- BRIN:大数据顺序访问优化
- SP-GiST、Bloom:特殊用途索引
差异:
PostgreSQL 的索引机制是可扩展的,支持 BTree, GIN, GiST, SP-GiST, BRIN, Hash 等多种索引类型。MySQL 仅支持少数固定索引类型,而类型依赖具体的存储引擎(如 InnoDB、MyISAM)。
四、事务与并发控制对比
两者的 MVCC(多版本并发控制)机制
对比点 | MySQL (InnoDB) | PostgreSQL |
---|---|---|
MVCC实现 | Undo Log + 隐藏列 | 多版本快照存储(Tuple Versioning) |
行版本清理 | 通过 Purge 线程 | 通过 VACUUM |
锁类型 | 行锁、间隙锁 | 行级锁,无间隙锁 |
并发性能 | 高,但可能死锁 | 高,读写无锁化更彻底 |
五、总结与选型建议
对比维度 | MySQL | PostgreSQL |
---|---|---|
性能 | 读写性能优异 | 稍逊但稳定 |
事务一致性 | 较弱(依赖隔离级别) | 强一致 |
JSON 支持 | 一般 | 强大 |
扩展性 | 中 | 高(自定义类型/函数) |
分布式能力 | TiDB 等衍生产品 | 原生扩展支持 |
学习曲线 | 低 | 稍高但值得 |
适用场景 | Web、电商、CMS | 金融、分析、日志、微服务 |
建议:
MySQL:如果你的项目是传统Web应用、读多写少、团队熟悉MySQL、追求快速部署和主从复制的简单性。
PostgreSQL:如果你的应用涉及复杂分析、GIS、JSON文档存储、高并发写入或需要高度可扩展性,PostgreSQL是更强大、更长远的选择。