Skip to content

Latest commit

 

History

History
421 lines (311 loc) · 18.4 KB

File metadata and controls

421 lines (311 loc) · 18.4 KB

数据血缘分析系统 — 架构设计评估报告

评估日期: 2026-05-20 项目版本: v2.1.0 评估范围: 后端 Python 代码架构、核心模块设计、代码质量、可维护性


一、项目概览

维度 描述
项目定位 数据血缘分析系统,解析 Oracle/数仓 SQL 脚本,构建字段级血缘图谱,提供口径追溯与可视化
技术栈 FastAPI + Python 3.11+ + Pydantic v2 + D3.js v7
核心模块 解析层(Parser)、血缘追溯(Lineage)、口径追溯(Caliber)、指标血缘(Indicator)、数据仓库(Repository)
代码规模 ~40 个 Python 文件,核心代码约 8000+ 行

目录结构

DATA_RELY_ANALYSIS_SYS/
├── app/                          # 应用层 (FastAPI)
│   ├── api/                      # API 路由 (lineage, caliber, indicator, parse, system)
│   ├── services/                 # 业务服务 (parser, lineage, caliber, indicator, progress)
│   ├── models/                   # Pydantic 请求/响应模型
│   ├── utils/                    # 工具类 (cache_manager, path_utils, file_handler)
│   ├── config.py                 # 配置管理
│   ├── dependencies.py           # 依赖注入
│   └── repository.py             # 数据仓库层
├── core/                         # 核心引擎层
│   ├── models.py                 # 核心数据模型 (dataclass)
│   ├── indicator_models.py       # 指标血缘数据模型
│   ├── lineage_tracer.py         # 字段级 BFS 血缘追溯引擎
│   ├── caliber_tracer.py         # 口径 BFS 追溯引擎
│   ├── caliber_extractor.py      # 口径信息提取器
│   ├── caliber_exporter.py       # 口径文档导出器
│   ├── indicator_graph_builder.py # 指标血缘图构建器
│   ├── indicator_config_parser.py # 指标配置解析器
│   ├── indicator_sql_parser.py   # 指标 SQL 解析器
│   ├── table_parser.py           # Oracle 表结构解析器
│   ├── procedure_parser.py       # 存储过程解析器
│   ├── parser_protocol.py        # 解析器协议 (Protocol)
│   ├── parser_registry.py        # 解析器注册表
│   ├── base_sql_parser.py        # [已废弃] 解析器基类
│   ├── layer_detector.py         # 数据分层检测
│   ├── table_name_resolver.py    # 表名归一化/解析
│   ├── field_cleaner.py          # 字段名清洗
│   ├── data_validator.py         # 数据校验
│   ├── sql_boundary_detector.py  # SQL 边界检测
│   ├── adapters/                 # 解析器适配器 (Oracle tab/prc)
│   └── warehouse/                # 数仓脚本解析器 (DDL/DML/CTL)
├── static/                       # 前端静态文件 (D3.js + 原生 JS)
├── tests/                        # 测试用例
├── deprecated/                   # 已废弃代码
└── AI指标血缘分析_数仓&管架/      # 数据文件 (SQL 脚本)

二、架构设计优点

2.1 分层清晰,职责基本合理

项目采用了 应用层(app/) → 核心引擎层(core/) → 数据层 的三层架构:

  • app/api/: 路由层,只做参数校验和响应封装,不含业务逻辑
  • app/services/: 服务层,编排核心引擎,处理缓存/索引等横切关注点
  • core/: 纯算法引擎,不依赖 FastAPI,可独立测试

这种分层让核心算法(BFS 追溯、图构建)与应用框架解耦,是合理的架构选择。

2.2 Protocol + Registry 的解析器扩展机制

parser_protocol.py 使用 typing.Protocol(鸭子类型)定义解析器接口,parser_registry.py 提供按文件扩展名自动路由的注册表。新增数据源只需实现 Protocol 并 register(),无需修改已有代码,符合开闭原则。

2.3 BFS 追溯引擎设计扎实

lineage_tracer.pycaliber_tracer.py 的 BFS 引擎:

  • 预构建 4-5 个字典索引,将 O(N) 查询降为 O(1)
  • 循环依赖检测(含 schema 变体环路防护)
  • 层级兼容性过滤(防止 EAST 表直接追溯到 ODS 层)
  • TMP 表桥接策略(处理 ETL 临时表模式)
  • 同表字段变换折叠(看穿脱敏等中间步骤)

这些设计体现了对实际数据血缘场景的深入理解。

2.4 依赖注入与缓存管理

