数据库字段血缘分析策略

2026-02-09 | 从全量血缘 → 异常检测的决策过程与实施方案

1. 背景:为什么考虑字段级分析

前置工作中完成了表级数据流依赖图(见 DB 依赖关系图),发现了:

自然延伸的问题是:能否把同样的分析下沉到字段级别? 即对每个字段追溯写入来源(数据血缘 / Data Lineage)。

2. 全量血缘 vs 异常检测

全量血缘表(放弃)

  • 输出 500+ 行,大量"正常"记录淹没问题
  • 生成第二天就过时——任何新 API 都会使其失效
  • 审查效率极低:490 条正确中找 10 条异常
  • 前端到 API 的映射经常是动态的(hooks/stores),准确率低

异常检测报告(采纳)

  • 只输出有问题的字段,每条都值得审查
  • 可重复运行,类似 lint 工具
  • 输出可能只有 20-30 条,高信噪比
  • 直接产出可执行的行动项
核心判断:表级分析之所以有效,是因为找到了异常模式(有写没读、有读没写、完全没用)。字段级分析也应该遵循同样的原则——只报告偏离"健康生命周期"的字段。

3. 字段健康生命周期模型

一个健康的字段应有完整的生命周期,任何一环缺失即为异常:

Schema 定义
Code 写入 (INSERT/UPDATE)
Code 读取 (SELECT)
Both 约束 (FK/CHECK/验证)

4. 异常检测维度

维度异常类型说明严重度
存在性 死字段 Schema 有定义,代码中没有任何 INSERT/UPDATE 写入
只写不读 字段被写入但从未被 SELECT 使用
代码引用不存在的列 SQL 语句引用了 Schema 里没有的字段名
一致性 多源写入 同一字段被 3+ 个不同 API 入口写入,可能有一致性风险
写入值域不匹配 代码写入的值与 Schema 类型/约束不一致
完整性 缺约束的外键 字段名形如 *_id 但没有 FK 约束
隐式写入 只被 trigger / DB function 写入,应用层不知道
冗余性 DEFAULT 从未覆盖 列有 DEFAULT 值且代码从未写入过非默认值

5. 实施方案

分析脚本需要交叉比对三个数据源

graph LR
  A["information_schema.columns\n(Schema 真相)"] --> D["交叉比对引擎"]
  B["代码中 INSERT/UPDATE\n(写入扫描)"] --> D
  C["代码中 SELECT\n(读取扫描)"] --> D
  D --> E["异常报告\n(仅输出问题字段)"]
  style A fill:#312e81,stroke:#6366f1
  style B fill:#064e3b,stroke:#10b981
  style C fill:#78350f,stroke:#f59e0b
  style D fill:#1e293b,stroke:#475569
  style E fill:#7f1d1d,stroke:#ef4444

数据源 1:Schema 真相

SELECT table_name, column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'tenant_xxx'
ORDER BY table_name, ordinal_position;

数据源 2:代码写入扫描

扫描所有 backend/**/*.js 中的 INSERT/UPDATE SQL 语句,提取:

数据源 3:代码读取扫描

扫描所有 SELECT 语句,提取被读取的表和字段。注意 SELECT * 需特殊处理(标记为"读取全部")。

已知局限

静态分析的盲区

定位:尽力而为的 lint 工具,不追求 100% 准确率,但高严重度发现应具有高置信度。

6. 与前置工作的关系

分析粒度输出状态
DB 数据流依赖图 表级 死表、只读表、只写表、孤立表 已完成
死表清理 + API 补全 表级 删 10 张死表、补 9 个 API 端点 已完成
字段级异常检测 字段级 死字段、多源写入、缺约束等 待实施

7. 决策记录

Decision Log — 2026-02-09

讨论结论:放弃全量字段血缘表,改为字段级异常检测报告。
理由:全量血缘表输出大、保鲜难、审查效率低。异常检测遵循与表级分析相同的"只报告偏离正常模式"原则,输出可执行行动项。
下一步:编写分析脚本,交叉比对 Schema + 代码 INSERT/UPDATE + 代码 SELECT,输出异常报告。