回滚成功需每个.up.sql严格匹配可执行.down.sql;migrate down 1按版本号从高到低执行当前最高已应用版本的.down.sql,依赖schema_migrations表而非操作日志,缺失或错误.down.sql将静默跳过。

回滚能成功,前提是每个 .up.sql 都有对应、可执行的 .down.sql,且版本号严格匹配。
回滚命令为什么只执行部分迁移?
执行 migrate down 1 时,工具不是“撤销最后一次 up”,而是按版本号从高到低,找当前已应用的最高版本对应的 .down.sql 执行一次。它依赖 schema_migrations 表记录的已应用版本列表,而非操作日志。
- 如果当前数据库版本是
1085649617,且该版本的1085649617_xxx.down.sql存在且语法正确,命令才生效 - 若该
.down.sql文件缺失、权限不足、或内容为空,命令会静默跳过(不报错),但实际未执行任何 SQL - 使用
goto命令前,务必确认目标版本的.down.sql文件存在——工具不会自动补全或提示缺失
down.sql 写什么才真正安全?
回滚脚本不是“反向拼写 up.sql”,而是要精确抵消其副作用。常见错误是只删表、不删索引,或忽略约束依赖。
- 创建表的
.up.sql应配DROP TABLE IF EXISTS users;,而不是TRUNCATE users;(后者不删表结构) - 添加字段的
ALTER TABLE users ADD COLUMN phone TEXT;,对应ALTER TABLE users DROP COLUMN phone;;MySQL 5.7+ 才支持DROP COLUMN,旧版本需用MODIFY重建表 - 加唯一索引的
CREATE UNIQUE INDEX idx_email ON users(email);,必须配DROP INDEX idx_email ON users;,漏掉会导致后续up失败
回滚失败后数据库变“脏”怎么处理?
当 .down.sql 执行中途报错(如外键冲突、权限拒绝),migrate 会标记数据库为 “dirty” 状态,并拒绝后续所有迁移命令,直到人工干预。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 运行
migrate force 1085649617可强制将当前版本设为指定值,但仅用于修复状态,不执行任何 SQL - 先手动清理残留变更(例如删掉
.down.sql没删干净的索引),再用force同步版本号 - 切勿在生产环境直接
force到任意版本——必须确保数据库实际结构与目标版本一致,否则后续up会因 DDL 冲突失败
不同数据库对 down 操作的支持差异
PostgreSQL 和 MySQL 对 DDL 回滚的支持程度不同,直接影响 .down.sql 的写法。
- PostgreSQL 支持事务内 DDL,
.down.sql可以包裹在BEGIN; ... COMMIT;中,失败则全部回滚 - MySQL 的 DDL 默认自动提交,无法回滚。所以
ALTER TABLE类操作一旦执行就不可逆,.down.sql必须设计成幂等且可重试(例如用DROP INDEX IF EXISTS) - CockroachDB 要求
down.sql中不能含CREATE DATABASE或DROP DATABASE—— 这类操作只能在初始化阶段做,迁移中禁止
最常被忽略的点:回滚不是“撤销命令”,而是“执行另一组预定义 SQL”。.down.sql 的可靠性,完全取决于你写它时有没有站在生产环境出错的那一刻去想——它能否在表被锁、索引残留、权限收紧的情况下依然跑通。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










