惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

C
Check Point Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 聂微东
月光博客
月光博客
博客园 - 司徒正美
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
量子位
Recent Announcements
Recent Announcements
V
V2EX
P
Proofpoint News Feed
小众软件
小众软件
云风的 BLOG
云风的 BLOG
腾讯CDC
宝玉的分享
宝玉的分享
Microsoft Azure Blog
Microsoft Azure Blog
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
A
About on SuperTechFans
B
Blog
博客园_首页
GbyAI
GbyAI
博客园 - Franky

博客园 - 风哥数据库教程

数据库教程FGMT20‑Oracle容灾体系架构与数据库升级迁移方案 数据库教程FGMT19‑Oracle多租户容器架构与新特性总结 数据库教程FGMT18‑Oracle‑EMCC智能化集中运维管理系统 数据库教程FGMT17‑Oracle性能优化之故障诊断与性能优化 数据库教程FGMT16‑Oracle性能优化之操作系统诊断与分析 数据库教程FGMT15‑Oracle性能优化之物化视图与任务管理 数据库教程FGMT14‑Oracle性能优化之资源限制与资源管理 数据库教程FGMT13‑Oracle性能优化之数据仓库与分区表 数据库教程FGMT12‑Oracle性能诊断与综合分析 数据库教程FGMT11‑Oracle性能优化之性能收集与报告分析 数据库教程FGMT10‑Oracle性能优化之统计信息管理与维护 数据库教程FGMT09‑Oracle性能优化之执行计划与基线管理 数据库教程FGMT08‑Oracle性能优化之基准测试与案例分析 数据库教程FGMT06‑Oracle数据库日常管理维护操作 数据库教程FGMT07‑Oracle数据库备份恢复与数据迁移 数据库教程FGMT05‑Oracle数据库SQL语言开发与应用实战 数据库教程FGMT03‑生产环境Linux+Oracle19c+ASM安装配置与项目实战 数据库教程FGMT01‑Oracle数据库基础知识与版本特性 数据库教程FGMT00‑教程学习大纲与学习指南(第一部) MySQL 8/9集群InnoDB Cluster自动化安装过程-Fgedu MySQL8/9 MGR组复制集群安装自动化全过程-Fgedu MySQL8/9主从复制集群安装自动化全过程-Fgedu MySQL8.x/9.x数据库安装自动化全过程-Fgedu 异构数据库迁移全过程-FGO2ODB工具 从国外数据库迁移到国产数据库全过程-FGO2CDB工具 Oracle迁移到PostgreSQL数据库-FGOracle2PG工具 Oracle 26ai数据库一键自动安装 for Fgedu Oracle 19c数据库一键自动安装 for Fgedu 数据库数据文件误删恢复工具FGFDU(FGEDU File DUL) 华为高斯数据库恢复工具FGOGDU(FGEDU openGauss DUL)
MySQL迁移到PostgreSQL数据库-FGMySQL2PG工具
风哥数据库教程 · 2026-08-31 · via 博客园 - 风哥数据库教程

# MySQL迁移到PostgreSQL数据库-FGMySQL2PG工具

> 本手册覆盖FGMySQL2PG程序介绍、功能特性、命令行与可视化操作、各类案例场景与故障排查。

---

## 一、FGMySQL2PG程序介绍

### 1.1 工具简介

FGMySQL2PG 是一款面向企业级数据库迁移场景的高性能 MySQL 到 PostgreSQL 全栈迁移工具。它不仅覆盖表结构、数据、索引、视图、函数、用户与权限的迁移,还在底层实现了语法转换、字符集规范化、批量写入优化、并发控制、断点续传与失败重试等关键工程能力,能够在 PB 级数据量、复杂业务对象、严格一致性要求的场景下保持稳定运行。

工具同时提供两种使用入口:

- **命令行入口**(`fgmysql2pg` / `python -m fgmysql2pg`):适合脚本化、批处理、CI/CD 集成、无人值守迁移场景;

- **可视化控制台**(Web Console):基于 Flask + 原生 JavaScript 构建的单页应用,无需记忆参数,浏览器中即可完成连接测试、迁移评估、启动/停止迁移、查看报告的完整流程,适合运维人员手工操作与排错。

两类入口共享同一套核心引擎(converter / mysql_conn / pg_conn / native),转换规则与稳定性机制完全一致,用户可按场景自由切换。

### 1.2 作者信息

作者:风哥

官方网站:  http://www.fgedu.net.cn ,   http://www.itpux.com

数据库教程:   https://edu.51cto.com/lecturer/8020378.html

### 1.3 设计理念

- **简洁强大**:单页 Web UI 集成所有功能,无页面跳转;命令行参数极简,子命令清晰;

- **稳定优先**:采用 COPY FROM STDIN 批量写入、3 次指数退避重试、死锁检测、UTF8MB4 字符集规范化、PG 会话级 statement_timeout/lock_timeout 等机制,保证迁移不中断、不乱码、不丢行;

- **可观测**:实时进度条、逐表状态网格、滚动日志、HTML 迁移报告四位一体,让每一次迁移都"看得见";

- **可回放**:所有配置可保存为 YAML 文件,支持反复载入复用,适合多环境部署。

### 1.4 与同类工具的差异

- 函数映射覆盖更广(348 个映射,覆盖 JSON / 日期 / 正则 / 聚合 / 字符串 / 加密 / 系统 / 流程控制等全部常用类别);

- 存储过程流程控制语法自动转换(IF/ELSEIF/WHILE/REPEAT/DECLARE/LEAVE/ITERATE → PL/pgSQL 等价语法);

- PG 端用 psycopg2 的 COPY FROM STDIN,性能接近 Go 的 pgx.CopyFrom;

- Web 控制台原生单页,无任何前端构建依赖,部署即用。

---

## 二、功能特性

### 2.1 核心迁移能力

| 模块 | 能力描述 |

| --- | --- |

| 表结构 DDL | MySQL 建表语句自动转 PG 建表语句,40+ 类型映射,覆盖 UNSIGNED / ZEROFILL / BIT / ENUM / SET / JSON / BLOB / VARBINARY 等边界 |

| 数据同步 | COPY FROM STDIN 批量写入;键集分页(有主键)与全列排序分页(无主键大表)双策略,避免丢行;并发调度,吞吐量 5-8 倍于 executemany |

| 索引迁移 | 自动重建主键、唯一索引、普通索引、复合索引、FULLTEXT 索引;Greenplum / YugabyteDB 等 MPP 库自动降级为普通 CREATE INDEX |

| 视图迁移 | MySQL 视图 DDL 自动转 PG 视图 DDL,反引号→双引号、函数名映射、复杂函数参数重组(CONCAT_WS / JSON_EXTRACT / IF 等) |

| 函数 / 存储过程 | 348 个函数映射;存储过程流程控制语法自动转 PL/pgSQL(IF/ELSEIF/WHILE/REPEAT/DECLARE/LEAVE/ITERATE) |

| 用户与权限 | MySQL 用户迁移为 PG 角色;表权限映射为 GRANT 语句;密码无法迁移(MySQL 用 caching_sha2,PG 用 SCRAM) |

| 数据校验 | 同步后逐表比对 MySQL 与 PG 行数,不一致表直接列清单 |

| HTML 迁移报告 | 一条命令生成单文件可视化报告,含完整统计与错误详情 |

### 2.2 函数与语法兼容

