迁移文件应按严格递增版本号命名(如0001_init_users_table.sql),每文件只做一件事,含up/down段,用标准sql,配合migration_history表实现幂等与线程安全。

怎么设计 migration 文件的结构才不会翻车
迁移工具的核心不是执行 SQL,而是让每次变更可追溯、可重放、可回退。别一上来就写 execute_sql(),先想清楚文件怎么组织。
- 每个 migration 文件必须带严格递增的版本号前缀,比如
0001_init_users_table.sql、0002_add_email_index.sql——靠字典序保证执行顺序,别用时间戳(本地时区/编译机时间不一致会乱) - 一个文件只做一件事:建表、加字段、改索引……不要在
0003_...里既改字段又删视图,否则出错时无法精准定位 - 必须包含配对的
UP和DOWN段(哪怕用注释标记),例如:-- UP\nCREATE TABLE users (...);\n-- DOWN\nDROP TABLE users;
,否则后续想回退就只能手动擦屁股 - SQL 里禁用方言特有语法(如 MySQL 的
AUTO_INCREMENT、PostgreSQL 的SERIAL),统一用标准 SQL + 运行时适配器转换,不然换数据库引擎直接挂
如何让 migrate 命令真正幂等且线程安全
线上服务可能多实例同时启动,如果两个进程都去跑 migrate up,轻则报错,重则数据错乱。关键不是“锁文件”,而是用数据库自身做协调。
- 建一张
migration_history表(字段至少含version、applied_at、checksum),每次执行前查它,已存在就跳过——这是唯一靠谱的幂等依据 - 用
INSERT ... ON CONFLICT DO NOTHING(PostgreSQL)或INSERT IGNORE(MySQL)插入新记录,失败即说明已被其他进程抢跑,不抛异常、不中断流程 - 避免在 C++ 里用
std::filesystem::create_directories()或临时文件做锁——分布式环境完全无效,本地测试也掩盖并发问题 - 执行单条 SQL 前加事务(
BEGIN/COMMIT),但整个 migration 流程不要包在一个大事务里——长事务阻塞其他查询,且部分数据库(如 MySQL MyISAM)不支持 DDL 回滚
为什么不能直接用 libpq 或 sqlite3 原生 API 写迁移逻辑
裸调用驱动 API 看似灵活,实际会让错误处理、参数绑定、连接复用变成黑洞。
-
libpq的PQexec()返回PGresult*,但迁移脚本里大量字符串拼接 SQL(比如动态表名),极易触发 SQL 注入——你真敢把用户配置的table_prefix直接插进char buf[1024]? - SQLite 的
sqlite3_exec()不支持参数化 DML,遇到带单引号的默认值(如DEFAULT 'O''Reilly')就得手写转义,而 migration 脚本偏偏常含这类值 - 不同数据库对事务内 DDL 的行为不一致(PostgreSQL 允许,MySQL 5.7+ 要求 AUTOCOMMIT=OFF),原生 API 不帮你屏蔽——得自己写一堆
#ifdef分支 - 推荐方案:用
sqlpp11或轻量封装(比如封装execute_statement(const std::string& sql, const std::vector<:string>& params)</:string>),把参数绑定和错误码翻译收口
如何验证 migration 工具在真实环境不丢数据
开发机上跑通 up/down 不代表安全。重点验证三类场景:
- 给大表(千万级)加非空字段且带默认值:PostgreSQL 会全表重写,MySQL 8.0+ 可能锁表,必须在测试库用真实数据量压测,看是否超时或 OOM
- 执行
DOWN后再UP:检查数据是否还在(比如ADD COLUMN后DROP COLUMN再加回来,原始数据丢了没?) - 中断恢复:杀掉正在运行的
migrate进程,重启后工具是否从断点继续(靠migration_history记录判断),而不是重复执行或跳过 - 别信“本地 SQLite 测试通过”——SQLite 的类型亲和性、约束延迟检查和主流数据库差很远,至少要在目标 DB(如 PostgreSQL Docker 实例)里跑一遍集成测试
事情说清了就结束。最麻烦的永远不是写第一个 migration,而是三年后有人往旧脚本里加一行 ALTER TABLE 却忘了更新 DOWN 段,或者 checksum 校验没开,导致补丁包发到生产环境时悄悄跳过了某次关键修复。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











