是什么
数据质量巡检是把「数据是否可用」变成可调度、可告警的查询:每批数据落地后自动检查量级、时效、字段质量与分布是否符合预期,结果落表并接入告警。它解决的典型问题是「任务成功但数据是坏的」——写入链路没报错,接口返回 200,然而某个上游字段全空、某个分区少了九成数据。
基本信息
| 检查维度 | 完整性(量级、空值、主键重复)、时效(最新数据时间)、合法性(枚举与类型)、分布(分位数与取值比例) |
|---|---|
| 执行位置 | 数据落地后的下游任务里,与 ETL 同调度;巡检本身也写结果表 |
| 判定方式 | 优先用同时段历史基线,固定阈值作为兜底 |
| 输出 | 检查项、期望值、实际值、是否通过,落表后可做趋势图 |
典型使用场景
- 上游中断的早发现:写入量相对同时段基线骤降,比等到用户投诉快很多
- 字段质量劣化:某个必填字段空值率从 0.1% 涨到 30%,通常是上游改版或解析规则失配
- 时效劣化:最新数据时间长期停留在几小时前,说明采集或调度已经卡住
- 分布漂移:某个枚举值占比突变、金额分位数整体上移,往往对应业务规则或埋点变更
- 回填校验:补数后检查是否与线上数据重叠、是否出现主键重复
- 发布前后对照:版本上线后关键指标是否出现台阶式变化
巡检查询示例(ClickHouse)
巡检查询示例(ClickHouse)
-- 量级与时效:近一小时行数与最新数据时间
SELECT count() AS rows_1h, max(ts) AS latest,
dateDiff('second', max(ts), now()) AS lag_seconds
FROM logs.events
WHERE ts >= now() - INTERVAL 1 HOUR;
-- 字段质量:空值率与非法值占比
SELECT countIf(user_id = '' OR user_id IS NULL) / count() AS null_ratio,
countIf(status NOT IN ('paid','refunded')) / count() AS invalid_ratio
FROM logs.events
WHERE ts >= now() - INTERVAL 1 DAY;
-- 唯一性:按主键统计重复
SELECT count() AS total, uniqExact(order_id) AS uniq FROM dwd.orders WHERE dt = yesterday();与历史基线对比(伪代码)
与历史基线对比(伪代码)
# 同时段历史分位数做基线,比固定阈值更能适应周期与趋势
today = run_check("rows_1h")
baseline = median(same_hour_last_14_days("rows_1h"))
if today < baseline * 0.5 or today > baseline * 2:
alert("量级偏离基线", today=today, baseline=baseline)
# 记录本次实际值,作为下次基线的样本检查项与阈值建议
| 检查项 | 计算方式 | 典型阈值 |
|---|---|---|
| 量级 | 近一小时或近一日行数与历史同时段比 | 低于基线 50% 或高于 200% 告警 |
| 时效 | now 与最大时间戳之差 | 超过采集周期的两到三倍告警 |
| 空值率 | 空值或 NULL 计数除以总行数 | 必填字段超过 1% 立即告警 |
| 重复率 | 总行数与去重主键数之比 | 大于 1.001 即需排查 |
| 分布 | 关键枚举占比、分位数与历史对比 | 占比偏移超过 20% 关注 |
基线比固定阈值更稳:流量有周期也有趋势,写死的「低于 N 条告警」不是频繁误报就是形同虚设。用同时段历史分位数做基线,并在促销、发版等已知变更期间暂停或放宽判定。
巡检任务自身也会失败或被跳过:要监控巡检结果表的写入时间,否则「没有告警」和「巡检没跑」看起来一模一样,故障会被静默掩盖。