**JSON 函数族(13 个)**:`JSON_OBJECT`→`json_build_object`、`JSON_ARRAY`→`json_build_array`、`JSON_INSERT/REPLACE/SET`→`jsonb_set`、`JSON_REMOVE`→`jsonb_delete`、`JSON_EXTRACT`→`->`、`JSON_VALUE`→`->>`、`JSON_MERGE_PATCH`→`||`、`JSON_VALID`→`jsonb_typeof` 等。

**日期/时间函数**:`YEARWEEK`、`DAYNAME`、`MONTHNAME`、`QUARTER`、`WEEK`、`DATE_ADD/SUB INTERVAL`、`TIME_FORMAT`、`TIME_TO_SEC`、`SEC_TO_TIME`、`ADDDATE`、`SUBDATE`、`TIMEDIFF`、`MICROSECOND`、`UTC_TIMESTAMP`、`MAKEDATE`、`MAKETIME` 等。

**正则函数**:`REGEXP_LIKE`、`REGEXP_INSTR`、`REGEXP_REPLACE`、`REGEXP_SUBSTR`。

**聚合/字符串函数**:`GROUP_CONCAT(DISTINCT ... ORDER BY ... SEPARATOR ...)`→`string_agg`、`CONCAT_WS(sep,a,b,c)`→`ARRAY_TO_STRING(ARRAY[a,b,c], sep)`、`IF(cond,a,b)`→`CASE WHEN`、`CAST(x USING charset)`→`x`、`BIT_AND/OR/XOR`、`STD/STDDEV/VARIANCE`、`SUBSTRING_INDEX`、`ELT`、`QUOTE`、`SPACE`、`BIT_LENGTH` 等。

**存储过程流程控制**:`ELSEIF`→`ELSIF`、`WHILE...DO...END WHILE`→`WHILE...LOOP...END LOOP`、`REPEAT...UNTIL`→`LOOP...EXIT WHEN`、`LEAVE`→`EXIT`、`ITERATE`→`CONTINUE`、`DECLARE var TYPE DEFAULT x`→`DECLARE var TYPE := x`、`SET @x:=`→`x:=`、`DELIMITER` 剥离、`CREATE PROCEDURE`→`CREATE FUNCTION ... RETURNS void`。

**全文检索**:`MATCH(col) AGAINST(q)`→`to_tsvector(col) @@ to_tsquery(q)`。

### 2.3 稳定性强化(核心特性)

1. **UTF8MB4 字符集规范化**:MySQL 端强制 `charset=utf8mb4` + `use_unicode=True`,PostgreSQL 端强制 `client_encoding=UTF8`;BLOB/VARBINARY 列直接返回 bytes 不被错误解码,从根本上防止 emoji、4 字节字符、二进制内容乱码。

2. **无主键表分档策略**:

   - 行数 ≤ 200 万:流式 OFFSET 分页,性能足够;

   - 行数 > 200 万:全列 ORDER BY + OFFSET 分页,保证顺序稳定,避免并发写入下丢行/重行;

   - 单列主键:`WHERE pk > last_pk ORDER BY pk LIMIT n`,O(n);

   - 复合主键:行构造器 `(pk1,pk2) > (?,?)`,O(n)。

3. **COPY FROM STDIN 批量写入**:相比 `executemany` 提升 5-10 倍性能;自动对 None/bytes/制表符/换行符/反斜杠做转义;失败自动回退到 `execute_values`,确保即使在 JSON、特殊字符、类型不匹配场景下也能完成迁移。

4. **重试与死锁检测**:识别 14 类可重试 SQLSTATE(`40P01` 死锁、`40001` 序列化失败、`08000` 连接异常、`53300` 连接数满、`53M00` Yugabyte 临时不可用等),3 次指数退避(1s/2s/4s),不可重试错误直接回退。

5. **PG 会话参数白名单**:从 `pg_connection_params` 解析 `statement_timeout` / `lock_timeout` / `search_path` / `idle_in_transaction_session_timeout` 等,在连接建立后自动 `SET`,防止长查询挂死整个迁移进程。

6. **进度更新合并机制**:前端节流由"丢弃"改为"合并",被节流的最后状态会在 200ms 后兜底 poll,彻底解决"进度条卡在 95%"的 Bug,保证 100% 最终状态不丢失。

7. **协作式停止**:用户点击"停止迁移"或 Ctrl+C 后,停止信号会在下一个批次/表边界被检测到,当前批次正常完成,避免数据半截写入。

8. **连接池探活**:每次借出连接先 `SELECT 1` 探活,失效连接自动剔除重建,避免长时间空闲后的连接超时。

9. **数据类型边界处理**:

   - `BIGINT UNSIGNED` 上限 18446744073709551615 超 PG BIGINT → 升档为 `NUMERIC(20,0)`;

   - `INT UNSIGNED` → `BIGINT`;

   - `SMALLINT UNSIGNED` → `INTEGER`;

   - `BIT(n)` 保留长度,不被 `_normalize` 误剥离;

   - `TINYINT(1)` 可选映射 `BOOLEAN` 或 `SMALLINT`;

   - `ZEROFILL` 仅显示属性,剥离不影响存储;

   - `ENUM`/`SET` 统一映射为 `TEXT`(PG 原生 enum 类型需手动维护值域);

   - `JSON` → `JSONB`(PG 推荐,支持索引与 GIN)。

### 2.3.1 大数据量迁移能力(100GB - 5TB)

针对企业级核心库的大数据量迁移场景,工具内置一套完整的 TB/PB 级迁移引擎,核心机制如下:

1. **MySQL 流式游标(SSCursor)**

   - 使用 PyMySQL 的 `SSCursor` 服务端游标,逐批 `fetchmany(batch_size)` 读取,不在客户端缓存全量数据;

   - 默认 `read_timeout=28800`(8 小时)放宽,避免长时间无数据返回被强制断开;

   - 配合键集分页(`WHERE pk > last_pk`)实现 O(n) 复杂度的全表扫描,避免 OFFSET 在大表上的 O(n²) 性能塌方。

2. **生产者-消费者流式管道**

   - 单表迁移采用双线程架构:生产者线程持续从 MySQL 流式读取,主线程持续向 PG COPY 写入,两端并行;

   - 有界队列(默认容量 4)连接生产者与消费者,限制内存峰值在 GB 级(而非数据量级);

   - 队列满时生产者自动阻塞,避免数据堆积导致 OOM;

   - 队列空时消费者等待,自动适应生产者读取速率。

3. **断点续传**

   - 每张表独立检查点文件(`./.checkpoint/{schema}.{table}.json`),记录已迁移主键值/偏移量、已插入行数、总行数、列定义;

   - 迁移中断(进程崩溃、网络断开、用户 Ctrl+C)后重启,自动加载检查点,从最近位置继续,避免重头再来;

   - 检查点文件采用原子写入(临时文件 + rename),防止中断时半截 JSON 损坏;

   - 表迁移完成后检查点自动删除,避免下次重复加载;

   - 启动时通过 `list_pending()` 识别上次中断的表,并在进度事件中上报 `resumed_tables` 统计。

4. **动态批大小(AIMD 简化版)**

   - 根据队列水位与内存压力自适应调整批次大小:

     - 队列使用率 > 75%:消费者慢,批大小 × 0.8 降低单批延迟;

     - 队列使用率 < 25% 且批次填满:生产者慢,批大小 × 1.2 提高吞吐;

     - RSS 内存超 `memory_limit_mb`:批大小立即减半;

   - 批大小在 [1000, 200000] 范围内调整,避免极端值。