dependencies.py 使用 @lru_cache + FastAPI Depends 实现单例依赖注入,简洁有效。cache_manager.py 实现了 TTL + LRU + 倒排索引的内存缓存,满足毫秒级搜索需求。

2.5 数据模型使用 dataclass 而非 dict

models.pyindicator_models.py 使用 @dataclass 定义核心数据结构,类型安全且自带 @property 计算字段,比裸 dict 更可维护。


三、架构弊端与风险

3.1 【严重】ParserService 职责过重,God Class 倾向

问题: parser_service.py 承担了过多职责(约 920 行):

  1. 解析器初始化与注册
  2. 缓存加载(pickle/JSON 双格式)
  3. 文件解析(全量/增量/上传)
  4. 数据序列化(dataclass → dict)
  5. LineageTracer 构建(从缓存重建 dataclass 对象)
  6. 数据查询(get_table_list, search_tables)
  7. 缓存持久化(JSON + pickle 双写)

风险: 任何一项变更都可能影响其他功能;测试困难;难以理解全貌。

建议: 拆分为 4 个专职类:

新类 职责
ParseOrchestrator 编排解析流程(全量/增量/上传)
CacheStore 缓存读写(pickle/JSON 双格式)、缓存失效策略
DataSerializer dataclass ↔ dict 双向转换
LineageTracerFactory 从不同数据源构建 LineageTracer

3.2 【严重】dataclass ↔ dict 双轨数据模型,转换逻辑散落各处

问题: 项目同时使用两套数据表示:

  • dataclass: core/models.py 中的 TableInfo, ProcedureInfo, FieldMapping, CaliberInfo
  • dict: ParseResult.tables, ParseResult.procedures 等全部是 list[dict]

转换逻辑散落在:

  • parser_service.py_parse_tab_directory, _parse_proc_directory, _parse_single_tab, _parse_single_prc, get_lineage_tracer(反向 dict → dataclass)
  • caliber_service.py_build_tracer(dict → dataclass)
  • caliber_service.py_result_to_dict(dataclass → dict)

风险:

  • 字段增删时需同步修改多处序列化/反序列化代码,极易遗漏
  • dict 无类型检查,字段名拼写错误只能在运行时发现
  • get_lineage_tracer() 中的 dict → dataclass 重建逻辑(约 120 行)与 _build_tracer() 中的重建逻辑高度重复

建议:

  1. 统一使用 dataclass 作为内存中的唯一数据表示
  2. 为 dataclass 添加统一的 to_dict() / from_dict() 方法(或使用 dataclasses.asdict + 自定义反序列化)
  3. JSON/pickle 缓存层负责序列化,业务层只操作 dataclass

3.3 【严重】LineageService.query_lineage 方法过长(约 250 行)

问题: lineage_service.pyquery_lineage() 方法包含:

  • 缓存检查与刷新
  • 表名解析与 schema 校验
  • 字段级 BFS 追溯(上游/下游)
  • 旧版字段级追溯回退逻辑
  • 表级血缘补充
  • 字段映射过滤
  • 节点构建与去重
  • 结果组装

风险: 难以理解、测试和维护;任何分支的修改都可能引入回归。

建议: 拆分为独立方法:

def query_lineage(self, ...):
    self._check_and_refresh_cache()
    cached = self._try_cache(...)
    if cached:
        return cached

    if field:
        return self._query_field_lineage(...)
    return self._query_table_lineage(...)

def _query_field_lineage(self, ...):
    ...

def _query_table_lineage(self, ...):
    ...

3.4 【中等】CaliberInfo 数据类字段膨胀

问题: models.py 中的 CaliberInfo32 个字段,通过注释标注"批次A新增"、"批次B新增"、"批次C新增"不断追加:

@dataclass
class CaliberInfo:
    # 原始字段 (约 20 个)
    ...
    # 批次A新增:行号定位字段
    file_path: str = ""
    start_line: int = 0
    end_line: int = 0
    # 批次B新增:步骤级隔离条件字段
    step_isolated_where: list[SQLCondition] = field(default_factory=list)
    step_isolated_join: list[SQLCondition] = field(default_factory=list)
    # 批次C新增:CTE/函数/表达式字段
    cte_definitions: list[str] = field(default_factory=list)
    custom_functions: list[str] = field(default_factory=list)
    full_expression: str = ""
    is_custom_function_call: bool = False

风险: 违反单一职责原则;字段持续膨胀不可控;序列化/反序列化代码同步负担重。

建议: 使用组合模式拆分:

@dataclass
class SourceLocation:
    file_path: str = ""
    start_line: int = 0
    end_line: int = 0

