3、数据目录的架构设计:采集层、存储层、服务层、应用层的分层设计
聊到数据目录的架构,很多人第一反应就是「搞个元数据表,再搭个搜索界面」。嗯,我刚开始也是这么想的。结果呢?第一个版本上线后,采集器把生产库拖挂了,存储层查个血缘关系要等半分钟,服务层一并发就 OOM……
后来我花了整整两个迭代,把架构拆成了四层:采集层、存储层、服务层、应用层。每一层各管各的事,互不干扰。今天我就把这套分层设计的思路,掰开了讲给你听。
核心原则:上层依赖下层接口,下层对上层完全透明。你换掉存储层的数据库,应用层一行代码都不用改。
3.1 整体分层架构概览
先看一张图,把四层的关系理清楚。我习惯用这张图跟团队对齐认知,每次新同学加入,第一件事就是看这个。
说白了,这四层就是一条流水线:采集层负责「挖矿」,从各种数据源把元数据捞上来;存储层负责「仓库」,把挖来的东西分门别类放好;服务层负责「加工」,提供查询、搜索、血缘分析这些能力;应用层负责「展示」,让用户看得见、用得上。
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 网关) |
| 可替换 | 换存储组件不影响上层,换采集方式不影响下层 |
| 可测试 | 每一层都可以单独写单元测试和集成测试 |
嗯,这就是我这些年做数据目录架构设计的心得。分层设计看起来简单,但真正落地的时候,每一层都有不少坑。希望今天的分享能帮你少走一些弯路。