mysqldiff 不适合比对存储过程,因其默认忽略routine对象且不生成CREATE PROCEDURE语句;应导出INFORMATION_SCHEMA.ROUTINES定义后用diff比对,或使用mysqldbcompare --object-type=routine,也可用Python脚本灵活提取差异。

直接用 mysqldiff 比存储过程不靠谱
mysqldiff 默认只比表结构(CREATE TABLE),对存储过程、函数、触发器等对象基本忽略。即使加了 --difftype=sql,它也只输出 DDL 差异,不会生成 CREATE PROCEDURE 的替换语句,更不会处理 DEFINER 或 SQL SECURITY 变化。
常见错误现象:mysqldiff 报告 [PASS],但实际两个库的存储过程逻辑完全不同;或者报 [FAIL] 却只显示“object definition differs”,没具体哪行不同。
- 必须显式指定要比较的对象类型,比如加上
--object-type=procedure(部分新版 MySQL Utilities 支持,但非默认) - 更稳妥的做法是先导出所有存储过程定义,再用文本工具比对
-
INFORMATION_SCHEMA.ROUTINES是唯一可靠的数据源,字段包括ROUTINE_NAME、ROUTINE_DEFINITION、DEFINER、SQL_SECURITY等
导出 ROUTINE 定义再用 diff 对比
这是最可控、兼容性最好的方式,适用于 MySQL 5.6+ 所有版本,不依赖额外工具。
执行以下命令分别导出两个库的存储过程(含函数)定义:
mysql -h host1 -u user -p -Nse "SELECT CONCAT('DELIMITER ;;\n', ROUTINE_DEFINITION, ';;\nDELIMITER ;\n') FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='db1' AND ROUTINE_TYPE='PROCEDURE' ORDER BY ROUTINE_NAME" > db1_procedures.sql
mysql -h host2 -u user -p -Nse "SELECT CONCAT('DELIMITER ;;\n', ROUTINE_DEFINITION, ';;\nDELIMITER ;\n') FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='db2' AND ROUTINE_TYPE='PROCEDURE' ORDER BY ROUTINE_NAME" > db2_procedures.sql
注意点:
-
-N去掉列名头,-s强制简洁输出,避免多余空格干扰diff -
ORDER BY ROUTINE_NAME确保两个文件中过程顺序一致,否则diff会误报大量差异 - 如果要同时比函数,把
ROUTINE_TYPE='PROCEDURE'改成IN ('PROCEDURE','FUNCTION') -
ROUTINE_DEFINITION字段可能为NULL(如 DEFINER 权限不足),需提前确认账号有SELECT权限到INFORMATION_SCHEMA.ROUTINES
然后运行:diff -u db1_procedures.sql db2_procedures.sql。输出里 - 行是 db1 特有或不同的内容,+ 行是 db2 的对应内容。
用 mysqldbcompare 时必须加 --object-type=routine
mysqldbcompare 是 MySQL Utilities 中真正支持存储过程对比的工具,但它默认不启用 routine 比较,必须手动打开。
典型命令:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
mysqldbcompare --server1=user:pass@host1:3306 --server2=user:pass@host2:3306 db1:db2 --object-type=routine --difftype=sql --changes-for=server2
关键参数说明:
-
--object-type=routine:必须显式声明,否则跳过所有存储过程/函数 -
--changes-for=server2:表示以 server1 为基准,生成让 server2 同步的 SQL(即DROP + CREATE PROCEDURE) -
--difftype=sql:输出可执行 SQL,不是 unified diff 格式 - 如果遇到
ERROR: The database xxx does not exist,检查库名拼写和权限,mysqldbcompare不会自动忽略不存在的库
容易踩的坑:
- 两个库中同名存储过程的
DEFINER不同会导致比对失败,加--skip-definer-check可跳过(MySQL Utilities 1.6.5+) - 若过程体含换行符或注释格式不一致,
mysqldbcompare可能误判为差异,建议先导出再人工核对关键逻辑 - 它不会比对过程内的表访问权限或调用链,仅比定义文本
Python 脚本做轻量级 routine 差异提取
如果你不想装 MySQL Utilities,又需要自动化提取差异点(比如 CI 流程中判断是否需部署新过程),一个 30 行左右的 Python 脚本比任何命令行工具都灵活。
核心逻辑就是连两个库,查 INFORMATION_SCHEMA.ROUTINES,按 ROUTINE_NAME 分组,逐字段比对 ROUTINE_DEFINITION、SQL_SECURITY、IS_DETERMINISTIC 等。
示例关键片段:
cursor1.execute("SELECT ROUTINE_NAME, ROUTINE_DEFINITION, SQL_SECURITY, IS_DETERMINISTIC FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA=%s AND ROUTINE_TYPE='PROCEDURE'", ['db1'])<br>procs1 = {r[0]: r[1:] for r in cursor1.fetchall()}<br>cursor2.execute("SELECT ROUTINE_NAME, ROUTINE_DEFINITION, SQL_SECURITY, IS_DETERMINISTIC FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA=%s AND ROUTINE_TYPE='PROCEDURE'", ['db2'])<br>procs2 = {r[0]: r[1:] for r in cursor2.fetchall()}<br>for name in set(procs1.keys()) | set(procs2.keys()):<br> if name not in procs1:<br> print(f"+ {name} missing in db1")<br> elif name not in procs2:<br> print(f"- {name} missing in db2")<br> elif procs1[name] != procs2[name]:<br> print(f"~ {name} definition or attributes differ")
这种脚本的好处是:可精确控制比对字段、支持模糊匹配(比如忽略空格/注释)、能输出 JSON 供后续解析,且不暴露密码到 shell 历史中。
真正麻烦的从来不是“怎么比”,而是“比完之后要不要删掉旧过程再建新的”——DEFINER 权限、SQL SECURITY 切换、事务安全、线上锁表风险,这些都得在生成 SQL 前想清楚。