@dataclass
class StepIsolation:
    isolated_where: list[SQLCondition] = field(default_factory=list)
    isolated_join: list[SQLCondition] = field(default_factory=list)

@dataclass
class ExpressionDetail:
    cte_definitions: list[str] = field(default_factory=list)
    custom_functions: list[str] = field(default_factory=list)
    full_expression: str = ""
    is_custom_function_call: bool = False

@dataclass
class CaliberInfo:
    # 核心字段 (约 15 个)
    ...
    source_location: SourceLocation = field(default_factory=SourceLocation)
    step_isolation: StepIsolation = field(default_factory=StepIsolation)
    expression_detail: ExpressionDetail = field(default_factory=ExpressionDetail)

3.5 【中等】LineageTracer 与 CaliberTracer 大量代码重复

问题: lineage_tracer.py(约 1360 行)和 caliber_tracer.py 共享大量相似逻辑:

  • BFS 遍历框架(队列、visited、深度控制)
  • 表名归一化(_normalize_table_name, _bare_table
  • 循环依赖检测
  • 层级兼容性过滤
  • TMP 表桥接
  • 模糊匹配策略
  • 从 BFS 树构建链路

风险: 修一个 bug 需要同步两处;行为不一致的风险高。

建议: 提取公共基类或混入类:

class BaseTracer:
    """BFS 追溯公共逻辑"""
    def _normalize_table_name(self, name: str) -> str: ...
    def _bare_table(self, table_name: str) -> str: ...
    def _is_layer_compatible(self, src: str, tgt: str) -> bool: ...
    def _try_tmp_bridge(self, ...) -> Optional[SourceRecord]: ...
    def _bfs_trace(self, ..., on_visit: Callable) -> dict: ...

class LineageTracer(BaseTracer):
    """字段级血缘追溯 — 关注数据流向"""
    ...

class CaliberTracer(BaseTracer):
    """口径追溯 — 关注加工条件"""
    ...

3.6 【中等】缓存策略存在一致性问题

问题:

  1. pickle 缓存无版本校验: parser_service.pyload_from_cache() 只检查 metadata.total_tables 是否非零,不校验数据结构版本。代码变更后旧 pickle 可能导致反序列化失败或数据错乱。
  2. DataRepository 与 ParseResult 双数据源: ParserService._current_resultDataRepository 持有相同数据的不同表示,更新时需同步两处。
  3. LineageService 的文件 mtime 检测: 通过检查 lineage_data.json 的修改时间来决定是否刷新缓存,但 ParserService 内部的 _current_result 更新不会触发文件修改,可能导致缓存与内存数据不一致。

建议:

  1. pickle 缓存增加 schema_version 校验,版本不匹配时自动失效
  2. 统一数据持有者为 DataRepository,ParserService 不再自行持有 _current_result
  3. 使用事件/回调机制替代文件 mtime 检测

3.7 【中等】API 响应格式不统一

问题:

  • 部分 API 使用 Pydantic 模型(如 LineageQueryResponse, CaliberQueryResponse
  • 部分 API 返回裸 dict(如 search_procedures, get_caliber_summary, export_caliber_document
  • 成功/失败格式不一致:有的用 {"success": True, "data": ...},有的用 {"data": ...}

风险: 前端需要针对不同端点写不同的响应解析逻辑;类型安全性差。

建议: 统一使用 Pydantic 响应模型,所有 API 返回类型声明完整。

3.8 【低】测试覆盖不足且分散

问题:

  • 项目根目录存在多个临时测试文件:_test_graph.py, _test_parser.py, _basic_test.py, _full_test.py, test_indicator_service.py, test_display_bug_fixes.py, comprehensive_test.py, e2e_verify.py
  • 正式测试目录 tests/ 下的测试文件较少,且多为 API 集成测试
  • 核心引擎(LineageTracer, CaliberTracer, IndicatorGraphBuilder)缺乏单元测试

风险: 重构时无法验证行为不变性;回归风险高。

建议:

  1. 清理根目录的临时测试文件,统一到 tests/
  2. 为核心引擎添加单元测试(BFS 追溯、图构建、表名归一化等)
  3. 添加 pytest-cov 配置,设定覆盖率目标

3.9 【低】CORS 配置过于宽松

问题: main.py 中 CORS 配置为:

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

allow_origins=["*"]allow_credentials=True 同时使用存在安全风险(浏览器实际上会拒绝这种组合的 credential 请求)。

建议: 生产环境应限制 allow_origins 为实际前端域名。

3.10 【低】deprecated 代码未清理

问题:

  • base_sql_parser.py 标注为 @deprecated,但仍被保留
  • deprecated/ 目录下的 api_server.py 未删除
  • 根目录的 _excel_full_output.txt 等临时文件未清理

建议: 完成迁移后删除废弃代码和临时文件,减少认知负担。


四、性能相关评估

4.1 当前性能优化措施(已实施)

优化项 实现方式 效果
内存倒排索引 CacheManager.build_index() 搜索 <50ms
BFS 预建索引 LineageTracer._build_index() 字段追溯 O(1) 查找
pickle 缓存 ParserService.load_from_cache() 启动加速 10x
LRU + TTL 缓存 CacheManager 热点查询毫秒级响应
图预处理 TableLineageTracer.build_graph() 表级追溯免遍历

4.2 潜在性能风险

风险点 描述 影响
全量内存加载 所有数据(表/过程/血缘/映射/口径)全部加载到内存 数据量增长后可能 OOM
BFS 无结果限制 LineageTracer 的 BFS 无节点数上限 菱形依赖图可能产生指数级链路
重复 dataclass 重建 每次构建 LineageTracer/CaliberTracer 都从 dict 重建 启动慢,内存浪费
同步阻塞 所有 API 使用 def 而非 async def,BFS 追溯在主线程执行 高并发时阻塞事件循环

4.3 优化建议

  1. BFS 结果限制: 为 LineageTracer 添加 max_nodes 参数(类似 IndicatorGraphBuilder 的 max_depth),防止结果爆炸
  2. 异步化重查询: 将 BFS 追溯放入线程池(asyncio.run_in_executor),避免阻塞事件循环
  3. 增量索引更新: 数据更新时只重建受影响的索引部分,而非全量重建
  4. 内存分页: 大数据量场景下考虑分页加载或使用 SQLite 替代全量内存

五、代码质量评估

5.1 优点

  • 类型标注覆盖率高,公共函数基本都有返回值类型
  • 日志记录充分,关键操作都有 logger 输出
  • 异常处理基本到位,无裸 except
  • 使用 from __future__ import annotations 统一延迟注解求值
  • 文件头 docstring 清晰描述模块职责

5.2 待改进

问题 示例 建议
方法过长 LineageService.query_lineage (~250行), ParserService.get_lineage_tracer (~140行) 拆分为子方法
重复代码 dict ↔ dataclass 转换逻辑在多处重复 提取统一的序列化层
魔法字符串 "oracle", "upstream", "INSERT_SELECT" 等硬编码 使用枚举或常量
内部类定义在闭包中 LineageTracer._bfs_trace 内定义 _is_layer_compatible 提取为实例方法
导入在函数内部 from core.parser_protocol import ParseOutput 在方法内导入 移到文件顶部或使用 lazy import 模块

六、优化建议优先级排序

优先级 建议 预期收益 影响范围
P0 统一数据模型(消除 dict/dataclass 双轨) 消除序列化 bug 温床,降低维护成本 core/, app/services/
P0 拆分 ParserService(God Class) 提高可测试性和可维护性 app/services/
P1 提取 LineageTracer/CaliberTracer 公共基类 消除重复代码,统一行为 core/
P1 拆分 LineageService.query_lineage 提高可读性和可测试性 app/services/
P1 CaliberInfo 字段组合拆分 防止字段持续膨胀 core/models.py
P2 统一 API 响应格式 前端开发体验 app/api/
P2 BFS 结果限制 + 异步化 性能与稳定性 core/, app/services/
P2 缓存一致性修复 数据正确性 app/services/, app/utils/
P3 测试覆盖增强 回归防护 tests/
P3 清理废弃代码 代码整洁度 全局
P3 CORS 安全加固 安全性 app/main.py

七、总结

整体评价: 中等偏上 (7/10)

核心优势: BFS 追溯引擎设计扎实,对实际数据血缘场景(TMP 表桥接、同表变换折叠、层级兼容性过滤、schema 变体去重)有深入理解;Protocol + Registry 的扩展机制设计合理;分层架构方向正确。

核心问题: 数据模型双轨制(dict/dataclass)是最大的技术债,导致了大量重复的序列化代码和潜在的一致性风险;ParserService 的 God Class 倾向使系统脆弱;两个 Tracer 的代码重复增加了维护负担。

建议路线: 优先解决数据模型统一问题(P0),这是其他优化的基础;然后拆分 God Class 和提取公共基类(P1);最后处理 API 规范化和性能优化(P2-P3)。


本报告基于 2026-05-20 的代码快照生成,部分问题可能在后续版本中已修复。