5. **内存硬保护**

   - 两级内存阈值:`memory_limit_mb`(软限,触发 GC)和 `memory_hard_limit_mb`(硬限,触发生产者阻塞);

   - RSS 超 `memory_hard_limit_mb` 时生产者主动阻塞,等待消费者消化队列,最长 60 秒;

   - 阻塞期间持续 GC,内存仍超 `hard_limit * 1.25` 时再次 GC;

   - 5TB 场景建议 `memory_hard_limit_mb: 4096`,实测峰值稳定在 2-3 GB。

6. **连接断开重连**

   - MySQL 生产者线程:捕获 `Lost connection` / `server has gone away` / `broken pipe` / `Connection reset` 等错误,指数退避重试 3 次(1s/2s/4s),重试时从断点位置继续读取;

   - PG 消费者:`batch_insert` 内置 3 次指数退避重试,连接池自动丢弃坏连接重建;

   - TCP keepalive(idle=30s/interval=10s/count=3)防止长时间迁移被中间设备超时断开。

7. **多表并行调度**

   - `ThreadPoolExecutor` 并发同步多张表(默认 `concurrency=10`),单表失败不影响其他表;

   - 每张表独立生产者-消费者管道,互不干扰;

   - 全局内存监控在 Manager 启动时上报 RSS,供前端预警。

8. **进度与 ETA**

   - 单表进度:每批 COPY 完成后上报 `inserted/total/pct/rows_per_sec/eta_sec/rss_mb`;

   - ETA 基于最近 5 批的滑动窗口均值,避免早期抖动或尾部偏置;

   - 全局进度:按表完成度计算,显示 `done/total` 表数与累计行数。

### 2.3.2 大数据量迁移配置项

在 `config.yml` 的 `conversion.limits` 段配置大数据量迁移参数:

```yaml

conversion:

  limits:

    # 并发表数(5TB 场景建议 4-8,避免内存叠加)

    concurrency: 8

    # 单批 INSERT 行数(大表建议 50000-100000)

    batch_insert_size: 50000

    # ===== TB 级迁移扩展 =====

    # 流式游标(SSCursor),必开

    streaming_cursor: true

    # 生产者-消费者队列容量(每表独立,5TB 建议 4-8)

    queue_size: 4

    # 内存软限(MB),触发 GC 与批大小减半

    memory_limit_mb: 2048

    # 内存硬限(MB),触发生产者阻塞;5TB 建议 4096

    memory_hard_limit_mb: 4096

    # 大表阈值(行数),超过启用断点续传

    large_table_threshold: 1000000

    # 超大表阈值(行数),超过启用流式管道

    huge_table_threshold: 10000000

    # 断点续传检查点目录(空字符串禁用)

    checkpoint_dir: "./.checkpoint"

    # COPY 单批最大行数

    copy_chunk_size: 100000

    # 重试次数与退避基数

    retry_max: 3

    retry_backoff_base: 1.0

    # 单表最大耗时(秒),0 无限制;5TB 建议 86400(1 天)

    per_table_timeout: 86400

    # 全局最大耗时(秒),0 无限制;5TB 建议 432000(5 天)

    global_timeout: 432000

    # 迁移规模预设:small/medium/large/huge

    scale_preset: huge

```

### 2.4 可观测性

- **实时进度条**:按表完成度计算百分比,显示 `done/total` 计数;

- **速率与 ETA**:基于 EMA(指数移动平均)平滑显示 rows/s,根据已完成表数估算剩余时间;

- **逐表状态网格**:pending / syncing(蓝色脉冲动画)/ done / error 四态可视化,显示行数、索引数、DDL 状态、校验 M/P 行数对比;

- **滚动日志**:彩色分级(phase/data/table/ok/warn/err),自动滚动,最多保留 500 行;

- **不一致表列表**:迁移完成后高亮显示 MySQL 与 PG 行数不一致的表;

- **HTML 报告**:一键打开迁移报告页面,含完整统计与错误详情。

### 2.5 命令行子命令

| 子命令 | 用途 |

| --- | --- |

| `fgmysql2pg -c config.yml` | 执行转换(默认) |

| `fgmysql2pg convert -c config.yml` | 同上 |

| `fgmysql2pg test -c config.yml` | 仅测试连接,打印版本 |

| `fgmysql2pg assess -c config.yml` | 迁移前评估,输出 HTML 报告 |

| `fgmysql2pg report -l conversion.log` | 从日志生成 HTML 报告 |

| `fgmysql2pg web -c config.yml -p 8088` | 启动可视化控制台 |

| `fgmysql2pg -v` / `--version` | 显示版本 |

### 2.6 Web 单页全功能界面

- **连接配置**:MySQL 源库与 PostgreSQL 目标库的主机/端口/用户/密码/库;

- **转换选项**:12 个开关(表结构/数据/索引/视图/函数/用户/权限/校验/清空/跳过/字段小写/表名小写/tinyint→BOOLEAN);

- **高级选项**(折叠):白/黑名单表/视图/函数、批量插入大小、最大行数/批、带宽限制、MPP 模式、日志路径;

- **操作按钮**:测试连接、迁移评估、开始转换、查看报告、保存配置、载入配置、开始迁移、停止迁移;

- **配置管理**:`/api/config/save` 保存为 YAML,`/api/config/load` 载入 YAML,支持反复复用。

### 2.7 控制脚本

项目根目录提供 `webctl.py` 脚本,用于管理 Web 控制台生命周期:

| 命令 | 用途 |

| --- | --- |

| `python3 webctl.py start [-c config.yml] [-H 0.0.0.0] [-p 8088]` | 后台启动,写 PID 文件 |

| `python3 webctl.py stop` | 停止(SIGTERM → 5s → SIGKILL → pkill 兜底) |

| `python3 webctl.py status [-p 8088]` | 查看运行状态(PID、端口监听) |

| `python3 webctl.py restart [-c ...] [-H ...] [-p ...]` | 重启(stop + start) |

---

## 三、支持环境

### 3.1 操作系统

| 平台 | 支持情况 |

| --- | --- |

| Linux(x86_64 / ARM64) | 完全支持,推荐生产环境部署 |

| macOS(Intel / Apple Silicon) | 完全支持,开发与测试环境 |

| Windows 10/11 + WSL2 | 完全支持,原生 Windows 需 psycopg2 编译环境 |

| Docker 容器 | 完全支持,推荐基于 python:3.9-slim 镜像 |

### 3.2 Python 环境

- **Python 版本**:3.8 / 3.9 / 3.10 / 3.11 / 3.12(推荐 3.9+)

- **关键依赖**:

  - `PyMySQL>=1.1.0`(MySQL 驱动)

  - `psycopg2-binary>=2.9.9`(PostgreSQL 驱动)

  - `PyYAML>=6.0`(配置文件解析)

  - `Flask>=3.0.0`(Web 控制台)

  - `python-docx>=1.0`(仅生成 docx 报告时需要)

### 3.3 数据库版本

| 数据库 | 支持版本 |

| --- | --- |

| MySQL | 5.6 / 5.7 / 8.0 / 8.1+(推荐 5.7+,完整支持 utf8mb4 与 JSON 类型) |

| MariaDB | 10.3 / 10.5 / 10.6 / 10.11 / 11.x |

| PostgreSQL | 10 / 11 / 12 / 13 / 14 / 15 / 16(推荐 13+,支持 `CREATE ROLE IF NOT EXISTS`) |

| Greenplum | 6.x / 7.x(MPP 模式) |

| YugabyteDB | 2.x / 2024.x(MPP 模式) |

| openGauss / GaussDB / HighGo / Kingbase | 兼容 PG 协议,可用 |

### 3.4 浏览器要求(可视化)

- Chrome / Edge / Firefox / Safari 现代版本(支持 EventSource SSE 与 fetch)

