结论先给:如果原字段仍能表达业务含义,只是数量或枚举不够,优先做“增量扩展”——加字段、加关联表、加映射层,旧数据保留并逐步回填;如果原字段的语义已经混乱到无法区分“是什么”和“为什么”,那扩展只会把问题放大,此时应新建结构并做一次性迁移。判断依据不是字段多少,而是旧字段是否还能被稳定解释。
字段不够用的常见表现有三种:一是枚举值反复追加,代码里到处是if type == 7这类魔法数字;二是同一字段在不同页面被赋予不同含义,比如status在后台表示审核状态,在前台却被当成上下架状态;三是新增业务需要多值,而原字段是单值,只能靠逗号拼接。
前两种情况里,第一种适合增量扩展,第二种和第三种更适合重建结构。一个可操作的验证动作是:随机抽取二十条旧记录,让不熟悉该模块的人只看字段名和值,判断这条记录的业务状态。如果判断结果与真实状态一致率很低,说明字段语义已经不可靠,继续加字段只会让歧义叠加。这个动作的结果直接决定下一步走“加字段”还是“建新表”。
增量扩展成立的前提是:旧字段仍有唯一、稳定的解释,新需求只是补充维度。此时可以按下面的顺序推进。
这套做法的代价是过渡期存在两份数据,需要明确哪一份是权威来源。假设一个预约类站点,原字段service_type只能存一个服务类型,后来需要支持多选。若直接改成逗号拼接字符串,后续按类型筛选会变得困难;若新增一张appointment_service关联表,旧字段保留作主类型,历史数据按主类型回填一条关联记录,新数据写多条。这样筛选、统计、导出都能基于关联表完成,旧页面也不用立刻改。
反例很具体:当旧字段承担了“身份标识”作用,而新需求要求同一实体有多个身份时,增量扩展会失效。比如原表用phone作为用户唯一标识,后来业务需要支持一个用户绑定多个手机号,同时还要保留历史订单归属。此时给原表加phone2、phone3只是把一对多关系硬塞进单表,查询、去重、登录校验都会变复杂。
这种情况下应新建user_contact表,把手机号从用户主表剥离,原phone字段保留为“主联系方式”用于兼容旧逻辑。迁移时先写入新表,再逐步把校验逻辑切过去。判断是否属于这类反例,可以看一个信号:新需求是否要求“同一主体对应多条同类记录”。如果是,单表加列就不是扩展,而是拖延。
字段扩展常伴随旧接口、旧后台或旧合作方的退出。退出时不必全盘推倒,值得保留的通常是三类:一是仍在校验的历史数据,比如订单快照、操作日志;二是被多个页面复用的只读查询;三是已经沉淀下来的枚举字典,即使字段废弃,字典本身仍可作为新结构的取值参考。
不值得保留的是那些只服务于旧交互、且没有任何数据沉淀的中间层。判断方法是看这段逻辑停止运行后,是否还有下游依赖它的输出。没有下游依赖的,可以随旧系统一起下线;有下游依赖的,先加一层适配,再安排退出时间点。这个判断会影响迁移顺序:先切有下游依赖的部分,最后清理无依赖的残骸。
不要直接开始改表。先导出一份字段清单,标注每个字段的当前含义、写入来源、读取位置和是否存在多义。对存在多义的字段,标记为“需重建”;对含义唯一但数量不足的,标记为“可增量”。这份盘点结果决定迁移是分阶段加字段,还是新建结构后一次性切换。盘点完成后,再选择一个影响面最小的模块做试点迁移,验证回填和切换流程,确认无误后再推广到其他模块。整个过程中,旧数据不要删除,保留到新结构稳定运行并经过至少一个完整业务周期为止。