3、数据目录架构设计:分层架构设计、元数据存储选型、API网关设计

数据目录这东西,说白了就是个「元数据的搜索引擎」。你想想看,企业里几千张表、几万个字段,没有个清晰的架构,最后肯定变成一团乱麻。我见过太多项目,一开始图省事,把所有东西塞进一个单体应用,结果半年后就跑不动了。

今天咱们聊聊怎么搭一个能打的数据目录架构。我会把分层设计、存储选型、API网关这三个核心问题掰开揉碎讲清楚。

3.1 分层架构设计:别让所有逻辑挤在一起

我个人习惯把数据目录分成四层。每层各司其职,互不干扰。这样做的好处是——哪层出了问题,改哪层就行,不用动全局。

核心分层原则:上层依赖下层,下层不感知上层。数据流向是单向的,不允许跨层调用。

数据目录分层架构图 接入层(API Gateway) 统一入口 · 认证鉴权 · 流量控制 · 请求路由 服务层(核心业务逻辑) 元数据采集 · 血缘解析 · 质量评分 · 搜索索引 标签管理 · 数据分级 · 生命周期管理 存储层(元数据持久化) 关系型库 · 图数据库 · 搜索引擎 · 对象存储 基础设施层 监控告警 · 日志采集 · 容器编排 · 配置中心

3.1.1 接入层

这是用户和系统打交道的第一个关口。我建议用API网关统一收口,别让客户端直接摸到后端服务。为什么?因为安全、限流、版本管理这些脏活累活,网关能帮你挡掉一大半。

3.1.2 服务层

核心业务逻辑都在这层。我习惯把它拆成几个微服务:采集服务、血缘服务、搜索服务、标签服务。每个服务独立部署,独立扩容。举个例子,大促期间搜索流量暴涨,我只扩搜索服务就行,其他服务不动。

我的经验:服务拆分粒度别太细。我曾经把一个采集服务拆成8个微服务,结果运维成本翻了三倍。后来合并成3个,反而跑得更稳。适度拆分才是王道。

3.1.3 存储层

元数据存储没有银弹。不同类型的数据,得用不同的存储引擎。后面我会详细讲选型策略。

3.1.4 基础设施层

监控、日志、配置管理这些,看着不起眼,但少了它们,系统就是裸奔。我见过一个项目,元数据采集服务挂了三天没人发现,就是因为没配告警。

3.2 元数据存储选型:没有万能数据库

很多人问我:「能不能用一个数据库搞定所有元数据?」我的回答很直接——不能。元数据有结构化、半结构化、图关系、全文检索四种形态,你让MySQL去存血缘关系,那不是难为它吗?

元数据类型 推荐存储 适用场景 我踩过的坑
技术元数据(表结构、字段) MySQL / PostgreSQL 强一致性、事务支持、复杂查询 用MongoDB存表结构,结果关联查询慢到崩溃
血缘关系、数据链路 Neo4j / JanusGraph 多跳查询、路径分析、影响分析 用MySQL递归查血缘,10层就超时
全文搜索、快速检索 Elasticsearch 模糊搜索、分词匹配、聚合统计 用ES存全量元数据,结果数据一致性出了问题
操作日志、审计记录 HBase / ClickHouse 高吞吐写入、时间范围扫描 用MySQL存审计日志,单表过亿后查询成了噩梦

注意:不要试图用ES替代关系型数据库。ES的「近实时」特性意味着数据写入后有一小段不可见窗口。我曾经因为这个特性,导致元数据采集和展示之间出现了数据不一致,排查了整整两天。

3.2.1 混合存储架构怎么搭?

我推荐的做法是:用MySQL存核心元数据(保证强一致),用Neo4j存血缘关系(方便图遍历),用ES做搜索索引(提升查询性能)。三份数据之间通过定时任务同步,或者用消息队列做实时同步。

// 伪代码:元数据同步流程
public class MetadataSyncJob {
    
    public void sync() {
        // 1. 从MySQL读取变更的元数据
        List<Metadata> changes = metadataDao.getChangedRecords(lastSyncTime);
        
        // 2. 同步到ES
        for (Metadata meta : changes) {
            esClient.index("metadata_index", meta.getId(), meta);
        }
        
        // 3. 同步血缘关系到图数据库
        for (Lineage lineage : changes.getLineages()) {
            graphClient.createRelation(lineage.getSource(), 
                                       lineage.getTarget(), 
                                       lineage.getRelationType());
        }
        
        // 4. 记录同步状态
        syncLogDao.insert(new SyncLog(changes.size(), new Date()));
    }
}

3.3 API网关设计:统一出入口

API网关不是简单的反向代理。在企业级数据目录里,它承担着四个关键职责。

3.3.1 认证与鉴权

数据目录里存着企业的核心资产信息。谁可以看?谁可以改?这些权限必须由网关统一管控。我建议用OAuth2.0 + JWT的方案,网关校验,把用户信息透传给下游服务。

3.3.2 流量控制与限流

你想想看,如果某个业务方写了个死循环,疯狂调用元数据查询接口,后端扛得住吗?网关层必须做限流。我习惯用令牌桶算法,每个客户端分配一个速率,超了直接返回429。

3.3.3 请求路由与版本管理

微服务架构下,客户端不需要知道具体调用哪个服务。网关根据请求路径,自动路由到对应的服务实例。版本管理也交给网关——v1的请求走旧服务,v2的请求走新服务,平滑升级无感知。

3.3.4 协议转换与聚合

有时候前端需要一次查询多个维度的数据。如果让前端调三次接口,网络开销太大。网关可以做请求聚合——把三个请求合并成一个,后端并行查询,网关统一组装返回。

网关选型建议:

  • Kong:插件生态丰富,适合复杂策略场景
  • Spring Cloud Gateway:Java技术栈首选,与微服务无缝集成
  • APISIX:高性能,动态路由,适合大规模集群

3.4 架构设计中的避坑指南

做数据目录架构这几年,我踩过的坑能写一本书。挑几个典型的说说。

  • 别一开始就追求完美架构。我曾经花三个月设计了一套「完美」的分层架构,结果业务方等不及,自己用Excel管元数据了。先跑起来,再迭代优化。
  • 元数据采集别用同步方式。如果采集任务卡住了,整个API都跟着阻塞。用异步任务 + 消息队列,采集失败不影响查询。
  • ES索引别存太多字段。我见过有人把整个元数据对象都塞进ES,结果索引体积膨胀到几百GB,查询性能直线下降。只存需要搜索的字段就好。
  • 图数据库的写入性能是瓶颈。Neo4j的查询很快,但批量写入很慢。如果每天有几百万条血缘关系要写入,建议用批量导入工具,别一条一条insert。

一个小技巧:在网关层做请求日志,记录每个接口的调用次数、耗时、错误码。这些数据能帮你快速定位性能瓶颈。我每次排查线上问题,第一件事就是看网关日志。

数据目录的架构设计,说到底就是「分」与「合」的平衡。分层是为了解耦,存储选型是为了各取所长,网关是为了统一管控。这三件事做好了,数据目录就有了坚实的底座。至于上层怎么玩出花来,那是后面章节的事。