先判断一件事:现有数据是否必须原地保留。如果旧记录可以迁移、下游消费方也能同步调整,就做一次结构扩展;如果旧数据不能停、外部接口已经按现有字段写死,就在原表旁边加一张扩展表,用关联键把新字段挂上去。两条路都能走,差别在于你愿意承担一次停机迁移,还是愿意长期维护一层关联查询。
能迁移的条件通常有三个:数据量在可接受范围内、没有外部系统直接读这张表、业务允许一个短窗口停止写入。满足这三条,直接改结构最干净,字段语义统一,后续查询不用反复联表。
不能迁移的条件也很明确:有第三方按固定字段对接、历史数据要原样留档、或者写入不能中断。这时不要硬改原结构,改为新增扩展表,主表只加一个稳定的关联标识。判断依据不是“哪种更先进”,而是“改完之后,谁需要跟着改”。需要跟着改的人越多,越应该选旁路扩展。
假设一张订单表原本只有收货人、电话、地址,上线后发现需要区分省市区、需要记录发票抬头、还需要保存多个联系人。原地扩展的做法是:
这个顺序的关键动作是“先双写、后回填、再切换读取”。如果跳过双写直接改读取端,一旦回填不完整,页面就会出现空值,而你会误以为是查询写错了。
旁路扩展的核心是一张扩展表,字段包括:主表标识、扩展键、扩展值、创建时间。它适合字段种类还会继续增加的情况,比如今天加发票信息,下个月加物流偏好,明年加会员标签。每加一类只需要插入新行,不用再改表结构。
关联键必须选一个不会变的值。自增主键、业务单号都可以,但不要用手机号或邮箱,因为它们可能被修改。扩展表的查询代价是每次读取都要关联,所以只把真正低频、可选的字段放进去;高频筛选的字段仍然应该留在主表,否则每次列表页都要联表,响应会明显变慢。
假设某网站上线后需要给用户增加“所属行业”和“企业规模”两个字段,用户量不大,也没有外部系统直连数据库。这时原地扩展更划算:新增两个可空列,写入端先写,历史用户留空并在后台逐步补录。反过来,如果这两个字段来自第三方同步,且对方随时可能增加新字段,就应改用扩展表,把每次同步的键值对存下来,避免每来一个新字段就改一次表。
如果你拿不到生产库的改表权限,也拿不到完整的历史数据,仍然可以做三件事:
这些动作能让你在权限到位后直接推进,但要注意:写入量或抓取量暂时为零、回填任务没有报错,都不能单独证明字段设计已经正确。零写入可能只是没有新数据产生,没有报错可能只是回填范围为空。真正的验证是拿一条已知的旧记录,检查扩展后能否还原出完整信息。
结构改完不是结束。要确认三件事:读取端是否已经全部切到新字段、旧字段是否还有写入、回填任务是否覆盖了全部历史区间。只要还有一处读取端在用旧字段,就不能删旧列。删除动作应该放在最后,并且在此之前保留一次可回滚的备份。字段扩展的代价往往不在改表那一刻,而在之后几周里陆续发现的遗漏调用点。