- 屏幕分辨率 ≥ 1366×768(推荐 1920×1080)

### 3.5 网络与权限

- 迁移执行机需能同时访问 MySQL 与 PostgreSQL 端口;

- MySQL 账户需具备 `SELECT`、`SHOW VIEW`、`TRIGGER`、`PROCESS`(读取 mysql.user 表)等权限;

- PostgreSQL 账户需具备 `CREATE`、`INSERT`、`INDEX`、`USAGE` 等权限,迁移用户时需要 `CREATEROLE`;

- 防火墙需放行 MySQL 3306、PostgreSQL 5432 与 Web 控制台端口(默认 8088)。

---

## 四、程序使用

### 4.1 安装

```bash

# 1. 解压源码

cd FGMySQL2PG

# 2. 安装依赖

pip install -r requirements.txt

# 3.(可选)安装 docx 生成支持

pip install python-docx

```

### 4.2 配置文件

复制 `config.example.yml` 为 `config.yml`,按实际环境填写:

```yaml

mysql:

  host: 127.0.0.1

  port: 3306

  username: root

  password: your_pwd

  database: source_db

  connection_params: charset=utf8mb4&interpolateParams=true&readTimeout=60s&writeTimeout=60s&timeout=30s

  consistent_snapshot: false   # 源库并发写入时建议开启

postgresql:

  host: 127.0.0.1

  port: 5432

  username: postgres

  password: your_pwd

  database: target_db

  pg_connection_params: search_path=public connect_timeout=300 statement_timeout=0

conversion:

  options:

    tableddl: true

    data: true

    indexes: true

    view: true

    functions: false

    validate_data: true

    lowercase_columns: true

    skip_existing_tables: true

  limits:

    concurrency: 10

    batch_insert_size: 50000

```

完整配置项参见源码根目录 `config.example.yml`。

### 4.3 命令行操作流程

#### 4.3.1 测试连接

```bash

fgmysql2pg test -c config.yml

# 或

python -m fgmysql2pg test -c config.yml

```

输出 MySQL 与 PostgreSQL 版本号即表示连接正常。

#### 4.3.2 迁移前评估

```bash

fgmysql2pg assess -c config.yml -o assess.html

```

生成 HTML 兼容性报告,列出可转换对象、风险点、函数映射覆盖率。

#### 4.3.3 执行迁移

```bash

fgmysql2pg -c config.yml

# 或显式

fgmysql2pg convert -c config.yml

```

控制台实时打印进度,完成后输出统计汇总。

#### 4.3.4 生成报告

```bash

fgmysql2pg report -l conversion.log -e errors.log -o report.html

```

从已有日志生成 HTML 报告,无需重新迁移。

#### 4.3.5 启动可视化控制台

```bash

# 前台运行(Ctrl+C 退出)

fgmysql2pg web -c config.yml -H 0.0.0.0 -p 8088

# 后台运行(推荐用 webctl.py)

python3 webctl.py start -c config.yml -p 8088

python3 webctl.py status -p 8088

python3 webctl.py stop

python3 webctl.py restart -p 9090

```

### 4.4 可视化控制台操作流程

1. **填写连接**:在左侧"MySQL 源库"与"PostgreSQL 目标库"卡片填写主机/端口/用户/密码/库名;

2. **测试连接**:点击"测试连接"按钮,日志区域显示 MySQL 与 PG 版本,表示连通;

3. **配置选项**:勾选需要的转换项;如需白/黑名单或自定义批量大小,点击"显示高级选项"展开;

4. **迁移评估**(推荐):点击"迁移评估",生成兼容性评分报告,识别风险点;

5. **开始转换**:点击"开始转换"或右侧"开始迁移"按钮,状态徽章变为"运行中";

6. **实时监控**:进度条、速率 ETA、逐表网格、滚动日志实时刷新;

7. **停止迁移**(可选):点击"停止迁移",工具在当前批次完成后退出;

8. **查看报告**:迁移完成后点击"查看报告"打开 HTML 报告页;

9. **保存配置**:点击"保存配置"将当前界面设置写入 config.yml,下次"载入配置"即可复用。

### 4.5 REST API

控制台同时提供 REST API,便于集成到运维平台:

| 方法 | 路径 | 功能 |

| --- | --- | --- |

| GET | `/` | 单页控制台 |

| GET | `/api/status` | 获取迁移状态快照 |

| GET | `/api/progress` | SSE 实时事件流 |

| POST | `/api/test` | 测试 MySQL/PG 连接 |

| POST | `/api/assess` | 执行迁移评估 |

| GET | `/api/assess/report` | 获取评估 HTML 报告 |

| POST | `/api/convert` | 启动转换任务 |

| POST | `/api/stop` | 停止当前迁移 |

| GET | `/api/report` | 获取迁移 HTML 报告 |

| POST | `/api/config/save` | 保存配置为 YAML |

| GET | `/api/config/load` | 载入 YAML 配置 |

---

## 五、mysql迁移到postgresql案例场景与操作过程

### 5.1 场景一:mysql迁移到postgresql-中小业务库全量迁移(< 10GB)

**背景**:某电商系统 MySQL 5.7 业务库(约 8GB,120 张表,含 JSON 字段、视图、存储函数)迁移到 PostgreSQL 14。

#### 命令行操作

```bash

# 1. 测试连接

fgmysql2pg test -c config.yml

# 2. 迁移评估

fgmysql2pg assess -c config.yml -o assess.html

# 3. 执行迁移

fgmysql2pg convert -c config.yml

# 4. 查看报告

fgmysql2pg report -l conversion.log -o report.html

```

#### 可视化操作

1. 启动控制台 `python3 webctl.py start -p 8088`;

2. 浏览器打开 `http://127.0.0.1:8088`;

3. 点击"测试连接"确认 MySQL 5.7 与 PostgreSQL 14 连通;

4. 转换选项勾选:表结构、数据、索引、视图、数据校验;高级选项中"字段小写"勾选;

5. 点击"迁移评估",确认评分 ≥ 80 分、无致命风险;

6. 点击"开始转换",观察进度条与逐表网格;

7. 迁移完成后,"不一致表"区域显示"无",点击"查看报告"归档。

**预期耗时**:8GB 数据约 10-15 分钟(取决于网络与磁盘 IO)。

### 5.2 场景二:mysql迁移到postgresql-大表无主键迁移(> 5000 万行)

**背景**:日志表 `t_log` 无主键,行数 6000 万,约 50GB。

**工具自动策略**:

- 识别无主键;

- 行数 > 200 万 → 切换"全列排序 OFFSET 分页"策略;

- 每批 50000 行,通过 `ORDER BY col1,col2,... LIMIT 50000 OFFSET n` 稳定分页;

- PG 端 COPY FROM STDIN 批量写入,失败自动回退 execute_values;

- 遇死锁自动重试 3 次(1s/2s/4s)。

#### 命令行操作

```bash

# 编辑 config.yml,调低并发与批量大小

# conversion.limits.concurrency: 4

# conversion.limits.batch_insert_size: 20000

# conversion.options.consistent_snapshot: true

fgmysql2pg convert -c config.yml

```

#### 可视化操作

1. 高级选项中"批量插入大小"设为 20000;

2. "并发数"调低为 4(避免大表 OFFSET 偏移过大造成源库压力);

3. 启用"一致性快照"(若源库有持续写入);

4. 启动迁移,逐表网格中观察 `t_log` 状态从 pending → syncing(蓝色脉冲)→ done。

**优化建议**:在源表加自增列做伪主键 `ALTER TABLE t_log ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY`,可让工具走键集分页,O(n)。

