3、数据质量监控体系架构:整体架构设计、核心组件、数据流与元数据管理

聊到监控体系架构,我见过不少团队一上来就堆组件。Kafka、Flink、Spark、ES……全招呼上。结果呢?跑了一个月,告警淹没,没人看。说白了,架构不是炫技,是解决问题。

这一章,我把自己的经验拆开来讲。咱们从顶层设计往下走,看看一个能落地的特征质量监控体系,到底长什么样。

3.1 整体架构:分层解耦,各司其职

我个人习惯把监控体系分成四层。每层只干一件事,层与层之间通过标准接口通信。这样改一层,不影响其他层。

核心原则:采集与计算分离,计算与存储分离,存储与展示分离。

先看这张架构图,后面我逐层拆解。

特征数据质量监控体系架构总览 数据采集层 实时采集(Kafka Connect / Flume) 离线采集(Sqoop / DataX / 定时任务) 计算与校验层 实时校验(Flink / Spark Streaming) 离线校验(Spark SQL / Hive / 批处理) 规则引擎(Drools / 自研规则解析器) 存储与元数据层 指标存储(ClickHouse / Elasticsearch / InfluxDB) 元数据中心(MySQL / Neo4j / 自研元数据服务) 告警事件存储(Kafka + HDFS 冷备) 展示与告警层 监控大盘(Grafana / 自研 Dashboard) 告警通道(钉钉/企微/邮件/短信/PagerDuty) 数据流方向

3.2 核心组件详解

架构图看完了,咱们聊聊每个层里到底放了什么组件。我挑几个关键的说。

3.2.1 数据采集层

采集层是地基。地基不稳,上面全白搭。

  • 实时采集:特征数据通常从消息队列(Kafka)来。我们用 Kafka Connect 或者自研的 Flume Agent 去拉。注意一点:采集过程要做轻量级校验——比如字段是否为空、格式是否对。别把脏数据放进去。
  • 离线采集:Hive 表、HBase 表、或者业务库的 T+1 数据。用 DataX 或者 Sqoop 拉过来。我建议在采集时就打上时间戳和批次号,方便后面回溯。

我的经验:曾经有个项目,离线采集没做字段长度校验。结果某个上游表字段从 varchar(50) 改成了 varchar(500),采集程序没感知,直接截断了。那批特征数据全废了。后来我在采集层加了一个 schema 兼容性检查,再没出过类似问题。

3.2.2 计算与校验层

这是整个体系的大脑。规则怎么定义、怎么执行、怎么聚合,都在这一层。

我把校验规则分成三类:

规则类型 说明 典型场景
完整性规则 检查字段是否有空值、缺失值比例是否超标 用户 ID 不能为空,特征向量不能全零
准确性规则 检查值域范围、格式、业务逻辑一致性 年龄在 0-120 之间,手机号符合正则
一致性规则 跨表、跨时间窗口的字段值是否对齐 今日统计的订单数 = 昨日统计 + 今日增量

执行引擎这块,我推荐实时用 Flink,离线用 Spark SQL。为什么?Flink 的 event-time 语义处理乱序数据很舒服;Spark SQL 写复杂聚合逻辑更直接。

注意:规则不要写死。一定要做成配置化的。我曾经见过一个团队,每条规则都写一个 Flink 作业,最后维护了 200 多个作业。改个阈值都要重新编译打包。后来我改成规则引擎 + 动态配置中心,规则数量降到 30 个,维护成本降了 80%。

3.2.3 存储与元数据层

这一层容易被低估。很多人觉得“不就是存个数据吗?”其实不然。

我把它拆成三个部分:

  • 指标存储:校验结果指标(通过率、异常数、延迟等)。用 ClickHouse 或者 Elasticsearch。ClickHouse 适合聚合查询,ES 适合全文检索和日志查看。
  • 元数据中心:记录“特征从哪里来、经过哪些校验、依赖哪些表”。我用 Neo4j 存血缘关系,用 MySQL 存规则定义和配置。
  • 告警事件存储:每次触发的告警都要落盘。Kafka 先缓冲,再入 HDFS 冷备。方便事后复盘。

核心观点:没有元数据管理,监控体系就是瞎子。你不知道一个告警是特征本身的问题,还是上游表的问题,还是采集程序的问题。

3.2.4 展示与告警层

这一层是给人和系统看的。

展示我用 Grafana,原因很简单——插件多,社区活跃。但我会在 Grafana 之上封装一层自研的 Dashboard,把特征质量评分、趋势图、异常分布做成业务视角的视图。

告警通道我分了三级:

  1. P0(严重):核心特征不可用,立即电话 + 短信 + 钉钉@所有人。
  2. P1(警告):特征质量下降,钉钉群通知,15 分钟未响应自动升级。
  3. P2(通知):非核心特征异常,邮件日报汇总。

3.3 数据流:从采集到告警的完整链路

光有组件不行,得串起来。我画了一条典型的数据流,你看看。

数据流示例:实时特征质量监控 Kafka Topic Flink 校验作业 规则引擎 告警判定 告警通道 ClickHouse 指标存储 元数据中心 告警事件存储 说明:实时数据从 Kafka 进入 Flink,经过规则引擎校验后, 结果写入 ClickHouse 和元数据中心,同时触发告警判定。整个过程端到端延迟控制在 30 秒以内。

这条链路里,有一个容易被忽略的点:规则引擎的输入不只是当前数据,还要查历史状态。比如“连续 3 个窗口特征通过率低于 99%”这种规则,需要状态管理。Flink 的 KeyedState 或者外部存储(Redis)都可以。

3.4 元数据管理:让监控体系“有脑子”

元数据管理是我特别想强调的部分。很多团队的监控体系“没脑子”——只知道告警,不知道为什么告警。

我设计的元数据模型包含以下几类:

  • 特征元数据:特征名称、所属实体、数据类型、值域、采样率、生产时间。
  • 血缘元数据:特征依赖哪些上游表、经过哪些 ETL 任务、被哪些模型使用。
  • 规则元数据:规则定义、阈值、生效时间、所属特征、告警级别。
  • 运行元数据:每次校验的执行时间、耗时、通过率、异常明细。

举个例子:某个特征的“空值率”突然飙升到 20%。没有元数据,你只知道“特征 A 异常”。有了血缘元数据,你发现特征 A 依赖的上游表 B 昨天改了 schema,新增了一个非空字段。问题定位从小时级缩短到分钟级。

元数据的存储,我推荐用图数据库 + 关系型数据库的组合。图库(Neo4j)存血缘关系,方便做链路追踪。关系库(MySQL)存规则和配置,方便做事务和版本管理。

3.5 避坑指南

最后,分享几个我踩过的坑。

我曾经……把所有的校验逻辑都放在 Flink 作业里。结果规则一多,作业状态越来越大,Checkpoint 经常失败。后来我把规则引擎独立出来,Flink 只做数据清洗和简单过滤,复杂规则交给 Drools 引擎。状态小了,稳定性也上来了。

还有一个坑:告警风暴。某个特征挂了,下游十几个特征跟着一起告警。我加了一个“告警聚合”机制——同一个根因的告警,合并成一条,附带受影响的特征列表。不然值班同学真的会崩溃。

嗯,架构设计就聊到这里。记住一句话:好的架构是演化出来的,不是设计出来的。先跑通最小闭环,再逐步完善。