mysqldiff 是唯一能稳定生成可读 HTML 差异报告的官方跨库结构比对工具,需显式指定不同 host/port 的 server1/server2,支持字段级解析,默认不检查库级字符集需手动核验。
用 mysqldiff 生成跨库结构差异报告最稳
直接上结论:mysql官方工具 mysqldiff 是目前唯一能稳定输出可读 html 差异报告的跨库结构比对方案,不用写脚本、不依赖 python 包、不碰 sql 解析器。
它本质是 MySQL Utilities 套件里的命令行工具,底层走的是 SHOW CREATE TABLE + 字段级解析,不是靠 INFORMATION_SCHEMA 粗暴对比,所以能识别默认值变更、注释差异、索引顺序变化这些容易被忽略的点。
常见错误现象:mysqldiff 报错 Access denied for user 不是因为权限不够,而是没给 --server1 和 --server2 显式指定用户(哪怕都是 root),必须带 user:pass@host:port 格式;另外两个库不能在同一个实例里用不同 database 名来“模拟跨库”,它强制要求物理分离(不同 host 或 port)。
- 安装方式:MySQL 5.7 自带,8.0+ 需单独装
mysql-utils包(Debian/Ubuntu 装mysql-utilities,CentOS/RHEL 用mysql-community-utils) - 基础命令:
mysqldiff --server1=root:pass@10.0.1.10:3306 --server2=root:pass@10.0.2.10:3306 db1:db2 --changes-for=server2 --difftype=html > diff.html -
--changes-for=server2表示“以 server2 为目标库,列出让它变成 server1 需要执行的 DDL”——这点反直觉,但决定了生成的 SQL 是否可直接执行 - HTML 输出里所有表名、字段名、SQL 片段都自动加了
<code>标签,不用再加工
mysqldiff 不支持同实例跨 database 的真实原因
它不是偷懒,是设计使然:当 --server1 和 --server2 指向同一 IP+端口时,mysqldiff 会拒绝运行,并报错 Servers must be different。这不是 bug,是为避免误把同一份数据当成两套源来比对。
实际迁移中常遇到“老库在旧实例,新库刚建在新实例”,这时必须确保两个 --serverX 参数的 host/port 完全不同。如果测试环境只有单实例,得用 Docker 启一个临时 MySQL 实例,或改 host 文件伪造两个域名指向同一 IP(port 必须不同)。
立即学习“前端免费学习笔记(深入)”;
- 绕过方法:用
mysqldump -d分别导出两个库的结构,再用diff或git diff对比文本——但丢失字段顺序、索引类型、外键约束等语义信息 - 更靠谱的替代:用
pt-table-sync --print --sync-to-master(Percona Toolkit),但它只输出同步 SQL,不生成 HTML 报告 - 注意
mysqldump -d导出时加--skip-extended-insert,否则单条 INSERT 变成多行,diff 结果不可读
HTML 报告里哪些差异真会影响迁移
生成的 HTML 里列了一堆“不同”,但不是每项都要处理。重点盯三类:
- 字段类型不一致:
INTvsBIGINT、VARCHAR(255)vsVARCHAR(500),尤其后者可能触发隐式截断 - 默认值变更:
DEFAULT NULLvsDEFAULT '',MySQL 8.0+ 对严格模式下空字符串插入行为更敏感 - 索引定义差异:主键字段顺序不同、联合索引字段数不一致、
KEY和UNIQUE KEY类型混用——ORM 或分库中间件可能依赖索引名或顺序 - 忽略项:
COMMENT、AUTO_INCREMENT当前值、COLLATE相同但写法不同(如utf8mb4_0900_ai_civsutf8mb4_general_ci)一般不影响功能,除非业务显式依赖排序规则
字符集和排序规则差异怎么不被漏掉
mysqldiff 默认只比对表结构,不检查库级 CHARACTER SET 和 COLLATION。如果 source 库是 utf8mb4,target 库建库时忘了指定,后续插入 emoji 就会失败,但 HTML 报告里完全看不到这行差异。
必须手动补查:SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = 'db_name';,两边各跑一次,结果不一致就得提前改 target 库。
- 建库语句务必显式带上:
CREATE DATABASE db2 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; - 如果 target 库已存在且字符集不对,
ALTER DATABASE db2 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;只改库默认值,已有表不受影响,还得逐个ALTER TABLE - 表级字符集优先级高于库级,所以
mysqldiff会把表的CHARSET差异标出来,但不会提示“库默认值不一致”这种全局风险
跨库结构对比不是比完就完的事,HTML 报告只是起点,真正卡住迁移的往往藏在库级配置、隐式类型转换、或者某个字段的 NOT NULL 约束漏掉了。


