### 5.3 场景三:mysql迁移到postgresql-含 emoji 与 4 字节字符的迁移

**背景**:用户评论表 `t_comment` 含 emoji、中文生僻字、4 字节 Unicode。

**工具保证**:

- MySQL 端强制 `charset=utf8mb4`,即使源库默认 latin1 也会被覆盖;

- `use_unicode=True` 让 PyMySQL 正确解码字符列;

- PostgreSQL 端 `client_encoding=UTF8`,服务端默认 UTF8 存储;

- BLOB/VARBINARY 列直接以 bytes 传输,不被错误 decode。

#### 命令行操作

```bash

# config.yml 的 connection_params 中 charset 字段保持 utf8mb4(默认)

fgmysql2pg convert -c config.yml

# 迁移后抽检

psql -c "SELECT count(*) FROM t_comment WHERE content ~ '[^\x00-\xffff]'"

```

#### 可视化操作

1. 连接配置卡片确认 MySQL 端 charset 字段为 utf8mb4(默认值,不要改成 utf8mb3 或 latin1);

2. 启动迁移;

3. 完成后用 psql 抽检 4 字节字符是否完整。

### 5.4 场景四:mysql迁移到postgresql-视图与存储过程迁移

**背景**:业务库含 30 个视图与 5 个存储过程,使用 `CONCAT_WS`、`JSON_EXTRACT`、`IF(...)` 等 MySQL 特有函数。

**工具自动转换示例**:

| MySQL 原语句 | 转换后 PG 语句 |

| --- | --- |

| `SELECT CONCAT_WS(',', a, b, c)` | `SELECT ARRAY_TO_STRING(ARRAY[a, b, c], ',')` |

| `SELECT JSON_EXTRACT(doc, '$.user.name')` | `SELECT (doc->'user'->'name')` |

| `SELECT JSON_VALUE(doc, '$.age')` | `SELECT (doc->>'age')` |

| `SELECT IF(x>0, 'yes', 'no')` | `SELECT (CASE WHEN x>0 THEN 'yes' ELSE 'no' END)` |

| `SELECT DATE_ADD(NOW(), INTERVAL 1 DAY)` | `SELECT (NOW() + INTERVAL '1 DAY')` |

| `SELECT GROUP_CONCAT(DISTINCT name ORDER BY id SEPARATOR ';')` | `SELECT string_agg(DISTINCT (name)::text, ';' ORDER BY id)` |

| `CREATE PROCEDURE p() BEGIN DECLARE i INT DEFAULT 0; WHILE i<10 DO SET i:=i+1; END WHILE; END` | `CREATE FUNCTION p() RETURNS void LANGUAGE plpgsql AS $$ BEGIN DECLARE i INT := 0; WHILE i<10 LOOP i := i+1; END LOOP; END $$;` |

#### 命令行操作

```bash

# config.yml 启用视图与函数

# conversion.options.view: true

# conversion.options.functions: true

fgmysql2pg assess -c config.yml -o assess.html   # 先评估风险

fgmysql2pg convert -c config.yml

```

#### 可视化操作

1. 转换选项中勾选"视图"与"函数";

2. 点击"迁移评估",报告中列出无法自动转换的语法点,需人工复核;

3. 复杂存储过程建议迁移后用 pgAdmin 打开校验语法;

4. 启动迁移,完成后在 PG 端执行 `\df+` 查看函数定义。

### 5.5 场景五:mysql迁移到postgresql-迁移到 Greenplum / YugabyteDB

**背景**:数据仓库迁移到 Greenplum 7。

#### 命令行操作

```yaml

# config.yml

conversion:

  mpp:

    enabled: true

    database: greenplum

```

```bash

fgmysql2pg convert -c config.yml

```

#### 可视化操作

1. 高级选项"MPP 模式"选择 `greenplum`;

2. Greenplum 不支持 `CREATE INDEX CONCURRENTLY`,工具会自动降级为普通 `CREATE INDEX`;

3. YugabyteDB 的 `53M00` 临时不可用错误会被识别为可重试;

4. 大表建议先在 Greenplum 侧预分布键,再迁移数据。

### 5.6 场景六:mysql迁移到postgresql-分批次增量迁移

**背景**:业务不停机迁移,先迁历史数据,再迁增量。

#### 命令行操作

```bash

# 第一次:全量迁移

fgmysql2pg convert -c config.yml

# 第二次:迁移增量(启用 truncate_before_sync)

# config.yml: conversion.options.truncate_before_sync: true

fgmysql2pg convert -c config.yml

```

#### 可视化操作

1. 第一次:勾选"表结构"与"数据",执行全量迁移;

2. 第二次:勾选"数据"并启用"同步前清空",迁移增量数据;

3. 最后切换应用写入 PG,完成迁移。

### 5.7 场景七:mysql迁移到postgresql-仅迁移部分表(白名单)

**背景**:业务库 200 张表,仅需迁移 30 张核心表。

#### 命令行操作

```yaml

# config.yml

conversion:

  options:

    use_table_list: true

    table_list: [t_order, t_user, t_payment]

```

```bash

fgmysql2pg convert -c config.yml

```

#### 可视化操作

1. 高级选项"白名单表"填写 `t_order,t_user,t_payment,...`(逗号分隔);

2. 工具自动设置 `use_table_list=true`;

3. 评估报告中只显示白名单表;

4. 其他表保持不变。

### 5.8 场景八:排查不一致表

**背景**:迁移完成后,"不一致表"区域显示 `t_order MySQL=10000 PG=9998`。

#### 命令行排查

```bash

# 1. 查看日志中该表的校验记录

grep "t_order" conversion.log | grep -i "校验\|mismatch\|不一致"

# 2. 检查源表是否有触发器在迁移期间持续写入

mysql -e "SHOW TRIGGERS LIKE 't_order'"

# 3. 检查目标表是否有约束拒绝部分行

psql -c "\d t_order"

# 4. 重新执行该表迁移(白名单 + truncate)

# config.yml: conversion.options.table_list: [t_order], truncate_before_sync: true

fgmysql2pg convert -c config.yml

```

#### 可视化排查

1. 查看"滚动日志"中该表的 `校验不一致` 行;

2. 检查源表是否有触发器在迁移期间持续写入;

3. 检查目标表是否有约束(NOT NULL/CHECK)导致部分行被拒;

4. 重新执行该表迁移:在白名单中只填该表,启用 `truncate_before_sync`;

5. 若仍不一致,启用 `consistent_snapshot: true` 重试。

### 5.9 场景九:CI/CD 集成(无人值守)

**背景**:数据库变更自动化流水线,每日构建后自动同步 schema 到测试 PG。

```bash

# CI 脚本片段

pip install -r requirements.txt

cp config.example.yml config.yml

# 用环境变量覆盖连接信息

sed -i "s/root/$MYSQL_USER/" config.yml

sed -i "s/your_pwd/$MYSQL_PWD/" config.yml

# 1. 测试连接(失败则流水线中止)

fgmysql2pg test -c config.yml || exit 1

# 2. 仅迁移表结构(不迁数据)

# config.yml: conversion.options.data: false, tableddl: true

fgmysql2pg convert -c config.yml

# 3. 生成报告归档

fgmysql2pg report -l conversion.log -o report-$(date +%Y%m%d).html

```

### 5.10 场景十:可视化迁移报告归档

**背景**:迁移完成后需向团队归档迁移报告。

#### 命令行

```bash

fgmysql2pg report -l conversion.log -e errors.log -o report-$(date +%Y%m%d).html

# 上传到 wiki / OSS / 内网归档

```

