最初的设计:两张"看起来很通用"的表
SEO 表:一张表存所有内容的 SEO,通过"对象类型 + 对象业务号"关联首页、栏目、文章、单页、专题。听起来很省:新增内容类型不用加字段。
模块配置表:一张 key-value 表,存每个内容模块的开关与默认值(评论是否需审核、列表每页几条等)。
两张表都符合"通用化"的直觉,也都撑不过两周。
SEO 表为什么被删
问题一:编辑时要跳页。 编辑一篇文章后,还要去另一个菜单配 SEO。结果是绝大多数文章根本没配 SEO,前台只能回落到站点默认值——等于 SEO 功能形同虚设。
问题二:查询多一次关联。 列表页要展示 SEO 状态(配了/没配),就得 join 一次多态表;而多态关联没有外键也没法建有效索引,只能按类型 + 业务号查,性能和一致性都差。
问题三:删除内容后产生孤儿记录。 多态关联没有级联删除,文章删了 SEO 记录还在,迟早要靠数据体检清理。
改法很直接:在五张内容表上各加三列——seo_title、seo_keywords、seo_description,留空即回落默认值。编辑内容时同屏维护,不配也能跑。
结果是:SEO 配置率从"少数文章"变成"几乎每篇都有"。功能是否被使用,往往取决于它出现在哪个页面。
key-value 配置表为什么被删
模块配置表的问题更隐蔽:
- 每个配置项的键是字符串,代码里写
get('comment_audit'),拼错不报错,只是取到默认值; - 取值都要经过一次"查表 + 反序列化",且没有类型约束;
- 与系统域的配置(站点信息、上传设置、短信)职责重叠,同一个开关两处都能配。
处理方式是收口到系统配置:所有配置统一进 sys_configs 的「网站配置」面板,按分组管理(内容设置、评论设置等)。内容模块不再有自己的一套配置。
一致性收益很明显:备份只需一份,迁移只需一套,权限按分组分配。
迁移时守的两条
删表前把存量数据搬走:
- SEO 数据搬迁:按类型逐批把旧表的三列写回对应内容表(只补空值,不覆盖已手填的);
- 配置项搬迁:旧 key-value 的键映射到新分组的具体键,映射表写在脚本里,逐项核对"旧键 → 新键 → 默认值"。
搬迁必须可重跑:中途失败不需要回滚数据库,重跑时按"是否已有值"决定覆盖策略。
什么时候"通用表"是对的
不是所有多态表都该删。判断标准是:
- 字段少且高度一致(就是这三个 SEO 字段)→ 内联;
- 字段差异大、需要独立生命周期(订单、结算单)→ 独立表;
- 配置项种类多但要强类型(系统配置)→ 单表 + 分组 + 代码常量键。
"通用"并不等于"好维护"。一个只有 3 列的多态表,省下的是一列 DDL,付出的是查询复杂度、编辑体验和数据一致性。
结果
两张表下线后,全库表数从 166 降到 164,代码里少了多态关联的分支,后台少两个菜单。删功能有时是提升交付质量最直接的方式。