需在建表前锁定目标环境版本与能力,统一使用最低兼容类型(如TEXT替代JSON),脚本按版本号管理并记录执行状态,避免重复执行;大表加索引须避开高峰,外键需确保父表有索引且云数据库显式开启约束;禁用生产ddl-auto,Flyway/Liquibase脚本须人工编写与校验。
DDL 脚本生成时字段类型不一致,怎么对齐开发和生产?
开发库用的是 json 类型,生产库是 mysql 5.7,不支持 json —— 这类问题不是“生成脚本”失败,而是生成了也不能跑。关键在建表前就锁定目标环境的版本和能力。
- 先查生产库版本:
SELECT VERSION();,再对照官方文档确认支持的类型(比如 MySQL 5.7 支持JSON,但有些云厂商阉割了) - 开发阶段统一用最低兼容类型:比如用
TEXT替代JSON,用DATETIME替代TIMESTAMP(避免时区隐式转换) - 工具生成脚本前必须指定目标方言,例如
mysqldump --no-data --compact比 ORM 自带的create_table更可控
ALTER TABLE 多次执行报错 ERROR 1060 (42S21): Duplicate column name
这是同步 DDL 最常卡住的地方:脚本被重复执行,而 ALTER TABLE ADD COLUMN 不具备幂等性。别指望靠“人工检查是否已存在”来规避。
- 加字段前先查
INFORMATION_SCHEMA.COLUMNS,例如:SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'user' AND COLUMN_NAME = 'status' - 用存储过程封装判断逻辑(适合 DBA 统一维护),或改用支持条件执行的工具如
pt-online-schema-change(注意它不适用于所有场景) - 更稳妥的做法是:把 DDL 脚本按版本号管理(如
v20240501_add_status_to_user.sql),配合一个schema_version表记录已执行脚本,由部署脚本控制跳过
外键和索引在生产环境创建失败,原因往往不在 SQL 本身
CREATE INDEX idx_user_email ON user(email) 在开发库秒建成功,到生产却卡住甚至超时——大概率是数据量和锁机制差异导致的。
- 大表加索引会锁表(MySQL 5.7 默认行为),必须避开业务高峰;8.0+ 可用
ALGORITHM=INPLACE, LOCK=NONE,但需确认参数是否启用 - 外键依赖的父表字段必须有索引,否则报
ERROR 1005;检查方式:SHOW INDEX FROM parent_table WHERE Column_name = 'id' - 云数据库(如阿里云 RDS、腾讯云 CDB)可能禁用外键,或要求显式开启
FOREIGN_KEY_CHECKS=1,且该设置不跨会话持久化
用 Flyway / Liquibase 生成 DDL,为什么上线后表结构还是不对?
工具只是执行载体,真正出问题的往往是迁移脚本没覆盖“隐式变更”。比如开发改了实体类字段名,但没写 RENAME COLUMN,只靠 JPA ddl-auto: update 生成的脚本不会包含重命名逻辑。
- 禁止在生产环境用
ddl-auto: update;它不生成可审计的 SQL,且对已有数据无感知(比如加NOT NULL字段会直接失败) - Flyway 的
V版本脚本必须手工编写,不能依赖代码生成;Liquibase 的generateChangeLog只适合初始化,后续变更仍要diffChangeLog+ 人工校验 - 每次提交 DDL 脚本前,用
mysqldiff或liquibase diff对比开发库与空库,确认输出和预期一致
最麻烦的不是语法错误,而是“看起来执行成功了,但字段默认值没生效”“索引建了但没被优化器选中”这类静默偏差。上线前一定要在预发环境连真实数据跑一遍查询计划,而不是只看 Query OK。