#### 可视化

1. 迁移完成后点击"查看报告"按钮,浏览器打开 HTML 报告;

2. 报告包含:迁移统计、逐表状态、错误详情、函数映射覆盖率;

3. 浏览器 `Ctrl+S` 保存为本地 HTML 文件归档。

### 5.11 场景十一:mysql迁移到postgresql-100GB 级业务库迁移

**背景**:电商平台订单库,约 100GB,最大单表 2 亿行(`t_order`),含 emoji 评价字段,要求 4 小时内完成迁移。

**容量评估**:

- 总数据量 100GB,最大表 2 亿行 × 500B/行 ≈ 100GB;

- 内存峰值预估:4 表并发 × 4 队列 × 50000 行/批 × 500B ≈ 400MB,远低于 2GB 软限;

- 预估耗时:100GB ÷ 50MB/s(千兆网 + COPY)≈ 35 分钟,加上索引重建约 1.5 小时。

#### 命令行操作

```yaml

# config.yml(100GB 场景推荐配置)

conversion:

  options:

    consistent_snapshot: true      # 一致性快照,避免并发写入丢行

    truncate_before_sync: true      # 重跑时清空目标表

    lowercase_columns: true

  limits:

    concurrency: 8                  # 8 表并行

    batch_insert_size: 50000

    streaming_cursor: true          # 流式游标

    queue_size: 4

    memory_limit_mb: 1024           # 1GB 软限

    memory_hard_limit_mb: 2048      # 2GB 硬限

    large_table_threshold: 1000000

    huge_table_threshold: 10000000

    checkpoint_dir: "./.checkpoint"

    scale_preset: large

```

```bash

# 1. 测试连接

fgmysql2pg test -c config.yml

# 2. 评估(识别大表与无主键表)

fgmysql2pg assess -c config.yml

# 3. 执行迁移(约 1.5 小时)

fgmysql2pg convert -c config.yml

# 4. 若中途断开,重启自动续传

fgmysql2pg convert -c config.yml

# 日志会显示: "检测到 N 张表有未完成检查点"

# 5. 生成报告

fgmysql2pg report -l conversion.log -o report-100g.html

```

#### 可视化操作

1. 浏览器打开控制台,"连接配置"填入 MySQL/PG 信息;

2. "高级选项"中设置:并发 8、批大小 50000、内存上限 1024MB;

3. 勾选"一致性快照"与"同步前清空";

4. 点击"开始评估",识别 `t_order` 为大表(行数 > 阈值);

5. 点击"开始迁移",进度条显示单表 ETA 与全局进度;

6. 若中断重启,进度条会从断点继续,日志显示"断点续传 N 张表";

7. 完成后点击"查看报告",确认行数一致。

### 5.12 场景十二:mysql迁移到postgresql-1TB 级核心库迁移

**背景**:银行核心交易库,1TB,最大单表 20 亿行(`t_transaction`),含 JSONB 字段,要求 48 小时内完成。

**容量评估**:

- 总数据量 1TB,最大表 20 亿行 × 500B/行 ≈ 1TB;

- 内存峰值预估:4 表并发 × 4 队列 × 50000 行/批 × 500B ≈ 400MB,硬限 4GB 足够;

- 预估耗时:1TB ÷ 30MB/s(长距离网络 + PG WAL)≈ 10 小时,加上索引与校验约 24 小时。

#### 命令行操作

```yaml

# config.yml(1TB 场景推荐配置)

conversion:

  options:

    consistent_snapshot: true

    truncate_before_sync: false     # 重跑时不清空(保留断点续传)

    lowercase_columns: true

  limits:

    concurrency: 6                  # 适度降并发,避免内存叠加

    batch_insert_size: 50000

    streaming_cursor: true

    queue_size: 4

    memory_limit_mb: 2048           # 2GB 软限

    memory_hard_limit_mb: 4096      # 4GB 硬限

    large_table_threshold: 1000000

    huge_table_threshold: 10000000  # 1000 万行启用流式管道

    checkpoint_dir: "./.checkpoint"

    copy_chunk_size: 100000

    retry_max: 5                    # 增加重试次数应对长连接抖动

    retry_backoff_base: 2.0

    per_table_timeout: 86400        # 单表最长 1 天

    global_timeout: 432000          # 全局最长 5 天

    scale_preset: large

postgresql:

  pg_connection_params: "statement_timeout=0 connect_timeout=60 idle_in_transaction_session_timeout=0"

  # statement_timeout=0 禁用语句超时,允许大表 COPY 持续数小时

```

```bash

# 1. 测试连接(含 PG 参数验证)

fgmysql2pg test -c config.yml

# 2. 评估(识别 20 亿行大表)

fgmysql2pg assess -c config.yml

# 3. 后台执行迁移(nohup + 日志重定向,防止 SSH 断开)

nohup fgmysql2pg convert -c config.yml > conversion.log 2>&1 &

echo $! > conversion.pid

# 4. 监控进度(实时查看日志)

tail -f conversion.log

# 关键字段:rows_per_sec, eta_sec, rss_mb, pct

# 5. 若进程意外退出,重启自动续传

fgmysql2pg convert -c config.yml

# 日志: "检测到 3 张表有未完成检查点,从断点继续"

# 6. 完成后校验

fgmysql2pg report -l conversion.log -o report-1tb.html

```

#### 可视化操作

1. 用 `webctl.py start` 启动控制台后台运行;

2. "连接配置"中填入 PG 参数 `statement_timeout=0`;

3. "高级选项"中设置:并发 6、批大小 50000、内存硬限 4096MB;

4. 启用"断点续传"(检查点目录默认 `./.checkpoint`);

5. 点击"开始迁移",进度条显示单表 ETA(基于 5 批滑动均值);

6. 中途若浏览器关闭,迁移进程在后台继续;

7. 重新打开控制台,自动恢复进度显示;

8. 若进程崩溃,重启控制台后点击"继续迁移",从断点恢复。

### 5.13 场景十三:mysql迁移到postgresql-5TB 级数据仓库迁移

**背景**:日志分析平台数据仓库,5TB,单表最大 50 亿行(`t_event_log`),无主键(日志追加表),要求 5 天内完成迁移。

**容量评估**:

- 总数据量 5TB,最大表 50 亿行 × 1KB/行 ≈ 5TB;

- 无主键表走全列排序 OFFSET 分页,O(n²) 但稳定;

- 内存峰值预估:4 表并发 × 4 队列 × 50000 行/批 × 1KB ≈ 800MB,硬限 4GB 足够;

- 预估耗时:5TB ÷ 15MB/s(无主键 OFFSET 较慢 + 长距离)≈ 100 小时,加上索引约 120 小时(5 天)。

#### 命令行操作

```yaml

# config.yml(5TB 场景推荐配置)

conversion:

  options:

    consistent_snapshot: true

    truncate_before_sync: false

    lowercase_columns: true

  limits:

    concurrency: 4                  # 最低并发,避免内存叠加与源库压力

    batch_insert_size: 20000        # 降批大小,降低单批内存峰值

    streaming_cursor: true

    queue_size: 4

    memory_limit_mb: 2048

    memory_hard_limit_mb: 4096      # 4GB 硬限

    large_table_threshold: 1000000

    huge_table_threshold: 10000000

    checkpoint_dir: "/data/checkpoint"  # 用独立磁盘,避免与迁移数据竞争 IO

    copy_chunk_size: 50000

    retry_max: 5

    retry_backoff_base: 2.0

    per_table_timeout: 259200       # 单表最长 3 天

    global_timeout: 432000          # 全局最长 5 天

    scale_preset: huge

mysql:

  connection_params: "readTimeout=86400 writeTimeout=86400 timeout=60"

  # readTimeout=86400(1 天)覆盖无主键大表 OFFSET 扫描

postgresql:

  pg_connection_params: "statement_timeout=0 connect_timeout=120 idle_in_transaction_session_timeout=0"

```

