做了几年数仓,数据质量这一块踩过的坑比写过的SQL还多。前几篇聊了质量规则怎么定、怎么配置校验任务,这一篇专门说“任务监控”——也就是怎么盯住这些质量任务本身,别让它们自己先垮掉。很多时候数据出问题,不是因为业务表变了,而是因为我们那个质量检查任务没跑、卡死、误报,或者延迟到凌晨六点才跑完,下游全等着。所以这篇把我在实际项目里用过的监控思路、指标设计、告警策略和故障排查记录都摊开来聊,适合正在搭质量监控体系、或者已经被线上告警搞到麻木的数仓开发者看看。
1. 先想清楚:数据质量任务监控到底在监控什么
1.1 质量任务自己也会出问题
我们把质量任务当成一套独立的作业来对待,它和ETL任务一样,有自己的依赖、运行时长、失败重试、资源占用。很多团队一开始只关注“质量规则是否通过”,却忽略了“执行质量任务的过程是否健康”。我碰到过最典型的情况:某个表的核心主键唯一性校验每天凌晨两点跑,某天因为该表的数据量暴增,这个校验跑了三个小时还没结束,结果它下游的报表ETL一直在等它,整条链路从凌晨堵到早上七点。说白了,质量任务不再是附加的小检查,它本身已经成了链路里的关键节点,不监控它,全局就得跟着遭殃。
所以监控的第一层,是把质量任务当作“一等公民”来管理:它的调度是否准时,运行是否超时,失败了有没有自动重试,重试了是否还是失败,资源队列里有没有被其他大查询挤死。这些都属于任务运行态的监控,而不是数据质量的业务判断。
1.2 监控的层次:任务层、结果层、影响层
我在落地监控体系的时候,习惯把监控分成三个层次:
- 任务层:关注质量任务本身的调度状态、执行时长、重试次数、资源使用、日志异常。比如“每日表行数校验”这个任务,今天是不是按时触发了,执行了多久,有没有OOM,日志里有没有出现JDBC连接超时。
- 结果层:关注质量任务的校验结果本身,包括规则通过率、具体校验项的失败明细、数据波动幅度、与历史基线的偏差。例如主键唯一性规则失败了,具体有多少重复主键,分布在哪一批数据里。
- 影响层:关注质量任务失败或延迟给下游带来的影响。这张表的质量检查没过,下游哪些应用依赖它,产生了什么级别的业务影响,是需要立即阻断还是可以降级放行。
三层分开看,才能做到“快速定位是调度问题还是数据问题”。只盯结果层,你会经常误以为业务数据坏了,结果查半天发现是质量任务自己没跑起来。只盯任务层,又看不到数据变化趋势。三个层次对应三种不同的告警来源和处理策略,后面我会具体讲。
2. 核心监控指标的选取与阈值设计
有了层次,还要有可量化的指标。我在这套体系里重点盯了下面几类指标,每个都有阈值设定逻辑和踩出来的教训。
2.1 任务运行态指标:状态、时长、重试、资源
最基础的当然是任务状态。成功、失败、取消、暂停,这些调度系统本身就有。但光看状态不够,我加上了两个指标:运行时长和重试次数。
运行时长非常容易出问题。质量任务通常比普通ETL要轻量,我给它们的基线时间定得很保守。做法是先收集两周正常执行耗时,取P50和P95,然后设定阈值为P95的1.5倍。比如某个字段空值率检查正常执行是5分钟,P95是8分钟,那超过12分钟就触发“慢任务告警”。为什么用P95而不是平均值?因为平均值容易被极端值带偏,P95更能代表大多数正常情况下的上界。另外延迟告警不能只看一次,连续三次超过阈值才报警,避免偶发的资源抖动造成噪音。
重试次数也要单独监控。调度系统一般支持失败自动重试,但重试不是越多越好。我见过一个任务重试了10次,每次都卡在同一个SQL逻辑上,白白消耗资源。所以对质量任务,我统一设置最多重试2次,并且把重试的间隔拉长到5分钟以上。如果重试后仍然失败,就立刻进人工处理队列。同时记录每一次重试的具体报错信息,如果连续多次重试的报错都一样,说明是确定性错误,重试再多也白搭。
资源指标容易被忽略。质量任务虽然小,但如果都挤在同一个资源组里,也可能互相影响。我一般关注任务执行时的内存用量、CPU时间片、是否出现GC时间过长。曾经有个空值校验任务,因为表数据量膨胀,读取时内存不够,不断触发Spill,执行时间从2分钟涨到40分钟。后来我们给质量任务设置了独立的资源队列,并限制它的扫描量级,用采样代替全表扫描,问题才解决。
2.2 数据结果指标:产出时间、数据量、空值率、唯一性
任务跑完不等于数据可信,结果层的指标是另一套逻辑。这里我常用的四类:
第一位是数据产出时间(Data Freshness)。对ODS层来说,就是当日分区是否在业务要求的时间点前就绪;对DWS/ADS层来说,就是每日宽表是否按时可查。我会为每个关键表设定一个SLA时间,例如“每日订单事实表必须在早上8点前产出,且质量校验通过”。一旦超时,产生“产出延迟告警”,级别为P1。
第二位是数据量波动。规则很简单:当天的记录数对比过去30天的均值,波动超过设定百分比就报警。但阈值不能是一个固定值,需要按表类型区分。大表例如订单明细,日增量相对稳定,我设定阈值在±20%;小表例如配置表,可能一天只有几十行,波动100%都很正常,我设定阈值在±200%。关键是要把绝对量和相对量结合起来,看相对变化率的同时,也看绝对值是否低于一个绝对下限(比如订单表绝对不能为0)。
第三位是空值率。我们在每个关键字段上配置了非空校验,但更合理的方式是监控“空值率”的突变。比如用户ID字段,平时空值率不到0.01%,某天突然变成5%,即使没碰非空规则,也说明上游数据出了问题。空值率的阈值是根据历史分位数算出来的,我用的是“超过过去7天均值的3倍标准差”触发告警,实践证明这比固定值更灵敏。
第四位是主键唯一性。这是硬规则,不能用统计兜底。它监控的是表内主键是否重复。每张关键表我都要求有唯一性校验任务,并且执行频率根据表的更新频率来定。日更表每天跑一次,小时级表每小时跑一次。唯一性校验每次跑完都会记录重复条数和重复Key样例,方便后续追踪。
2.3 阈值怎么定,从基线到动态
阈值设计是整个监控里最重要的细节,也是我最想强调的一点。很多人一开始就拍脑袋定“空值率不能超过1%”“数据量波动不能超过10%”,结果要么误报多到没人看,要么真出问题时阈值被绕过。
我的经验分三步:
- 第一步,先跑2周基线。把质量任务部署完,只记录结果不告警。期间收集每个指标的正常分布,包括每张表的行数均值、波动幅度、运行耗时分位数。
- 第二步,基于基线设定初始阈值。用百分位数+标准差组合,比如运行时长阈值是P95×1.5,数据量下界是均值的50%,上界是均值的200%,空值率用均值+3σ。
- 第三步,定期动态调整。每半个月自动重新计算一次基线,让阈值跟随数据本身的节奏变化。比如大促期间数据量翻倍,如果阈值是固定的,系统天天报警,动态基线会自然把阈值拉高。当然,调整要有上限,不能让异常被平滑掉。
我在实际项目中还加了一套“阈值红黄线”机制:黄线是“需要关注”,只记录和发低级别通知;红线是“必须处理”,直接P1告警并触发下游阻断。比如空值率超过均值+3σ是黄线,超过5倍σ就是红线。这样一下子把误报和真实故障分开了。
3. 监控功能的落地实现:从规则配置到告警闭环
指标设计再完,落地才是硬功夫。这一节讲讲在一个已有数仓环境里,如何一步步把监控功能做出来,并且跟调度系统打通成闭环。
3.1 在调度系统中挂质量任务
绝大多数数仓团队都有统一的调度系统,不管是自研的还是开源的。质量任务不应该脱离调度体系自己跑,否则状态信息没法汇聚,也没有统一依赖。我的做法是:
- 把质量任务包装成与ETL任务同构的“检查任务”,在调度DAG里挂在目标表对应的ETL任务之后。
- 质量任务通过参数传入要检查的表名、分区日期、规则集ID,这样同一个质量任务模板可以被多张表复用。
- 用调度系统的依赖机制让下游任务依赖质量任务的成功状态。如果质量任务失败,下游自动等待或者被阻断,而不是让脏数据继续蔓延。
需要注意的是,质量任务失败不等于数据不可用。有些表只是行数波动,并不影响核心指标计算,不建议一失败就阻断下游。我更倾向于给质量任务设“阻断级别”:比如主键唯一性、关键字段非空检查是阻断级;行数波动、运行时长延迟是警告级,只告警不阻断。阻断级别在配置里维护,并且随着数据仓库成熟度逐步收敛。
3.2 告警分级与通知策略
告警做不好,等于没有监控。我踩过最典型的坑就是“所有质量规则失败都发同一封邮件”,结果一天几百封,干净的人直接把这套系统拉黑。后来我把告警分成P1/P2/P3三级,并设置不同的通知方式。
级别定义大概这样:
| 级别 | 含义 | 触发场景 | 通知方式 | 响应要求 |
|---|---|---|---|---|
| P1 | 严重数据事故 | 主键唯一性破坏、核心表产出超时、数据量为0 | 电话/短信+邮件 | 15分钟内响应 |
| P2 | 数据异常需核查 | 空值率突增、数据量波动超阈、质量任务连续失败2次 | 邮件+即时消息 | 2小时内响应 |
| P3 | 提示性信息 | 任务运行时长超过P95、单次重试成功、规则通过但有波动 | 邮件聚合日报 | 当日处理 |
针对P1告警,我还在团队内部定义了一个响应流程:收到告警后先确认是数据源问题还是校验任务问题,然后在指定群组里同步排查结论,修复后需要补充说明原因并确认影响范围。这套流程看起来简单,但真正让告警从“信息”变成了“行动”。
另外,为了避免告警被忽略,我做了“告警摘要”和“降噪”机制。同一张表同一类规则在一个周期内反复告警时,只发一次P1,后续合并成一条P2跟进。凌晨的告警如果是非阻断性的,统一延迟到早上8点合并发送,避免半夜把人吵醒却处理不了。
3.3 数据质量报告与快照留存
监控不光是实时告警,还要有周期性报告。我的系统里每天生成一份“数据质量日报”,内容包括:
- 当日所有质量任务执行情况:总数、成功数、失败数、重试数、平均耗时。
- 各层级表的质量评分:ODS/DWD/DWS/ADS分别统计规则通过率。
- 当日告警清单:包括触发时间、规则、异常指标、影响分区、处理状态。
日报的作用不是告警,而是让团队看到趋势。通过连续几周的报告,你可以很清楚地发现哪些规则持续反复失败,哪些表成了“问题户”,然后决定是优化数据源、修改规则还是调整阈值。
快照留存也必不可少。每次质量任务执行后,我把校验结果快照(规则ID、表名、分区、指标值、执行时间)写进一张独立的元数据表。这表有两个用途:一是复现历史问题,比如业务方问“为什么上周三指标异常”,我能直接翻出当时的校验记录;二是用于计算下一轮的动态基线,历史快照就是最新的基线样本。我建议快照至少保留两年,存储在专门的质量元数据库里,不要混在业务库中。
4. 真实故障场景处置记录
理论说多了,不如看几个真实场景。这里放三个我亲手处理过的故障,每个都有起因、排查过程和最终修复策略。这些场景能帮你理解监控指标之间如何联动,以及为什么有些看起来很“正常”的情况也需要警惕。
4.1 场景一:质量任务把下游全堵死
现象是某天早上整个数仓下游报表数据都没更新,查调度系统发现所有下游任务都在等待一个“订单主键唯一性校验”任务,而这个校验任务已经跑了6个小时还没结束。
第一反应是看这个任务为什么这么慢。日志显示它一直在扫描一个分区为当天的订单表,但那张表的数据量从之前的日均400万行突然涨到了1800万行,原来5分钟跑完的扫描直接变成小时级。更深层的原因是,上游某业务系统一次性回补了历史数据,导致当天分区数据量异常膨胀,而我们的唯一性校验SQL是全表distinct,没有基于分区做剪枝,扫描成本被放大了数倍。
处理过程分两步。第一步,先中止这个校验任务,让临时修改后的校验SQL只检查增量分区,并放到单独的队列跑,等它完成后手动放行下游。第二步,从根治角度给校验任务加上水位线和预检查:任务启动前先查目标表分区行数,如果行数超过历史均值3倍,自动触发“数据量异常预警”,不再继续跑全量校验,而是先横向切片抽样校验。这样即使上游数据暴涨,质量任务也不会成为链路的瓶颈。
这个场景告诉我们:质量任务的执行策略也要自适应。监控不能只盯“任务是否成功”,还要监控“任务运行过程中的数据环境是否发生剧烈变化”。我把这个思路整理成了“前置条件检查”,在质量任务真正执行SQL前,先运行一组轻量查询评估当前分区的数据量、文件数、分区大小,任何一个指标偏离基线,就先中断并提醒人工介入。
4.2 场景二:数据量突变为0却没有告警
某天业务方反馈,某报表一条数据都没有。我查了数仓对应表,发现当日分区确实为空。但奇怪的是,我们的监控系统并没有发出任何“数据量为0”的告警。
排查后发现原因很蠢:这张表是新接入的表,当时配置监控时只设置了“行数波动”规则,并且阈值是“与过去30天均值比较超过±20%”。新表刚上线,过去30天里大部分分区是空的,所以当天的0行数据反而在“正常波动范围内”,规则被认为通过了。
这个案例是个典型的阈值设计缺陷:第一,对于没有充足历史基线的表,不能直接用相对波动率,必须叠加绝对阈值。后来我在所有表的监控上强制加了一条:“当日行数为0”属于P1级硬告警,不管历史均值是多少。第二,新表上线时必须选择“冷启动模式”,前两周只做绝对指标的检查,比如行数是否大于0、主键是否有重复、关键字段空值率是否低于1%,不做相对波动检查。等积累了充足基线再自动切换到动态阈值模式。
另外,我还学到了一个技巧:把“0行”当成特殊值处理。在很多统计逻辑里,数据量从1万变到0,波动率是100%,会触发告警;但如果从0变成1,波动率无穷大,很多系统算不出来就会忽略。所以我在计算波动率之前,先做一个空值判断,如果基期或当期为0,直接走特殊逻辑,不再套用百分比公式。
4.3 场景三:告警轰炸导致真故障被淹没
这个问题出现在监控体系上线后第三周。当时连续好多天,每天凌晨都有几十条“空值率超过阈值”的告警,排查下来发现都是同一张日志表的上游数据在某些时段确实会大面积出现空值,但下游使用方根本不关心那个字段。这种告警多了以后,团队看到P2告警基本不看,结果某天真正核心的金额字段空值率超过90%时,P1告警虽然发了,却因为大家都习惯忽略消息,延迟了40分钟才发现。
解决告警轰炸必须从规则治理入手。我做了三件事:
- 对现有规则全面打标,区分“强规则”(直接影响核心指标)和“弱规则”(仅提示)。弱规则的告警从即时发送改为日汇总发送,并且一周内如果同一弱规则触发了多次,只保留第一次,后续自动折叠。
- 增加“告警沉默”机制。对于同一个表/规则/异常类型,如果在过去N小时内已经发送过同类告警,并且指标没有进一步恶化(比如空值率没有翻倍),就不再重复发送,只是更新告警状态。这个机制帮助我们把每天的告警量从几十条降到了三五条。
- 建立“规则下线和整改”流程。如果一个规则连续两周触发率超过50%,说明它已经失去了区分异常的能力,要么是阈值太低,要么是该字段本身质量就差,不再适合作为监控点。必须申请重新评估和调整阈值,而不是任它继续制造噪音。
告警系统的目标不是“越多越好”,而是“该响的时候一定能响”。降噪这件事,做得好的人不多,但它是监控体系能否持久的关键。我一直给自己定了一个标准:每个人每天处理的数据质量告警不应该超过3条,超过就说明规则或者阈值有问题。
5. 质量监控体系持续迭代的几点经验
最后一部分聊一些方向性经验,不涉及具体功能,说说怎么让这套监控越用越顺手。
5.1 从“监控任务”到“监控数据资产”
早期我们做的是任务级监控,把注意力放在“这个质量任务有没有跑成功”。但数据仓库的数据质量监控到最后一定会转向数据资产级监控——也就是站在数据表、指标的视角来问:“这张核心表今天能不能用?它的数据是否可信?影响哪些下游报表?”
任务级监控只看执行结果,而资产级监控要看数据血缘。比如一张DWD表质量规则失败,资产级监控会立刻展示出它影响的下游DWS表有哪些,ADS里哪些指标被阻断,业务方开放了哪些接口。这样当告警发生时,响应者能第一时间评估影响面,而不是逐个任务去翻依赖。
落地这个并不需要很复杂。我们主要是在质量元数据表里增加血缘信息,因为调度系统本身就有任务依赖关系,把表名、任务名、下游消费方关联起来就能画出血缘小图。每次质量任务失败时,自动把受影响的下游表和指标打在告警内容里。这个小小的改动对应急响应效率提升非常明显。
5.2 减少无效告警的实操技巧
再看一些很实用的小技巧,都是我在长期运营中总结的:
- 按数据层级设置不同严格度。ODS层的质量规则重点是“完整性”和“数据量波动”,DWD层重点是“一致性”和“主键唯一性”,DWS/ADS层重点是“指标的合理性”和“可解释性”。不同层级的规则严格度不一样,告警严重度也不同。
- 对周期任务单独设置节假日规则。很多业务表在周末/节假日数据量骤降,用同一套阈值必然误报。我在调度日历里配置了“工作日/非工作日”两套基线,遇到节假日自动切换,极大降低了假告警。
- 质量任务尽量“轻量化”。不是每个规则都要全表扫描,能抽样就抽样,能用统计结果代替明细扫描就尽量代替。我见过一个质量任务因为每次都要full scan十几亿行,导致集群压力暴增,反而拖垮了正常ETL。后来调整为基于TABLESAMPLE的抽样校验,对大多数规则,1%抽样已经足够发现问题。
- 监控自己的监控。质量元数据库本身的健康也要盯,比如快照表是否有数据回写失败、日报任务是否按时生成、告警通道是否存在积压。我碰到过一次告警短信通道欠费,结果当天所有P1告警都没发出来,还是业务方来反馈才知道出了问题。
这些点看起来琐碎,但每一个都是真实环境里会咬人的坑。质量监控不是部署上线就完事,它和数仓本身一样,需要持续治理。
最后说点实在的
回看这几年做数据质量监控的经历,最大的体感是这个领域没有一劳永逸的完美方案。今天把告警阈值调好了,明天业务表结构变了又得重新摸基线;今天觉得这个规则查得很全面,后天新上的数据源又有新问题。但反过来,正因为这样,每次优化都能看到实实在在的收益:告警量降了,故障发现的时间从小时级降到分钟级,业务不再半夜打电话问“报表为什么没出来”。
如果你也在做数据仓库的数据质量监控,我建议先从最基础的任务运行监控做起,盯着状态、耗时、重试这三个指标,再慢慢地丰富规则和告警策略。不要一上来就设计一个大而全的指标引擎,那是写文章的时候好看,落地的时候会让你崩溃。把每一张核心表的监控闭环跑通,比铺一百条没人看的规则有用得多。这些经验都是我一段一段踩出来的,希望能帮你在自己的数仓环境里少走几步弯路。