MySQL 迁移到 PostgreSQL(PG),最大的问题通常不是数据,而是:
很多 MySQL 能执行的 DDL,到了 PostgreSQL 会直接报错。
下面按实际迁移项目经验,整理一个较完整的 DDL 兼容性说明。
| 项目 | MySQL | PostgreSQL |
|---|---|---|
| 大小写 | 不敏感 | 敏感 |
| 自增 | AUTO_INCREMENT | SERIAL/IDENTITY |
| 存储引擎 | InnoDB/MyISAM | 无 |
| 字符集 | 表级/库级 | 数据库级 |
| ENUM | 原生支持 | 需单独创建TYPE |
| COMMENT | 语法不同 | 使用 COMMENT ON |
| UNSIGNED | 支持 | 不支持 |
| ON UPDATE CURRENT_TIMESTAMP | 支持 | 不支持 |
| TINYINT | 支持 | 无 |
| DATETIME | 支持 | timestamp |
| 双引号 | 字符串可兼容 | 双引号表示对象名 |
CREATE TABLE user_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY
);
PostgreSQL 不支持:
AUTO_INCREMENT
需要改成:
CREATE TABLE user_info (
id BIGSERIAL PRIMARY KEY
);
CREATE TABLE user_info (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
);
很多迁移工具:
会导致:
duplicate key value violates unique constraint
因为 sequence 没同步。
迁移后必须:
SELECT setval(
'user_info_id_seq',
( SELECT MAX(id) FROM user_info)
);
status TINYINT
无 TINYINT。
通常改:
SMALLINT
或者:
BOOLEAN
id INT UNSIGNED
不支持:
UNSIGNED
需要扩大类型:
| MySQL | PostgreSQL |
|---|---|
| INT UNSIGNED | BIGINT |
| SMALLINT UNSIGNED | INTEGER |
create_time DATETIME
改:
create_time TIMESTAMP
建议:
TIMESTAMP WITHOUT TIME ZONE
或者:
TIMESTAMP WITH TIME ZONE
建议:
DOUBLE PRECISION
TEXT
LONGTEXT
BLOB
通常:
| MySQL | PostgreSQL |
|---|---|
| TEXT | TEXT |
| LONGTEXT | TEXT |
| BLOB | BYTEA |
JSON
建议:
JSONB
因为:
支持:
CHARSET =utf8mb4
COLLATE=utf8mb4_general_ci
不支持表级:
CHARSET=
不支持:
ENGINE=
这些必须删除。
syntax error at or near "ENGINE"
ENGINE =InnoDB
DEFAULT CHARSET =utf8mb4
ROW_FORMAT =DYNAMIC
PG 全部不支持。
必须删掉。
MySQL 很多字段名:
user
order
group
limit
key
在 PG 中是关键字。
CREATE TABLE order (
id INT
);
需要:
CREATE TABLE "order" (
id INT
);
但:
不建议大量双引号。
直接改字段名:
| 原字段 | 建议 |
|---|---|
| order | order_no |
| user | user_name |
| group | group_name |
默认:
默认:
CREATE TABLE UserInfo
查询:
select * from USERINFO;
也能成功。
会变成:
userinfo
如果:
CREATE TABLE "UserInfo"
以后必须:
SELECT * FROM "UserInfo";
否则报错。
迁移时:
统一转小写。
例如:
user_info
order_detail
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
不支持:
ON UPDATE CURRENT_TIMESTAMP
需要:
实现。
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.update_time = now();
RETURN NEW;
END;
$$ language 'plpgsql';
name VARCHAR(100) COMMENT '姓名'
需要:
COMMENT ON COLUMN user_info.name IS '姓名';
CREATE INDEX idx_name ON t(name(20));
不支持前缀索引。
需要:
FULLTEXT INDEX
使用:
GIN
to_tsvector
实现。
MySQL:
很多系统:
PG:
外键更严格。
迁移时:
可能因为脏数据失败。
insert or update violates foreign key constraint
迁移前:
先清洗脏数据。
有时:
'' == NULL
逻辑混乱。
严格区分:
''
NULL
| MySQL | PostgreSQL |
|---|---|
| IFNULL | COALESCE |
| NOW() | NOW() |
| SYSDATE() | CURRENT_TIMESTAMP |
| CONCAT | CONCAT |
| GROUP_CONCAT | STRING_AGG |
| DATE_FORMAT | TO_CHAR |
这是迁移难点之一。
DELIMITER //
CREATE PROCEDURE
使用:
LANGUAGE plpgsql
语法差异很大。
通常:
MySQL:
BEFORE INSERT
语法较简单。
PG:
需要:
分开创建。
MySQL:
PARTITION BY HASH
PG:
支持:
PARTITION BY RANGE/LIST/HASH
但:
实现机制不同。
很多不能直接迁移。
| 工具 | 问题 |
|---|---|
| pgloader | DDL转换不完整 |
| ora2pg | 更偏Oracle |
| DataX | 不处理DDL |
| DTS | 只同步数据 |
| 自研脚本 | 关键字遗漏 |
导出 MySQL DDL
↓
DDL 自动转换
↓
人工修正
↓
PG 测试执行
↓
迁移数据
↓
修正 sequence
↓
验证索引约束
↓
业务联调
□ AUTO_INCREMENT
□ ENGINE=
□ CHARSET=
□ COLLATE=
□ ON UPDATE CURRENT_TIMESTAMP
□ UNSIGNED
□ TINYINT
□ ENUM
□ COMMENT
□ 关键字冲突
□ 大小写问题
□ sequence同步
□ 外键脏数据
□ 存储过程
□ trigger
□ function
user_info
order_detail
不要:
UserInfo
统一:
不要:
"UserInfo"
否则后续开发很痛苦。
建议:
通常是:
| 难度 | 内容 |
|---|---|
| 高 | 存储过程 |
| 高 | trigger |
| 高 | function |
| 中 | 动态SQL |
| 中 | JSON SQL |
| 中 | 分区 |
| 低 | 普通表 |
| 场景 | 工具 |
|---|---|
| DDL转换 | pgloader |
| 数据迁移 | DataX |
| 实时同步 | Canal + Kafka + PG |
| 全量校验 | checksum |
| SQL兼容检查 | 自研脚本 |
CREATE TABLE user_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR( 100) NOT NULL,
status TINYINT DEFAULT 0,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
) ENGINE =InnoDB DEFAULT CHARSET =utf8mb4;
CREATE TABLE user_info (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR( 100) NOT NULL,
status SMALLINT DEFAULT 0,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
再增加 trigger 自动更新时间。
真正的难点不是:
而是:
尤其:
这些往往问题最多。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/10201716/viewspace-3171995/ ,如需转载,请注明出处,否则将追究法律责任。