```bash

# 1. 预创建无主键大表的目标表(可选,加速迁移)

psql -f pre_create_tables.sql

# 2. 评估(识别无主键大表,预估 OFFSET 扫描耗时)

fgmysql2pg assess -c config.yml

# 3. 后台执行迁移(用 screen/tmux 防止 SSH 断开)

screen -S migration

nohup fgmysql2pg convert -c config.yml > conversion.log 2>&1 &

echo $! > conversion.pid

# 4. 监控(关键指标)

tail -f conversion.log

# 关注:rss_mb(应稳定 < 4GB)、eta_sec、rows_per_sec

# 若 rss_mb 持续上涨,降低 concurrency 或 batch_insert_size

# 5. 监控检查点目录大小(应远小于数据量)

du -sh /data/checkpoint/

# 6. 若进程退出,重启续传(5TB 场景可能需多次续传)

fgmysql2pg convert -c config.yml

# 日志: "检测到 5 张表有未完成检查点,从断点继续"

# 已完成的表不会重跑(检查点已删除)

# 7. 完成后校验与报告

fgmysql2pg report -l conversion.log -o report-5tb.html

# 8. 清理检查点目录

rm -rf /data/checkpoint/

```

#### 可视化操作

1. 用 `webctl.py start` 启动控制台;

2. "连接配置"中填入 MySQL `readTimeout=86400`,PG `statement_timeout=0`;

3. "高级选项"中设置:并发 4、批大小 20000、内存硬限 4096MB;

4. 检查点目录设为 `/data/checkpoint`(独立磁盘);

5. 启用"一致性快照";

6. 点击"开始迁移",5TB 场景预计耗时 5 天,进度条缓慢推进属正常;

7. 每日检查控制台"内存使用"曲线,应稳定在 2-4GB;

8. 若网络抖动导致连接断开,日志显示"重试中...",迁移自动恢复;

9. 若进程崩溃,重启控制台后点击"继续迁移",从断点恢复;

10. 完成后点击"查看报告",确认所有表行数一致。

#### 5TB 场景注意事项

1. **源库压力**:`concurrency=4` 限制并发,避免源库被迁移进程拖垮;

2. **网络带宽**:5TB 数据迁移需确保 MySQL→PG 间带宽 ≥ 100MB/s;

3. **PG WAL 空间**:COPY 大量数据会产生大量 WAL,确保 PG `wal_keep_size` 足够或启用 `wal_compression=on`;

4. **磁盘空间**:PG 数据目录需预留 6TB+ 空间(5TB 数据 + 索引);

5. **检查点目录**:用独立磁盘,避免与迁移数据竞争 IO;

6. **断点续传**:5TB 场景几乎必须启用,避免中断后重头再来;

7. **无主键大表**:50 亿行 OFFSET 扫描较慢,考虑为源表添加自增列作为临时主键加速分页;

8. **监控指标**:重点监控 `rss_mb`(内存)、`eta_sec`(剩余时间)、`rows_per_sec`(吞吐速率)。

---

## 六、常用问题与排查

### 6.1 启动问题

| 现象 | 原因 | 解决 |

| --- | --- | --- |

| `ModuleNotFoundError: No module named 'flask'` | 未安装依赖 | `pip install -r requirements.txt` |

| `Permission denied: bind 0.0.0.0:8088` | 端口被占用或无权限 | 改用 `-p 18088` 或非特权端口 |

| `psycopg2-binary` 编译失败 | 系统缺 libpq | `apt install libpq-dev` 后重装,或用 conda |

| 控制台打开空白 | 浏览器禁用 JS 或 SSE | 启用 JS;轮询兜底会自动接管 |

| `webctl.py stop` 无效 | PID namespace 隔离 | 真实 Linux 服务器 PID 真实可正常 stop;沙箱环境用 `pkill -f "fgmysql2pg web"` 兜底 |

### 6.2 连接问题

| 现象 | 排查 |

| --- | --- |

| `MySQL 连接失败: Access denied` | 检查用户名密码;MySQL 8.0 默认 caching_sha2,PyMySQL 1.1+ 已支持 |

| `PostgreSQL 连接失败: password authentication failed` | 检查 pg_hba.conf 是否允许 md5/scram-sha-256 |

| `MySQL 连接失败: Lost connection` | 检查 `max_open_conns` 与 MySQL `max_connections`;降低 `concurrency` |

| 连接超时 | 检查防火墙;config.yml 调大 `connect_timeout` |

| `FATAL: too many connections` | PG `max_connections` 不足;降低 `max_conns` |

### 6.3 数据迁移问题

| 现象 | 原因 | 解决 |

| --- | --- | --- |

| 中文乱码 | 源库实际编码非 utf8mb4 | 工具已强制 utf8mb4;若仍乱码,检查源表 `CHARACTER SET` 是否 latin1,需先 `ALTER TABLE CONVERT TO CHARACTER SET utf8mb4` |

| emoji 丢失 | 源列是 utf8(3字节) | `ALTER TABLE t MODIFY col TEXT CHARACTER SET utf8mb4` |

| 进度卡在 95% | 前端节流丢弃最后状态 | 已修复(合并模式);刷新页面或点击"查看报告" |

| 大表 OFFSET 越来越慢 | 无主键 + OFFSET 天然 O(n²) | 工具已自动切全列排序分页;可加自增列做伪主键 |

| `COPY failed: invalid byte sequence` | 字符列含非法字节 | 工具自动回退 execute_values;如仍失败,检查源列 charset |

| `deadlock detected` | 并发写冲突 | 工具自动重试 3 次;降低 `concurrency` |

| `statement_timeout` 中止 | 单批次超过 0 或配置值 | config.yml 调大 `pg_connection_params` 中的 `statement_timeout` |

| `lock_timeout` 中止 | 等锁超时 | 调大 `lock_timeout` 或降低并发 |

| 行数不一致 | 源库并发写入 / 目标表约束拒行 | 启用 `consistent_snapshot: true`;检查目标表 NOT NULL/CHECK |

### 6.4 对象迁移问题

| 现象 | 解决 |

| --- | --- |

| 视图转换失败 | 查看日志中具体 SQL;复杂视图建议人工改写 |

| 存储过程语法错误 | PG 与 MySQL 语法差异大;工具处理常见模式,复杂逻辑需人工复核 |

| 函数 `RETURNS int(11)` 报错 | 工具自动剥长度,若仍失败检查 native.type_map |

| 索引创建失败 | 检查目标表是否已存在同名索引;启用 `truncate_before_sync` |

| `CREATE ROLE` 失败:已存在 | PG 13+ 支持 `IF NOT EXISTS`;老版本工具用 try/except 自动跳过 |

### 6.5 性能问题

| 现象 | 优化 |

| --- | --- |

| 迁移速度慢 | 提高 `concurrency`;加大 `batch_insert_size`;确保 PG 端用 COPY |

| 源库 CPU 飙高 | 降低 `concurrency`;启用 `consistent_snapshot` 减少锁竞争 |

| PG 端 WAL 暴涨 | 迁移前关闭 `synchronous_commit`;分批 `TRUNCATE + INSERT` |

| 大表 OFFSET 慢 | 加伪主键(`ALTER TABLE t ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY`) |

