PostgreSQL:详解 MySQL数据迁移,如何将数据平滑迁移到PostgreSQL

文章目录

    • 一、迁移前评估与准备
      • [1.1 明确迁移目标](#1.1 明确迁移目标)
      • [1.2 兼容性分析](#1.2 兼容性分析)
      • [1.3 数据量与停机窗口评估](#1.3 数据量与停机窗口评估)
    • 二、迁移工具选型与对比
      • [2.1 主流工具概览](#2.1 主流工具概览)
    • [三、使用 pgloader 进行自动化迁移(推荐方案)](#三、使用 pgloader 进行自动化迁移(推荐方案))
      • [3.1 安装 pgloader](#3.1 安装 pgloader)
      • [3.2 编写迁移配置文件(`migrate.load`)](#3.2 编写迁移配置文件(migrate.load))
      • [3.3 关键配置说明](#3.3 关键配置说明)
      • [3.4 执行迁移](#3.4 执行迁移)
    • 四、数据类型与函数转换详解
      • [4.1 常见数据类型映射](#4.1 常见数据类型映射)
      • [4.2 函数与操作符转换](#4.2 函数与操作符转换)
    • 五、处理自增主键与序列
      • [5.1 MySQL 的 AUTO_INCREMENT](#5.1 MySQL 的 AUTO_INCREMENT)
      • [5.2 PostgreSQL 的实现方式](#5.2 PostgreSQL 的实现方式)
      • [5.3 迁移后修复序列值](#5.3 迁移后修复序列值)
    • 六、索引、约束与性能调优
      • [6.1 索引转换](#6.1 索引转换)
      • [6.2 外键与约束](#6.2 外键与约束)
      • [6.3 性能调优建议](#6.3 性能调优建议)
    • [七、应用层适配与 SQL 改造](#七、应用层适配与 SQL 改造)
      • [7.1 ORM 适配](#7.1 ORM 适配)
      • [7.2 常见 SQL 改造点](#7.2 常见 SQL 改造点)
      • [7.3 事务与锁行为差异](#7.3 事务与锁行为差异)
    • 八、数据一致性验证
      • [8.1 行数校验](#8.1 行数校验)
      • [8.2 校验和比对(关键表)](#8.2 校验和比对(关键表))
      • [8.3 抽样比对](#8.3 抽样比对)
      • [8.4 应用端冒烟测试](#8.4 应用端冒烟测试)
    • 九、上线切换策略
      • [9.1 停机迁移(小系统)](#9.1 停机迁移(小系统))
      • [9.2 双写 + 切读(中大型系统)](#9.2 双写 + 切读(中大型系统))
      • [9.3 CDC 实时同步(零停机)](#9.3 CDC 实时同步(零停机))
    • 十、常见问题
      • [10.1 字符集与排序规则](#10.1 字符集与排序规则)
      • [10.2 0000-00-00 日期问题](#10.2 0000-00-00 日期问题)
      • [10.3 反引号与关键字冲突](#10.3 反引号与关键字冲突)
      • [10.4 大对象(BLOB)迁移](#10.4 大对象(BLOB)迁移)

将数据从 MySQL 平滑迁移到 PostgreSQL 是许多企业因成本、开源策略、功能需求或云架构调整而采取的关键举措。尽管两者均为关系型数据库,但在 SQL 语法、数据类型、事务模型、索引机制、函数生态 等方面存在显著差异。若处理不当,迁移过程易出现数据丢失、性能下降、应用中断等问题。

参考资源:


一、迁移前评估与准备

1.1 明确迁移目标

  • 降低成本:摆脱 MySQL 商业许可(如 Oracle MySQL Enterprise)
  • 功能增强:利用 PG 的 JSONB、GIS(PostGIS)、全文检索、窗口函数、物化视图等高级特性
  • 架构统一:已有 PG 技术栈,希望统一数据库平台
  • 云战略:迁移到 AWS RDS/Aurora PostgreSQL、Azure Database for PostgreSQL 等托管服务

1.2 兼容性分析

使用工具扫描 MySQL 模式,识别潜在问题:

差异点 MySQL PostgreSQL 迁移影响
标识符大小写 默认不区分(Linux 区分) 严格区分("Name"name 需统一命名规范
字符串比较 忽略尾部空格 严格比较 可能导致 JOIN/UNIQUE 失败
布尔类型 无原生 BOOLEAN,用 TINYINT(1) 原生 BOOLEAN 需转换字段类型
自增主键 AUTO_INCREMENT SERIAL / IDENTITY DDL 需重写
LIMIT/OFFSET LIMIT offset, count LIMIT count OFFSET offset 应用 SQL 需调整
字符串连接 CONCAT() 或 ` `(需开启)
日期时间 DATETIME 无时区 TIMESTAMP / TIMESTAMPTZ 时区处理需谨慎
默认值函数 NOW(), CURDATE() CURRENT_TIMESTAMP, CURRENT_DATE 函数替换
反引号 table 双引号 "table" 或无引号 需转义或移除

推荐工具:Ora2Pg (虽名含 Ora,但支持 MySQL 模式解析)、pgloader 内置检查、自定义脚本。

1.3 数据量与停机窗口评估

  • 小数据量(< 100 GB):可接受短暂停机(分钟级)
  • 中大数据量(100 GB ~ 10 TB) :需采用 在线迁移 + 增量同步,最小化停机时间
  • 超大数据量(> 10 TB):考虑分库分表迁移、并行导出导入

二、迁移工具选型与对比

2.1 主流工具概览

工具 类型 优点 缺点 适用场景
pgloader 开源 ETL 自动转换 DDL/DML,支持在线迁移,速度快 复杂函数/存储过程不支持 中小规模,结构简单
AWS DMS 托管服务 支持 CDC(变更数据捕获),零停机 费用高,需 AWS 环境 云上迁移,大企业
MySQL to PostgreSQL (from Intelligent Converters) 商业软件 图形界面,支持触发器/存储过程 闭源,收费 无开发资源团队
自定义脚本(mysqldump + sed + psql) 手动 完全可控 易出错,维护成本高 特殊定制需求
Debezium + Kafka + Sink Connector 流式架构 实时同步,高可用 架构复杂 超大规模,高可用要求

推荐组合

  • 中小项目pgloader(主力) + 手动修正
  • 大型项目AWS DMSDebezium(增量) + pgloader(全量)

三、使用 pgloader 进行自动化迁移(推荐方案)

3.1 安装 pgloader

bash 复制代码
# Ubuntu/Debian
sudo apt install pgloader

# macOS
brew install pgloader

# 源码编译(推荐最新版)
git clone https://github.com/dimitri/pgloader.git
cd pgloader
make pgloader

3.2 编写迁移配置文件(migrate.load

lisp 复制代码
LOAD DATABASE
     FROM mysql://user:pass@mysql-host:3306/source_db
     INTO postgresql://pguser:pgpass@pg-host:5432/target_db

WITH include only table names like 'users', 'orders',
     create tables,
     create indexes,
     reset sequences,
     workers = 8,
     concurrency = 1

CAST type datetime to timestamptz drop default drop not null,
     type date to date drop default,
     type tinyint to boolean using inline cast,
     type varchar to varchar drop typemod

AFTER LOAD DO
$$
  ALTER TABLE users ALTER COLUMN created_at SET DEFAULT NOW();
  CREATE INDEX CONCURRENTLY idx_orders_user_id ON orders(user_id);
$$;

3.3 关键配置说明

  • include only table names:按需迁移表,避免无关数据
  • create tables:自动建表(基于转换后 DDL)
  • reset sequences:修复自增 ID 序列值
  • CAST:类型映射规则(核心!)
    • datetime → timestamptz:保留时区信息
    • tinyint(1) → boolean:将 0/1 转为 true/false
    • varchar(N) → varchar:移除长度限制(PG 支持无限长)
  • AFTER LOAD DO:迁移后执行 SQL(如重建索引、设默认值)

3.4 执行迁移

bash 复制代码
pgloader migrate.load

输出包含错误日志、性能统计、未处理对象列表,便于排查。


四、数据类型与函数转换详解

4.1 常见数据类型映射

MySQL 类型 PostgreSQL 推荐类型 注意事项
INT, BIGINT INT4, INT8 直接映射
TINYINT(1) BOOLEAN 仅当表示真假时
TINYINT(n>1) SMALLINT 避免误转 BOOLEAN
VARCHAR(N) VARCHARTEXT PG 无性能差异,建议用 TEXT
TEXT, MEDIUMTEXT TEXT 统一为 TEXT
DATETIME TIMESTAMP WITHOUT TIME ZONE 若无时区需求
DATETIME TIMESTAMPTZ 若需时区转换(推荐)
DATE DATE 直接映射
DECIMAL(M,D) NUMERIC(M,D) 精确数值
FLOAT, DOUBLE REAL, DOUBLE PRECISION 直接映射
ENUM TEXT + CHECK 约束 或 自定义 DOMAIN PG 不推荐 ENUM
JSON JSONB 推荐 JSONB(支持索引、更快)
BLOB BYTEA 二进制数据

4.2 函数与操作符转换

MySQL 表达式 PostgreSQL 等效写法
NOW() CURRENT_TIMESTAMP
CURDATE() CURRENT_DATE
IFNULL(a, b) COALESCE(a, b)
ISNULL(a) a IS NULL
CONCAT(a, b) `a
SUBSTRING(str, pos, len) SUBSTR(str, pos, len)(pos 从 1 开始)
RAND() RANDOM()
LIMIT 10, 20 LIMIT 20 OFFSET 10
column "column" 或直接 column(若无关键字冲突)

建议:在应用层统一替换,或通过 PostgreSQL 的 自定义函数 兼容:

sql 复制代码
CREATE FUNCTION ifnull(anyelement, anyelement) RETURNS anyelement AS $$
SELECT coalesce($1, $2);
$$ LANGUAGE sql;

五、处理自增主键与序列

5.1 MySQL 的 AUTO_INCREMENT

  • 每张表独立计数器
  • 插入时可显式指定 ID(需开启 NO_AUTO_VALUE_ON_ZERO

5.2 PostgreSQL 的实现方式

方式一:SERIAL(旧版)
sql 复制代码
CREATE TABLE users (
    id SERIAL PRIMARY KEY,  -- 自动创建 sequence users_id_seq
    name TEXT
);
方式二:IDENTITY(SQL 标准,PG 10+ 推荐)
sql 复制代码
CREATE TABLE users (
    id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name TEXT
);

5.3 迁移后修复序列值

若数据已导入但序列未更新,会导致主键冲突:

sql 复制代码
-- 方法1:pgloader 自动处理(启用 reset sequences)
-- 方法2:手动重置
SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));

注意:IDENTITY 列的序列名可通过 pg_get_serial_sequence('users', 'id') 获取。


六、索引、约束与性能调优

6.1 索引转换

  • 普通索引CREATE INDEX 直接迁移

  • 唯一索引UNIQUE 约束自动创建索引

  • 全文索引 :MySQL 的 FULLTEXT 需重写为 PG 的 tsvector + GIN/GiST 索引

    sql 复制代码
    ALTER TABLE articles ADD COLUMN title_ts tsvector;
    UPDATE articles SET title_ts = to_tsvector('english', title);
    CREATE INDEX idx_title_fts ON articles USING GIN(title_ts);
  • 前缀索引 :MySQL 支持 INDEX(col(10)),PG 不支持,需用表达式索引:

    sql 复制代码
    CREATE INDEX ON users ((left(email, 10)));

6.2 外键与约束

  • PG 默认启用外键约束,确保参照完整性

  • 若 MySQL 未启用外键,迁移后需评估是否添加

  • 使用 NOT VALID 延迟验证大表外键:

    sql 复制代码
    ALTER TABLE orders ADD CONSTRAINT fk_user
        FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;
    ALTER TABLE orders VALIDATE CONSTRAINT fk_user;  -- 后台验证

6.3 性能调优建议

  • 批量插入 :关闭 autovacuum、增大 maintenance_work_mem

  • 并行迁移 :pgloader 的 workersconcurrency 参数

  • 目标库配置

    conf 复制代码
    shared_buffers = 4GB
    effective_cache_size = 12GB
    work_mem = 64MB
    max_wal_size = 4GB

七、应用层适配与 SQL 改造

7.1 ORM 适配

  • Sequelize / TypeORM / Django ORM :切换 dialect 为 postgres,调整连接字符串
  • MyBatis :修改 XML 中的函数(如 NOW()CURRENT_TIMESTAMP
  • Laravel Eloquent :基本兼容,注意分页语法(skip/take vs offset/limit

7.2 常见 SQL 改造点

场景 MySQL PostgreSQL
分页 LIMIT 10 OFFSET 20 同左(但 OFFSET 不能为负)
插入返回 ID INSERT ...; SELECT LAST_INSERT_ID(); INSERT ... RETURNING id;
字符串转日期 STR_TO_DATE('20250101','%Y%m%d') TO_DATE('20250101','YYYYMMDD')
随机排序 ORDER BY RAND() ORDER BY RANDOM()
获取当前数据库 DATABASE() current_database()

7.3 事务与锁行为差异

  • 默认隔离级别:MySQL(RR),PG(Read Committed)

  • 幻读处理:PG 在 RC 下无幻读(MVCC 实现不同)

  • 锁粒度:PG 行锁更精细,死锁检测更主动

  • 建议 :测试高并发场景,必要时显式设置隔离级别:

    sql 复制代码
    BEGIN ISOLATION LEVEL REPEATABLE READ;

八、数据一致性验证

8.1 行数校验

sql 复制代码
-- MySQL
SELECT COUNT(*) FROM users;

-- PostgreSQL
SELECT COUNT(*) FROM users;

8.2 校验和比对(关键表)

sql 复制代码
-- MySQL
SELECT MD5(GROUP_CONCAT(CONCAT(id, name, email) ORDER BY id)) FROM users;

-- PostgreSQL
SELECT MD5(STRING_AGG(CONCAT(id, name, email), '' ORDER BY id)) FROM users;

注意:需处理 NULL(CONCAT 在 PG 中遇 NULL 返回 NULL,可用 COALESCE

8.3 抽样比对

  • 随机抽取 1000 行,逐字段比对
  • 重点验证:时间戳、浮点数、布尔值、JSON 内容

8.4 应用端冒烟测试

  • 核心业务流程(注册、下单、查询)端到端测试
  • 监控错误日志、慢查询

九、上线切换策略

9.1 停机迁移(小系统)

  1. 停写 MySQL
  2. 执行最终增量同步(如有)
  3. 切换应用连接字符串至 PG
  4. 验证后恢复服务

9.2 双写 + 切读(中大型系统)

  1. 应用同时写 MySQL 和 PG(双写)
  2. 数据校验工具持续比对
  3. 逐步切读流量至 PG(如 10% → 50% → 100%)
  4. 确认稳定后停写 MySQL

9.3 CDC 实时同步(零停机)

  • 使用 Debezium 捕获 MySQL binlog
  • 通过 Kafka 将变更同步至 PG
  • 切换瞬间仅需处理秒级延迟数据

工具链:MySQL → Debezium → Kafka → Kafka Connect JDBC Sink → PostgreSQL


十、常见问题

10.1 字符集与排序规则

  • MySQL 默认utf8mb4 + utf8mb4_general_ci(不区分大小写)
  • PostgreSQL 默认UTF8 + en_US.UTF-8(区分大小写)
  • 解决方案
    • 应用层统一转小写比较

    • 或创建不区分大小写的 collation(PG 12+):

      sql 复制代码
      CREATE COLLATION case_insensitive (provider = icu, locale = 'und-u-ks-level2');
      CREATE TABLE users (name TEXT COLLATE "case_insensitive");

10.2 0000-00-00 日期问题

  • MySQL 允许 0000-00-00,PG 不允许

  • 迁移前清洗

    sql 复制代码
    UPDATE table SET date_col = NULL WHERE date_col = '0000-00-00';

10.3 反引号与关键字冲突

  • MySQL 用反引号包裹关键字(如 order
  • PG 用双引号("order"),但建议重命名字段避免冲突

10.4 大对象(BLOB)迁移

  • pgloader 支持 BYTEA,但超大文件(>1GB)建议单独迁移
  • 或存储为文件路径,数据库仅存 URL

总结:MySQL 到 PostgreSQL 的迁移是一项系统工程,成功的关键在于:

  • 充分评估:识别语法、类型、行为差异
  • 工具赋能 :以 pgloader 为核心,自动化 DDL/DML 转换
  • 渐进切换:通过双写、CDC 实现零停机
  • 全面验证:行数、校验和、业务逻辑三层保障
  • 长期优化:迁移后利用 PG 特性(JSONB、分区表、扩展)提升系统能力

PostgreSQL 以其标准兼容性、功能丰富性、社区活跃度,已成为 MySQL 的强大替代者。通过科学规划与严谨执行,迁移不仅是一次技术栈升级,更是系统架构现代化的重要契机。

相关推荐
番茄去哪了2 小时前
在Java中操作Redis
java·开发语言·数据库·redis
JiaHao汤2 小时前
一文掌握 SQL:数据定义、操作与查询完整教程
数据库·sql
白太岁2 小时前
Redis:(3) Lua 与 Redis、基于连接池的 Facade 模式封装
数据库·c++·redis·lua·外观模式
zlp19922 小时前
Flink DataStream API 消费binlog kafka实践
数据库·flink·kafka
l1t2 小时前
利用DeepSeek和qwen 3.5辅助生成SQL优化方法幻灯片视频
数据库·sql·音视频
_千思_2 小时前
【小白说】数据库系统概念 4
数据库
专注&突破3 小时前
DeepAgents 的 Backend详解
数据库
星火开发设计3 小时前
序列式容器:list 双向链表的特性与用法
开发语言·前端·数据结构·数据库·c++·链表·list
Zzz 小生3 小时前
LangChain Messages:消息使用完全指南
数据库·windows·microsoft