3、数据目录的架构设计:采集层、存储层、服务层、应用层的分层设计

聊到数据目录的架构,很多人第一反应就是「搞个元数据表,再搭个搜索界面」。嗯,我刚开始也是这么想的。结果呢?第一个版本上线后,采集器把生产库拖挂了,存储层查个血缘关系要等半分钟,服务层一并发就 OOM……

后来我花了整整两个迭代,把架构拆成了四层:采集层、存储层、服务层、应用层。每一层各管各的事,互不干扰。今天我就把这套分层设计的思路,掰开了讲给你听。

核心原则:上层依赖下层接口,下层对上层完全透明。你换掉存储层的数据库,应用层一行代码都不用改。

3.1 整体分层架构概览

先看一张图,把四层的关系理清楚。我习惯用这张图跟团队对齐认知,每次新同学加入,第一件事就是看这个。

数据目录四层架构图 数据目录四层架构 应用层 数据地图 · 血缘可视化 · 数据检索 · 质量看板 · 权限申请 服务层 元数据API · 搜索服务 · 血缘解析引擎 · 标签服务 · 缓存 存储层 关系库(MySQL) · 图库(Neo4j) · 搜索引擎(Elasticsearch) · 缓存(Redis) 采集层 Hive MetaStore · Kafka Schema · MySQL DDL · 调度系统 · 埋点日志 数据源 数据源 数据源 说明:箭头方向代表数据流向,下层为上层提供数据支撑

说白了,这四层就是一条流水线:采集层负责「挖矿」,从各种数据源把元数据捞上来;存储层负责「仓库」,把挖来的东西分门别类放好;服务层负责「加工」,提供查询、搜索、血缘分析这些能力;应用层负责「展示」,让用户看得见、用得上。

3.2 采集层:元数据的「搬运工」

采集层是整个数据目录的起点。你想想看,如果采集层没做好,后面三层再牛也是白搭——巧妇难为无米之炊嘛。

我在项目中遇到过最典型的问题:采集任务把 Hive MetaStore 的连接池打满了,导致分析师跑不了 SQL。从那以后,我对采集层定了一条铁律:不能影响源系统。

3.2.1 采集方式

采集方式主要有三种,我列个表对比一下:

采集方式 原理 优点 缺点 适用场景
直连采集 直接连接源数据库读取元数据 实时性好,实现简单 对源库有性能压力 MySQL、PostgreSQL 等关系库
日志采集 监听 DDL 变更日志(如 Binlog) 对源库无侵入 需要额外组件,延迟略高 核心生产库、高并发场景
API 采集 调用平台提供的元数据 API 标准化,安全可控 受限于 API 能力 Hive MetaStore、Kafka Schema Registry

我的建议:核心业务库用日志采集,非核心库用直连采集。Hive 和 Kafka 这类平台,优先用它们的原生 API。

3.2.2 采集策略

采集策略我一般分三种:

  • 全量采集:每天凌晨跑一次,把所有的表、字段、分区信息全量拉一遍。适合数据量不大(万级表以下)的场景。
  • 增量采集:只拉取变更的部分。我习惯用版本号或时间戳做增量标记,比如只采集 last_ddl_time 大于上次采集时间的表。
  • 事件驱动采集:监听 DDL 事件,有变更立刻触发采集。实时性最高,但实现复杂度也最高。

我曾经踩过一个坑:全量采集和增量采集同时跑,结果数据冲突了。后来我加了一个采集状态表,每次采集前先检查当前是否有任务在执行,有的话就排队等待。

3.3 存储层:元数据的「仓库」

存储层要解决的核心问题是:元数据该放哪儿?不同的元数据,存储方式完全不同。

3.3.1 存储选型

我现在的标准配置是这样的:

存储组件 存储内容 选型理由
MySQL 表结构、字段信息、分区信息、采集记录 关系型数据,事务支持好,查询灵活
Neo4j 血缘关系、数据链路、依赖图谱 图查询效率高,血缘分析场景天然适合
Elasticsearch 全文索引、搜索数据 分词搜索、模糊匹配、聚合分析
Redis 热点数据、缓存、会话 高并发读,毫秒级响应

注意:不要把所有元数据都塞进一个库里。我曾经见过有人把血缘关系存 MySQL,结果一个五层深度的血缘查询跑了 30 秒。换成 Neo4j 后,同样的查询只要 200 毫秒。

3.3.2 数据模型设计