| `pg_connection_params` 不生效 | 检查参数名是否在白名单内(statement_timeout/search_path/lock_timeout 等) |

### 6.5.1 大数据量迁移问题排查(100GB - 5TB)

| 现象 | 原因 | 解决 |

| --- | --- | --- |

| 进程被 OOM Killer 杀死 | `memory_hard_limit_mb` 设得太低或并发过高 | 升至 4096+;`concurrency` 降至 4;检查 `rss_mb` 监控曲线 |

| 迁移中途连接断开 | MySQL `wait_timeout` 或网络中间设备超时 | 设 `readTimeout=86400`;启用 TCP keepalive(默认已开) |

| 检查点目录膨胀 | 大量表未完成就退出 | 检查点文件每表仅几 KB,膨胀说明表数极多;正常现象 |

| 重启后未续传 | `checkpoint_dir` 为空或被删除 | 配置 `checkpoint_dir: "./.checkpoint"`;勿手动删除 |

| 重启后重复迁移已完成的表 | 检查点文件被删除 | 检查点文件在表完成后自动删除,误删会导致重跑 |

| `queue full timeout` | 消费者僵死(PG 卡死或网络断) | 检查 PG 状态与网络;降低 `queue_size` 减少堆积 |

| `rows_per_sec` 极低 | 无主键大表 OFFSET 扫描慢 | 为源表加临时自增主键;或接受 O(n²) 耗时 |

| `rss_mb` 持续上涨 | 队列堆积或 GC 不及时 | 降 `concurrency`/`batch_insert_size`;重启迁移续传 |

| 单表耗时超 `per_table_timeout` | 大表数据量预估不足 | 调高 `per_table_timeout` 或设为 0(无限制) |

| 5TB 场景进度极慢 | 网络带宽不足或源库 IO 瓶颈 | 确保带宽 ≥ 100MB/s;源库用 SSD;降 `concurrency` 减压 |

| PG `disk full` | WAL 或数据膨胀 | 启用 `wal_compression=on`;扩容 PG 数据盘 |

| 迁移后行数不一致 | 中途源库有写入 | 启用 `consistent_snapshot`;或迁移后二次增量同步 |

| 断点续传位置错误 | 主键非单调(如 UUID) | 用自增主键;UUID 主键无法键集分页,回退 OFFSET |

### 6.6 日志与报告

- **转换日志**:默认 `./conversion.log`,记录每张表的 DDL 状态、行数、索引数、错误;

- **错误日志**:默认 `./errors.log`,仅记录失败事件;

- **HTML 报告**:通过"查看报告"按钮打开,包含统计汇总与错误详情;

- **评估报告**:通过"迁移评估"生成,包含兼容性评分、风险等级、风险项列表;

- **Web 日志**:`fgmysql2pg.web.log`,记录 Flask 服务启动与运行时输出;

- **检查点文件**:`./.checkpoint/{schema}.{table}.json`,记录断点续传状态,迁移完成后自动删除。

### 6.7 调试技巧

1. 启动时加 `FGDEBUG=1` 环境变量,Flask 进入调试模式,异常栈完整打印;

2. 用 `fgmysql2pg test -c config.yml` 单独验证连接;

3. 用 `fgmysql2pg assess -c config.yml` 单独跑评估;

4. SSE 流断开时,前端会自动切换为 1 秒轮询,不影响进度;

5. 服务异常退出后,`/api/status` 仍可读取最后一次快照;

6. 查看 `fgmysql2pg.web.log` 排查 Web 服务异常;

7. 用 `python3 webctl.py status` 检查后台服务状态。

---

## 七、附录

### 7.1 配置文件完整示例

参见源码根目录 `config.example.yml`。

### 7.2 函数映射速查(部分)

| MySQL | PostgreSQL |

| --- | --- |

| `IFNULL(a,b)` | `COALESCE(a,b)` |

| `NOW()` | `CURRENT_TIMESTAMP` |

| `CURDATE()` | `CURRENT_DATE` |

| `UNIX_TIMESTAMP()` | `extract(epoch from now())` |

| `DATE_FORMAT(d,fmt)` | `TO_CHAR(d,fmt)` |

| `STR_TO_DATE(s,fmt)` | `TO_TIMESTAMP(s,fmt)` |

| `FIND_IN_SET` | `array_position(string_to_array(...))` |

| `FIELD` | `CASE` |

| `GROUP_CONCAT` | `string_agg` |

| `CONCAT_WS` | `ARRAY_TO_STRING(ARRAY[...])` |

| `JSON_OBJECT` | `json_build_object` |

| `JSON_ARRAY` | `json_build_array` |

| `JSON_EXTRACT(doc,'$.k')` | `(doc->'k')` |

| `JSON_VALUE(doc,'$.k')` | `(doc->>'k')` |

| `REGEXP_LIKE` | `~` |

| `IF(c,a,b)` | `CASE WHEN c THEN a ELSE b END` |

| `INSERT(str,pos,len,new)` | `overlay(str placing new from pos for len)` |

| `SUBSTRING_INDEX` | `split_part` |

| `QUOTE` | `quote_literal` |

| `STD/STDDEV` | `stddev_samp` |

| `VARIANCE` | `var_samp` |

| `LAST_INSERT_ID()` | `lastval()` |

| `SCHEMA()` | `current_schema()` |

| `MATCH(col) AGAINST(q)` | `to_tsvector(col) @@ to_tsquery(q)` |

### 7.3 数据类型映射速查

| MySQL | PostgreSQL |

| --- | --- |

| `TINYINT(1)` | `BOOLEAN` 或 `SMALLINT`(配置项 `tinyint1_as_boolean`) |

| `TINYINT` | `SMALLINT` |

| `SMALLINT` | `SMALLINT` |

| `INT` / `MEDIUMINT` | `INTEGER` |

| `BIGINT` | `BIGINT` |

| `SMALLINT UNSIGNED` | `INTEGER`(升档) |

| `INT UNSIGNED` | `BIGINT`(升档) |

| `BIGINT UNSIGNED` | `NUMERIC(20,0)`(升档) |

| `DECIMAL(p,s)` / `NUMERIC` | `DECIMAL(p,s)` / `NUMERIC` |

| `FLOAT` | `REAL` |

| `DOUBLE` | `DOUBLE PRECISION` |

| `BIT(n)` | `BIT(n)`(保留长度) |

| `CHAR(n)` | `CHAR(n)` |

| `VARCHAR(n)` | `VARCHAR(n)` |

| `TEXT` / `LONGTEXT` / `MEDIUMTEXT` / `TINYTEXT` | `TEXT` |

| `BLOB` / `LONGBLOB` / `MEDIUMBLOB` / `TINYBLOB` | `BYTEA` |

| `BINARY` / `VARBINARY` | `BYTEA` |

| `DATE` | `DATE` |

| `TIME` | `TIME` |

| `DATETIME` | `TIMESTAMP` |

| `TIMESTAMP` | `TIMESTAMPTZ` |

| `YEAR` | `SMALLINT` |

| `JSON` | `JSONB` |

| `ENUM` | `TEXT`(PG enum 需手动维护值域) |

| `SET` | `TEXT` |

| `BOOLEAN` / `BOOL` | `BOOLEAN` |

| `UUID` | `UUID` |

| `POINT` / `LINE` / `POLYGON` / `GEOMETRY` / `MULTIPOINT` | 同名 PostGIS 类型 |

### 7.4 关于作者

作者:风哥

官方网站:  http://www.fgedu.net.cn ,   http://www.itpux.com

数据库教程:   https://edu.51cto.com/lecturer/8020378.html

---