go语言需用golang-migrate/migrate等第三方cli工具实现真正数据库迁移,而非gorm的automigrate或手写db.exec;迁移文件须按时间戳成对命名up/down.sql,代码中调用需加版本检查与超时控制,且ddl事务支持因数据库而异。

Go 语言本身不提供数据库迁移能力,database/sql 只负责执行 SQL;真正做版本化迁移,必须用 migrate(即 golang-migrate/migrate)这类第三方 CLI 工具——不是 GORM 的 AutoMigrate,也不是手写 db.Exec 循环。
为什么不能在 Go 代码里直接调 migrate.Up()
并发启动时多个实例可能同时执行同一迁移,触发 PostgreSQL 的 relation already exists 或 MySQL 的 duplicate column 错误;migrate CLI 内置数据库锁(如 schema_migrations 表 + SELECT FOR UPDATE),而 Go API 封装层容易绕过它。
- 若真需代码触发(如 Serverless 场景),必须先调
m.Version()检查当前版本,再用context.WithTimeout包裹m.Migrate(1) - 别传裸连接字符串给
migrate.New(),应复用已初始化的*sql.DB实例,避免重复建连接 - PostgreSQL 用户注意:数据库用户必须有
CREATE TABLE权限,否则up会静默失败
迁移文件命名和内容怎么写才不出错
文件名必须是 YYYYMMDDHHIISS_description.up.sql 和 .down.sql 成对出现,时间戳保证全局顺序;description 里不能含空格或特殊字符,否则 CLI 解析失败。
-
.up.sql里不要写条件逻辑(比如 “如果字段不存在就加”),SQL 就是确定性指令 -
.down.sql必须能完全撤销.up.sql的副作用:up 中INSERT初始化数据,down 就得DELETE WHERE version = 'xxx' - 涉及重命名、类型转换等高危操作,优先用三步法:
CREATE TABLE new_users→INSERT INTO new_users SELECT ... FROM users→DROP TABLE users,而不是单条ALTER TABLE RENAME COLUMN
MySQL / SQLite 下事务支持很有限
PostgreSQL 支持 DDL 在事务中执行,MySQL 8.0+ 对部分 ALTER 操作支持事务,但 SQLite 的 ALTER TABLE 仍无法回滚——这意味着 .down.sql 不一定真能撤回所有变更。
- 别依赖 migrate 默认开启事务;如需强一致性,必须在 SQL 文件开头显式写
BEGIN;,结尾写COMMIT;或ROLLBACK; - 生产环境跑迁移前,务必用同版本数据库手动执行一遍:
mysql -u root -p mydb - MySQL 开启
innodb_lock_wait_timeout,避免长迁移卡住导致应用启动超时失败
真正难的不是命令怎么敲,而是每条 .up.sql 都得想清楚:它改了什么结构、影响哪些查询、down 时怎么清理干净、有没有被其他服务正在读写——这些没法靠工具自动推导,只能人来兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











