
凌晨两点我盯着那条ALTER TABLE的执行结果手心全是汗。不是 SQL 写错了——SQL 本身干干净净加一列再补一个默认值。错在我是在生产库上手动、直接、用 Navicat 连上去跑的。跑完那一下线上订单下单的接口就开始超时监控面板上一片飘红。我到现在都记得旁边同事那个眼神就差没开口问我你怎么敢的。那晚的事故查来查去才发现根因跟我加的列本身没关系是被我一直压着的另一个查询把锁给憋住了。但那一刻我真的慌了——因为我不知道自己刚才手动改生产库这个动作到底动过哪张表改了什么有没有人记录过。整个公司没有一份表结构变更的记录全靠大家心里有数。打岔一下你别笑。后来我翻 git 历史想找有没有人做过表结构备份结果一个都没找到。那一刻我算是彻底想明白了数据库的变更是整条发布链路里最容易被人肉的一环也是我最看走眼的一环。我为什么当时不直接用它说实话那阵子团队里确实没人提版本管理这四个字。大家默认表结构嘛谁手头有事谁就顺手改一下改完记住就行。可记住这回事在三个人以上协作时就是玄学——你记你加过这个字段我记我删过那张表等到要联调两边都对不上然后一起骂环境有问题。骂完一查是自己早改过生产库没通知别人。你可能会想那我用个现成的迁移工具Migration不就完了比如 Flyway、Liquibase 之类把每个变更写成一个带版本号的脚本按顺序执行谁改过一查就知道。我当时也是这么想的。可一上手才发觉最大的坑根本不是工具是你想不想把改库这件事从个人操作变成受控变更。工具再多你不愿意把每一次ALTER都写成脚本、走一次评审、留一份记录它就还是裸奔。我说个我后来踩过的真实坑。为了省事我把表结构变更和业务代码放在同一个发布脚本里想着一条流水线全搞定。结果有一次代码先发上去了脚本里的迁移因为锁超时没跑完生产库的表还是旧结构新代码一访问就报列不存在。那半小时的线上故障全程是我一个人在那一边安抚客服一边补跑迁移。后来我才悟到迁移最好跟代码拆开代码可以回滚Schema 迁移通常没法干净地回滚把它混在一起等于把两个不同节奏的东西强行捆死出事了连谁先谁后都说不清。还有更现实的一层——改了库之后你怎么知道它到底行不行。我们常说的那个词叫生产环境的表结构漂移迁移脚本在测试环境跑得好好的到生产一跑就报错因为生产的表和测试的早就长得不一样了。所以现在我会在每次迁移跑完后先做一次结构比对确认目标环境真的和我预期一致再让业务代码上去。这一步看着啰嗦但能省掉太多我以为它改了的尴尬。这段经历也让我回头重新看了一遍自己的分工。你发现没有越是到了关键的生产库越没人敢轻举妄动可恰恰又最容易因为怕麻烦而走野路子。数据库的变更管理考验的不是你会不会写ALTER TABLE而是你愿不愿意把每一步都变成可以被追溯、被评审、被回看的流程。写代码是能力敢不敢把代码库之外的东西也当代码来管才是更值钱的那点本事。说到这我得老实承认Agent智能体也确实帮我省了不少事——我可以让它先读一遍现有的迁移脚本清单标出哪些环境还没同步再让它生成一份结构比对报告。但它再怎么帮前提还是我得先把迁移有版本、变更留痕这套框架立起来。工具和 Agent 都是放大器放大器不会替你定规矩。话说到这把问题抛给你吧你现在的表结构变更是靠流程管着还是靠记住有没有哪次改库是你事后怎么也想不起来当时改了什么评论区聊聊我特别想听你手动改生产库的那次翻车经历——毕竟这种事说出来比埋在心里强。