存储层的数据模型,我习惯用「三张核心表 + 一张图」来概括:

  • 表信息表(table_info):存储表名、库名、类型、创建时间、最后 DDL 时间等。
  • 字段信息表(column_info):存储字段名、类型、注释、是否主键、默认值等。
  • 分区信息表(partition_info):存储分区字段、分区值、记录数、大小等。
  • 血缘关系图(lineage_graph):用 Neo4j 的节点和边表示表与表、字段与字段之间的依赖关系。

嗯,这里要注意:字段信息表的数据量可能非常大。一个千表级别的数仓,字段数轻松破十万。所以字段信息表一定要做好索引,我一般会在 table_id 和 column_name 上建联合索引。

3.4 服务层:元数据的「加工厂」

服务层是四层中最容易被低估的一层。很多人觉得服务层就是「封装一下数据库查询」,其实远不止如此。

3.4.1 核心服务

我一般把服务层拆成这几个模块:

  • 元数据查询服务:提供 RESTful API,支持按表名、字段名、库名等维度查询。接口设计要支持分页、排序、过滤。
  • 搜索服务:基于 Elasticsearch 实现全文搜索。支持中文分词、拼音搜索、模糊匹配。
  • 血缘解析引擎:解析 SQL 语句,提取表级和字段级的血缘关系。这个模块最复杂,我后面会单独讲。
  • 标签服务:支持给表、字段打标签,比如「PII数据」、「核心表」、「已废弃」。
  • 缓存服务:用 Redis 缓存热点查询结果,减少数据库压力。

3.4.2 血缘解析引擎的设计

血缘解析引擎,说白了就是「读懂 SQL,画出依赖图」。我刚开始做的时候,以为写个正则匹配就行了。结果呢?遇到 CTE、子查询、JOIN 各种复杂场景,正则根本搞不定。

后来我换成了基于语法解析的方案:

// 伪代码示例:血缘解析流程
1. 输入:SQL 语句
2. 词法分析:拆分成  流
3. 语法分析:构建抽象语法树(AST)
4. 遍历 AST:提取 INSERT、SELECT、FROM、JOIN 等关键节点
5. 构建血缘关系:源表 → 目标表,源字段 → 目标字段
6. 输出:血缘关系图(节点 + 边)

避坑指南:我曾经用了一个开源的 SQL 解析器,结果它不支持 Hive 的 LATERAL VIEW。后来我改用了 Calcite 的 SQL 解析器,兼容性好了很多。选型时一定要确认解析器支持你的 SQL 方言。

3.5 应用层:元数据的「展示台」

应用层是用户直接接触的部分。你想想看,如果应用层做得不好用,用户根本不会关心你底层用了什么技术。

3.5.1 核心功能模块

我一般会提供这几个功能:

  • 数据地图:以可视化的方式展示所有数据表,支持按库、按主题域、按标签浏览。
  • 血缘可视化:用 DAG 图展示表与表之间的血缘关系,支持点击节点查看详情。
  • 数据检索:搜索框支持模糊搜索、拼音搜索、高级搜索(按字段类型、按标签等)。
  • 质量看板:展示数据质量评分、异常记录、校验结果。
  • 权限申请:用户可以直接在页面上申请表的读/写权限,审批流程自动流转。

3.5.2 用户体验设计

应用层的设计,我有一条原则:让用户三步之内找到想要的数据。

具体怎么做?

  • 搜索框放在页面最显眼的位置,支持回车搜索。
  • 搜索结果按相关性排序,同时展示表名、库名、最近更新时间。
  • 表详情页把「血缘关系」和「字段信息」放在最前面,因为这是用户最关心的。
  • 页面加载时间控制在 2 秒以内,超过 2 秒就展示骨架屏。

经验之谈:应用层不要堆功能。我见过一个数据目录,功能列表拉下来有 30 多个入口,用户根本不知道点哪个。我的做法是:先上线核心功能(搜索 + 血缘 + 详情),其他功能按用户反馈逐步添加。

3.6 分层设计的核心收益

最后总结一下,为什么我坚持用四层架构?

收益 说明
解耦 每一层可以独立开发、独立部署、独立扩缩容
复用 服务层的 API 可以同时被多个应用层使用(Web 端、移动端、API 网关)
可替换 换存储组件不影响上层,换采集方式不影响下层
可测试 每一层都可以单独写单元测试和集成测试

嗯,这就是我这些年做数据目录架构设计的心得。分层设计看起来简单,但真正落地的时候,每一层都有不少坑。希望今天的分享能帮你少走一些弯路。