打开网易新闻 查看精彩图片

去年双11,某电商平台的订单表加了个字段,整个支付链路挂了8分钟。用户卡在付款页,客服电话被打爆,技术负责人当晚写了辞职信——虽然最后没交。

这事不稀奇。数据库迁移是Node.js生产环境最高风险的操作,没有之一。开发环境ALTER TABLE重启服务器就完事,生产环境10万用户同时在线,一个ADD COLUMN NOT NULL能锁死整张表。更坑的是,新旧代码同时跑,一个写老字段一个写新字段,数据直接乱套。

大部分团队怎么解决?祈祷,加凌晨3点的维护窗口。本文讲怎么用工程手段替代祈祷。

「扩展/收缩」模式:把一次作死拆成三次安全操作

「扩展/收缩」模式:把一次作死拆成三次安全操作

零停机迁移的核心叫expand/contract pattern(扩展/收缩模式),也有人叫并行变更。它把任何破坏性schema改动拆成三次独立部署,每次都对线上代码安全。

第一次部署:只加新结构,不动老的。比如给users表加display_name字段,设成nullable——nullable列不会锁表,这是关键。此时老代码只写username字段,新代码同时写两个字段,两边互不干扰。

第二次部署:跑后台任务回填数据。不是一次性UPDATE全表,而是分批处理。上面代码里的backfill脚本每次只拿1000行,改完歇100毫秒,把数据库负载压到最低。这步跑完,所有老数据都有了新字段的副本。

打开网易新闻 查看精彩图片

第三次部署:确认所有代码都读新字段后,DROP COLUMN username。这时候删列是安全的,因为已经没有任何代码引用它了。

三次部署,零停机。代价是流程变长,但比起8分钟宕机,这账很好算。

工具选型:为什么node-pg-migrate能活到现在

工具选型:为什么node-pg-migrate能活到现在

Node.js生态里迁移工具不少,node-pg-migrate是PostgreSQL阵营活得最久的。它支持JS/TS写迁移文件,DSL足够表达复杂操作,最重要的是——它不会替你隐藏SQL

很多ORM的迁移工具封装得太厚,你都不知道背后发了什么语句。生产环境需要的是可控性,不是魔法。node-pg-migrate让你手写SQL,但帮你管版本号、管执行顺序、管事务回滚。

安装就一行:npm install node-pg-migrate pg。配置走环境变量,CI/CD里塞个DATABASE_URL就能跑。

但工具只是工具。我见过团队用最好的迁移工具,照样在凌晨2点回滚。问题出在流程,不在工具。

打开网易新闻 查看精彩图片

生产环境的隐藏雷区:那些教程不会告诉你的

生产环境的隐藏雷区:那些教程不会告诉你的

第一,大表的索引操作。PostgreSQL 11之前,ADD INDEX会锁表;之后有了CONCURRENTLY选项,但代价是建索引变慢、CPU飙高。你需要在迁移脚本里显式指定CREATE INDEX CONCURRENTLY,然后盯着监控等它跑完。

第二,外键约束。ADD FOREIGN KEY默认会锁两张表。正确做法是先建索引,再单独加约束,或者干脆用NOT VALID + VALIDATE CONCURRENTLY分两步走。

第三,代码与schema的版本对齐。蓝绿部署时,老代码跑在新schema上,新代码跑在老schema上,这是常态。你的代码必须能容忍这种错位——读数据时fallback到老字段,写数据时双写。这要求业务代码有防御性设计,不是迁移工具能解决的。

有个细节很多人忽略:回填脚本跑完之后,一定要校验数据一致性。写个对账脚本,抽查username和display_name是否匹配,不匹配就告警。我见过回填任务中途失败,团队没发现,三个月后清理旧字段时丢了一千多条用户昵称。

最后,维护窗口没死透。有些操作就是没法在线做,比如PG 14之前的REINDEX TABLE。这时候诚实承认,提前公告,比假装自己能零停机然后翻车要强。

某金融科技公司的SRE负责人跟我说过一句话:「我们花了两年时间,把迁移窗口从每月一次降到每季度一次,但从来没敢说自己能零停机。」这大概是务实的技术团队该有的心态——追求零停机,但不为零而零。

你们团队最近一次生产迁移是什么时候?有没有遇到过新老代码抢写同一行数据的诡异bug?