
数据质量这五个词——完整性、准确性、一致性、及时性、唯一性——我在不同团队里听过至少三种不同的排序。做报表的同事最恨不及时做风控的同事最怕不准确做用户画像的同事天天跟唯一性打架而做数仓分层的那位永远在补完整性。这五个指标之所以被反复提起不是因为它们有多高深而是因为它们几乎覆盖了数据从产生到被消费的全链路上所有会出问题的地方。这篇文章想做的事情很具体把五个指标从口号拆成口径再从口径拆成能跑的SQL和能落地的监控规则。我会讲每个指标到底在度量什么、边界在哪里、用什么方式量化、阈值怎么定、出了问题从哪儿开始查。适合刚接手数据治理或数据质量工作的同学也适合已经在写校验任务、但总觉得规则写得零零散散、告警天天响却没人处理的同行。文中涉及的场景均为虚构代称代码和参数可以根据自己的业务替换。1. 数据质量为什么要单独拆成五个指标来讲1.1 一个笼统的数据不准毁掉了多少次沟通我见过最典型的场景是这样的业务方跑来说你们这个报表数字不对数据同学打开表一看表里确实有值逻辑也跑通了于是回一句数据没问题是业务理解错了。双方各说各话拉扯三天最后发现是上游一张维表的主键重复导致关联后金额被放大了一倍。这件事如果一开始就把唯一性和准确性分开说五分钟就能定位。笼统地说数据不准是一种无效表达。它把记录缺失、字段为空、口径不一致、主键重复、链路延迟这五类完全不同的问题混在一个词里而它们的排查路径、责任方、修复成本差异极大。完整性出问题通常要去查采集端和必填校验准确性出问题往往要去核对源系统和业务规则一致性出问题多半是码表版本或者关联逻辑的事及时性出问题看的是调度和资源唯一性出问题八成是幂等没做好或者上游重复推送。把指标拆开的第一层价值是让问题可被命名。可被命名的问题才能被分配责任人才能被统计趋势才能被纳入考核。这一层想清楚了后面所有的规则设计才有意义。1.2 五个指标之间不是并列关系而是互相拉扯很多人习惯把五个指标画成一张雷达图五个角各打一个分然后算个平均分当作数据质量总分。这个做法我在汇报场合见过不少但它有一个根本性的误导五个指标之间存在天然的取舍关系简单平均会把取舍抹掉。举个具体的例子。某电商团队为了提升及时性把订单表的同步频率从每小时一次改成每五分钟一次。及时性指标好看了但因为增量同步窗口变短跨库关联时维表还没更新导致订单归属的渠道字段出现大量空值完整性反而掉下去了。再比如为了提升唯一性在写入前做全量去重一次去重要扫几千万行任务跑四十分钟及时性直接崩掉。所以正确的做法不是给五个指标各打一个分然后求平均而是先明确当前业务阶段的主次。交易类链路的及时性和准确性优先级最高完整性可以靠事后补分析类链路的完整性和一致性优先级最高晚半小时出结果没人会在意。这个判断必须在设计监控体系之前完成否则你会得到一堆互相打架的告警。1.3 指标化的前提是能算出一个数我坚持一个观点一个不能被计算出具体数值的指标不算指标只能算口号。完整性如果不能算出今天订单表缺失了 0.37% 的记录那它就只是会议上的一句话。准确性如果不能算出金额字段与源系统对账差异率为 0.02%那它永远说不清到底有多不准。后面每一节我都会给出对应的量化公式和可执行的SQL。这里先统一一个前提所有指标都基于同一张校验结果表落地字段大致是校验日期、表名、指标类型、校验规则、总行数、异常行数、异常率、阈值、是否告警。这张表本身就是一个数据产品它让五个指标从抽象概念变成可查询、可趋势、可归因的实体。后面第7节会详细讲这张表怎么设计。注意不要一上来就追求全表全覆盖。先选三到五张核心表跑通闭环比铺开五十张表但没人看告警要有价值得多。2. 完整性先分清缺的是记录还是字段2.1 完整性的两个维度必须分开看完整性这个词最大的坑在于它有歧义。它可以指记录缺失——本该有1000条订单实际只有980条也可以指字段缺失——1000条订单都在但其中150条的收货地址是空的。这两类问题的成因和修复方式完全不同混在一起统计会让指标失去指导意义。记录级完整性关注的是行数对不对。判断依据通常来自上游的业务计数比如订单系统的当天下单量、支付系统的成功交易笔数。这类校验的关键是找到一个可信的参照系。很多团队的做法是拿自己昨天的数据乘一个经验系数来估算这个做法风险很高一旦业务有促销或者节假日估算就完全失真。更稳的方式是直接对接源系统的计数接口或者让上游在同步时带上批次总条数。字段级完整性关注的是关键字段填没填。这里的重点是关键两个字。一张两百个字段的表不可能每个字段都做非空校验需要按业务影响度排优先级。我的划分习惯是三层一级是主键和关联键空了整条记录就没法用二级是金额、状态、时间这类核心业务字段空了会导致指标算错三级是描述性字段空了只是不好看。2.2 完整性指标的量化口径与实现记录级完整性的计算方式完整性率 实际入库行数 / 上游应传行数 × 100%字段级完整性的计算方式字段完整率 (总行数 - 该字段为空的行数) / 总行数 × 100%注意空的定义。在多数数仓里空值可能是 NULL也可能是空字符串还可能是null、-、未知、0这类业务上的占位值。这几种情况必须在校验规则里统一处理否则你会发现监控显示完整率 99.8%但下游报表里一堆未知。-- 字段级完整性校验把多种空统一视为缺失 SELECT dwd_order_detail AS table_name, COUNT(*) AS total_rows, SUM(CASE WHEN receiver_name IS NULL OR TRIM(receiver_name) OR TRIM(receiver_name) IN (null, -, 未知, N/A) THEN 1 ELSE 0 END) AS null_rows, ROUND( SUM(CASE WHEN receiver_name IS NULL OR TRIM(receiver_name) OR TRIM(receiver_name) IN (null, -, 未知, N/A) THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 4 ) AS null_rate_pct FROM dwd_order_detail WHERE dt ${bizdate};记录级完整性则需要上游提供一个基准值通常落在一张对照表里WITH src AS ( SELECT src_count FROM ods_sync_batch_log WHERE table_name dwd_order_detail AND dt ${bizdate} ), tgt AS ( SELECT COUNT(*) AS tgt_count FROM dwd_order_detail WHERE dt ${bizdate} ) SELECT src.src_count, tgt.tgt_count, tgt.tgt_count - src.src_count AS diff_rows, ROUND((src.src_count - tgt.tgt_count) * 100.0 / NULLIF(src.src_count, 0), 4) AS missing_rate_pct FROM src CROSS JOIN tgt;阈值设定上记录级完整性我一般卡在 0.1% 以内超过就告警字段级要看字段等级主键和关联键必须是 0核心业务字段允许 0.5%描述性字段给到 5% 都问题不大。这些数字不是标准答案是从实际业务里跑出来的经验值你们的业务波动更大就适当放宽但一定要把阈值写进配置而不是硬编码在SQL里。2.3 完整性的三个隐蔽陷阱第一个陷阱是默认值伪装成完整。上游系统在字段为空时自动填 0 或者填当前时间同步到数仓后这个字段看起来 100% 有值完整率漂亮得很但实际上全是假的。这种情况必须在源端确认默认值策略靠数仓这边是查不出来的。我一般的做法是抽查一批记录去和源系统页面对比看到底是真空还是假空。第二个陷阱是分区漂移。按 dt 分区取数时如果某天的数据被写到了错误的分区你会发现当天完整率暴跌但第二天又恢复正常。这类问题不能靠单日校验发现需要做连续多日的趋势比对某一天突然偏离均值三个标准差以上就值得看一眼。第三个陷阱是增量同步的边界丢数。按更新时间做增量拉取时如果上游在两次拉取之间发生了数据更新而更新时的时间戳恰好落在边界上这批数据可能被漏掉。解决办法是把左闭右开改成左闭右闭重叠一分钟重复的部分靠唯一性去重解决——你看五个指标就是这么互相纠缠的。实操心得完整性的校验任务一定要跑在数据产出之后、下游消费之前卡在中间做拦截。等下游报表都出完了再告警通知的意义就只剩下下次注意了。3. 准确性最难量化也最容易扯皮的一项3.1 准确性没有绝对标准只有参照标准准确性的本质是数据值是否符合客观事实但麻烦在于数据团队往往接触不到客观事实只能接触到另一个系统里的值。所以实践中做的其实是与参照源比对而不是与真相比对。这句话很重要它决定了两件事第一参照源本身错了你的校验也是错的第二当两个系统对不上时不能默认是数仓错了。常见的参照源有三类。第一类是业务源系统比如订单金额跟交易系统对账这是最可靠的一类第二类是外部权威数据比如行政区划代码跟标准码表比对、身份证号做校验位验证第三类是规则推导比如实付金额 商品金额 运费 - 优惠这是用业务逻辑自洽性来判断不需要外部参照。第三类在实践中用得最多因为它不需要跨系统成本低。但它只能发现内部矛盾发现不了整条链路一起错的情况。所以成熟的做法是三层结合规则自洽做日常高频校验跨系统对账做每日一次外部参照做定期抽查。3.2 准确性规则怎么写才不沦为摆设我见过大量写得很随意的一致性规则比如金额不能为负。这条规则在退款场景下会产生海量误报因为退款单的金额本来就是负的。规则写错了告警就会泛滥泛滥的告警最后一定被静音然后整个监控体系就废了。写准确性规则有三个原则。第一是带上下文。同一条规则在不同业务场景下含义不同要按业务类型分组校验而不是全表一刀切。第二是可解释。每条规则都要能回答如果这条规则被触发说明业务上发生了什么答不上来的规则不要写。第三是有明确的修复动作。校验出来的异常行要么能自动修正要么有一个明确的人工处理入口否则查出来也只是躺在表里。-- 准确性订单金额的业务自洽校验按业务类型分组避免误报 SELECT order_type, COUNT(*) AS total_rows, SUM(CASE WHEN ABS(pay_amount - (goods_amount freight_amount - discount_amount)) 0.01 THEN 1 ELSE 0 END) AS mismatch_rows, ROUND(SUM(CASE WHEN ABS(pay_amount - (goods_amount freight_amount - discount_amount)) 0.01 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 4) AS mismatch_rate_pct FROM dwd_order_detail WHERE dt ${bizdate} AND order_type IN (normal, presale) -- 排除退款、换货等特殊类型 AND order_status NOT IN (cancelled) GROUP BY order_type;这里的 0.01 是浮点精度容忍度金额字段建议统一用 DECIMAL 存储避免 FLOAT 带来的尾差。这个细节听起来微不足道但我在三个不同的项目里都见过因为浮点精度问题导致对账差异率虚高、排查了两天的情况。3.3 准确性的优先级排序与治理节奏准确性不可能做到 100%也不需要。关键是区分影响决策的准确性和影响展示的准确性。前者必须卡死比如财务报表的收入金额、风控模型的逾期标识后者可以容忍比如商品详情页的销量展示。我的做法是给每张核心表列一个准确性敏感字段清单清单内的字段做每日全量校验清单外的做每周抽样。这个清单不是拍脑袋定的而是从下游消费情况反推的把这个表的下游任务全部拉出来看哪些字段被 WHERE 条件、JOIN 条件、聚合函数引用过被引用次数多的优先纳入。治理节奏上我个人不推荐发现问题就马上改上游的做法。上游改动影响面大往往需要排期。更现实的是在数仓内部先做一层修正把明显异常的记录打上标记或者路由到隔离表保证下游不被污染同时把问题沉淀成上游的待办。这个先止血再治病的顺序在实际项目里比追求一步到位有效得多。注意准确性校验的结果一定要保留原始值和处理动作不要只留一个异常标记。后面追溯问题的时候当时这条记录长什么样比当时它被判为异常有用得多。4. 一致性跨表、跨系统、跨时间的对齐问题4.1 一致性的四个层次要分层治理一致性经常被当成一个笼统的概念实际上它至少分四个层次治理手段差别很大。第一层是同表内的一致性比如订单表里订单状态是已支付但支付时间字段是空的。这类问题用规则校验就能发现成本最低。第二层是跨表一致性。订单表和订单明细表之间主表金额应该等于明细金额之和。这类校验需要 JOIN要注意数据量和关联键的唯一性否则会因为关联放大导致误判。第三层是跨系统一致性。数仓的订单数据与交易系统的数据是否一致这是最典型的对账场景通常按天跑批输出差异明细。第四层是跨时间一致性。同一个指标昨天算出来是 100 万今天重跑历史数据变成了 98 万。这说明上游有数据被回撤修改或者计算逻辑有变更。这类问题最难发现因为它不影响单日的正确性只影响历史可比性。解决办法是对关键指标做快照留存每次重跑都记录版本发现历史值变动就追查原因。4.2 跨表一致性校验的实操写法跨表校验最容易踩的坑是关联键重复导致结果失真。写校验之前必须先确认关联键在两张表里都是唯一的否则要先聚合再关联。-- 跨表一致性主表金额 vs 明细汇总金额 WITH main AS ( SELECT order_id, pay_amount FROM dwd_order_main WHERE dt ${bizdate} ), detail AS ( SELECT order_id, SUM(item_amount) AS detail_amount, COUNT(*) AS item_cnt FROM dwd_order_detail WHERE dt ${bizdate} GROUP BY order_id -- 先聚合保证关联键唯一 ) SELECT COUNT(*) AS total_orders, SUM(CASE WHEN ABS(m.pay_amount - COALESCE(d.detail_amount, 0)) 0.01 THEN 1 ELSE 0 END) AS inconsistent_orders, ROUND(SUM(CASE WHEN ABS(m.pay_amount - COALESCE(d.detail_amount, 0)) 0.01 THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 4) AS inconsistent_rate_pct FROM main m LEFT JOIN detail d ON m.order_id d.order_id;这里用 LEFT JOIN 而不是 INNER JOIN 是有讲究的。用 INNER JOIN 会漏掉主表有、明细表没有的情况而这恰恰是最严重的完整性问题。用 LEFT JOIN 配合 COALESCE能同时发现金额不一致和明细缺失两类问题。一致性指标的计算要注意分母。分母应该是主表的记录数而不是关联后的记录数。如果分母取错会出现明明有100条不一致异常率却只有0.5%的诡异结果。4.3 码表与主数据一致性问题的高发区我处理过的一致性问题里超过一半最终都指向码表。典型表现是同一个渠道在A系统叫app在B系统叫APP在C系统叫1。三个系统各自都对合到一起就全乱了。这类问题的根治办法是建立统一的码表管理所有系统在对接时做一次映射转换映射关系单独存一张表并且带版本和生效时间。听起来很重但比在几十个SQL里散落着CASE WHEN要轻得多。-- 码表映射带生效时间避免历史数据被新映射污染 SELECT o.order_id, o.channel_code AS raw_channel, COALESCE(m.std_code, UNKNOWN) AS std_channel FROM dwd_order_main o LEFT JOIN dim_code_mapping m ON o.channel_code m.src_code AND m.code_type channel AND o.create_time m.effect_time AND o.create_time COALESCE(m.expire_time, 9999-12-31);加生效时间这个细节非常关键。如果不加码表一更新所有历史数据重算后结果都会变直接引发跨时间一致性问题。我在一个项目里就是因为漏了这个条件导致月度报表重跑后和历史版本对不上解释了两天才说清楚。实操心得码表变更必须走审批并且留痕变更时间点和影响范围要同步给下游。没有这个流程一致性永远治不完。5. 及时性数据价值的保质期到底有多长5.1 把延迟拆开看才知道该优化哪一段很多人算及时性只算一个总时长从数据产生到报表可见。这个数字对业务方有意义对技术排查没意义。真正有用的是把它拆成四段。第一段是产生延迟业务发生到写入源库的时间这段通常不归数据团队管。第二段是采集延迟源库到ODS的时间取决于同步工具和调度频率。第三段是加工延迟ODS到DW DWS的时间取决于任务复杂度和资源。第四段是消费延迟数据就绪到报表刷新或接口返回的时间。拆开之后你会发现优化点往往不在你以为的地方。我遇到过一个案例团队一直以为是加工慢拼命优化SQL结果拆解后发现采集占了总时长的 70%因为同步任务是每小时整点跑一次平均要等半小时。5.2 及时性指标的量化与SLA设计及时性的核心指标是就绪时间点和延迟时长延迟时长 数据实际就绪时间 - 业务约定就绪时间注意是跟约定时间比而不是跟数据产生时间比。因为业务方真正在意的是我九点上班能不能看到昨天的数据而不是理论上的最小延迟。SLA 的设计要分层。核心报表约定 07:00 前就绪超过 15 分钟触发一级告警重要报表 08:00 前超过 30 分钟触发二级一般报表 10:00 前超过一小时发个通知就行。分层的目的是让告警有轻重全都是一级等于没有级别。数据等级约定就绪时间一级告警阈值二级告警阈值通知方式核心交易类07:00超时15分钟超时30分钟电话群消息经营分析类08:00超时30分钟超时60分钟群消息邮件探索分析类10:00超时60分钟超时120分钟邮件这张表要跟业务方一起定不能数据团队单方面拍。业务方认可的时间点才是有效SLA。5.3 及时性和准确性的取舍要有明确规则前面提到过为了快而牺牲准是常见做法但必须把规则写死不能靠人临场判断。我的建议是设一条降级线当上游延迟导致无法在约定时间内完成全量加工时先输出基于不完整数据的估算版本同时明确标注数据完整度和更新时间等全量数据到位后再覆盖更新。这个做法在经营分析场景下被广泛接受但在财务结算场景下绝对不能这么干。哪些场景可以降级哪些不可以要提前跟业务方确认并写进文档。等到出事那天再来讨论一定会吵起来。注意及时性告警不要只发给数据团队。上游采集延迟导致的超时数据团队除了转发告警什么也做不了必须直接触达责任方。6. 唯一性重复数据到底是从哪长出来的6.1 重复的四种来源一种都不能漏唯一性问题看起来简单就是同一条记录出现了多次但成因差别很大去重策略也不同。第一种是源系统重复推送。上游因为重试机制没做幂等同一条消息推了两次。这种情况在写入时做去重最有效。第二种是增量同步重叠。为了防丢数把时间窗口设成左闭右闭重叠部分就重复了。第三种是关联放大。主表一条记录关联了维表多条记录结果集膨胀。第四种是多次写入没有覆盖。同一分区被多个任务写入用的是追加而不是覆盖。第三种最容易被误判为数据重复。很多人看到订单数比预期多了一倍第一反应是去重其实根源在关联条件写错了。所以排查唯一性问题时第一步永远是确认重复发生在哪一层是源表就有重复还是关联之后才重复。-- 唯一性校验区分源表重复和关联后重复 -- 第一步源表主键唯一性 SELECT dwd_order_main AS table_name, COUNT(*) AS total_rows, COUNT(DISTINCT order_id) AS distinct_keys, COUNT(*) - COUNT(DISTINCT order_id) AS dup_rows, ROUND((COUNT(*) - COUNT(DISTINCT order_id)) * 100.0 / NULLIF(COUNT(*), 0), 4) AS dup_rate_pct FROM dwd_order_main WHERE dt ${bizdate}; -- 第二步业务唯一键校验一个订单在一个渠道下只应有一条有效记录 SELECT order_id, channel_code, COUNT(*) AS cnt FROM dwd_order_main WHERE dt ${bizdate} GROUP BY order_id, channel_code HAVING COUNT(*) 1 LIMIT 100; -- 只取样本用于定位问题而非全量展示第二步用 HAVING 过滤出重复组比全表扫一遍再看结果要高效得多。LIMIT 100 是刻意的排查阶段看样本比看全量有用几万条重复明细刷屏反而干扰判断。6.2 幂等设计是根治手段去重只是止血如果只能在唯一性上给一条建议我会说把写入改成幂等。具体做法是写入前先按分区删除再整批插入或者用 MERGE 语句按主键更新。-- 幂等写入先删分区再插入保证重复跑批不会产生重复数据 DELETE FROM dwd_order_main WHERE dt ${bizdate}; INSERT INTO dwd_order_main SELECT * FROM ods_order_main_stg WHERE dt ${bizdate};这个写法在批处理场景下最稳代价是每次都要重写整个分区。数据量在千万级以内可以接受上亿的话要考虑用主键 MERGE 或者按小时拆分分区。流处理场景下的幂等要靠主键去重通常用状态存储保存最近一段时间的主键集合写入前先判断。这里的最近一段时间要覆盖住上游可能的最大重试间隔设小了会漏设大了内存扛不住。6.3 唯一性指标的两种口径唯一性率 (总行数 - 重复行数) / 总行数 × 100%这里重复行数的定义要统一。如果一组主键出现了3次重复行数算2还是算3我建议统一算多出来的行数也就是 3 - 1 2这样唯一性率的分母和分子逻辑自洽。另一种口径是重复组占比即存在重复的主键个数除以总主键个数。这个指标更适合用来衡量影响面比如今天有 0.05% 的订单存在重复比重复了 1200 行更容易让业务方理解。两个口径都要算前者用于技术监控后者用于对外沟通。实操心得唯一性校验规则一定要覆盖业务唯一键不能只校验主键。主键是自增ID的话永远不会重复但业务上同一个订单号出现两次才是真问题。7. 五个指标怎么落成一套能跑起来的体系7.1 先建口径字典再写校验任务我见过太多团队直接开写校验SQL写到第五十张表的时候发现规则命名五花八门有的叫order_null_check有的叫check_order_001最后没人知道哪条规则对应哪个指标。正确的顺序是先定口径字典。字典里至少包含六个字段指标类型五选一、适用对象表名字段名、计算方式自然语言描述、阈值、责任方、告警等级。这张字典本身就是文档新同学接手时看字典比看代码快得多。指标类型适用对象计算方式阈值责任方等级完整性订单主表-主键主键为空行数/总行数0%采集组P0完整性订单主表-收货地址空值行数/总行数≤0.5%采集组P2准确性订单主表-实付金额与明细汇总差异0.01的行数占比≤0.1%交易组P0一致性订单主表-渠道编码无法映射到标准码表的行数占比≤0.05%主数据组P1及时性订单宽表就绪时间晚于07:00的分钟数≤15分钟调度组P0唯一性订单主表-订单号重复行数/总行数0%采集组P0这张表填完之后写校验任务就变成了填空题效率高很多也不会漏。7.2 校验任务的分层编排校验任务不要和业务任务混在一起跑。我的做法是分成三层。第一层是入库即校验数据写入ODS的同时做轻量校验只查主键非空和主键唯一这两项成本极低但能拦住最严重的问题。第二层是产出后校验DWD/DWS 表加工完成后跑全量规则这一层是主力。第三层是消费前校验报表刷新或接口发布之前做一次快速抽查只校验核心字段作为最后一道闸。分层的意义在于成本控制。全量规则每次都跑资源消耗很大而大部分问题在第二层就能发现。第三层只做兜底跑几条最关键的规则就行。编排上有个细节容易忽略校验任务本身要有超时控制。我遇到过一次校验任务因为全表扫描卡住把后续所有任务都堵了最后是整个调度队列雪崩。校验任务是守护者守护者自己不能成为风险源。7.3 告警分级与闭环机制告警发出去没人处理是数据质量体系最常见的死法。要解决这个问题光靠提高重视程度这种话没用得有机制。第一是分级。P0 必须电话触达P1 群消息加责任人P2 只进日报。分级的依据是影响面不是修复难度。第二是收敛。同一条规则连续多天触发要合并成一条持续告警而不是每天发一条新的否则责任人会直接屏蔽。第三是闭环。每条告警都要有状态流转待处理、处理中、已修复、已忽略。忽略必须填原因填了原因才能关闭。第四是复盘。每周挑一到两个P0/P1问题做根因分析重点不是这次怎么修的而是为什么监控没在更早的时候发现。这个动作坚持做三个月规则质量会明显提升。注意不要用数据质量得分作为唯一考核指标。得分可以被刷比如把阈值调宽。真正该考核的是P0问题的平均修复时长和重复发生率。8. 常见问题与排查实录8.1 五类问题的快速定位路径现象优先怀疑第一步动作常见根因报表总数比昨天少一截完整性比对上游批次计数增量窗口漏数、任务失败重跑未覆盖金额对不上但行数一致准确性抽样比对源系统浮点精度、优惠分摊规则变更同一指标两个报表数不同一致性检查两边的过滤条件和码表版本口径未统一、码表版本不一致早上九点报表还没出及时性看调度甘特图找卡点上游延迟、资源争抢、单任务超时订单数突然翻倍唯一性先查源表还是关联后关联放大、重复推送、分区重复写入这张表我贴在工位上用了很久新人上手基本能靠它完成第一轮定位。但要说清楚它只能定位到方向具体根因还得看数据。8.2 几个踩过的坑第一个坑是校验规则写得太严导致告警疲劳。有段时间我们对某张表的手机号字段做 100% 非空校验结果每天都有几十条空值查下来发现是境外用户本来就没有国内手机号格式。规则本身没错错在没区分业务场景。后来改成按用户类型分组校验告警量直接降到几乎为零。这件事让我记住一个原则任何 100% 的阈值都要先问一句真的存在 100% 的场景吗。第二个坑是校验任务和被校验任务用同一个资源队列。被校验任务把资源占满时校验任务排队等着等它跑起来问题已经影响下游了。后来把校验任务单独放到一个低优先级但保证最低资源的队列虽然慢一点但保证能跑起来。第三个坑是只校验最终表不校验中间表。有次DWS层出了问题追溯上去发现是DWD的一张中间表某个字段全是空值但因为最终表做了 COALESCE 兜底表面看数据正常实际上全是兜底值。后来在每一层都加了抽样校验虽然多了些任务但定位问题快了不止一倍。第四个坑是阈值从来不调整。业务量从日均一万涨到日均五十万阈值还按一万的时候定的绝对值结果天天告警。阈值要么用百分比要么定期回顾调整写死绝对值早晚出问题。8.3 给刚开始做这件事的同学的建议先别急着上平台。我见过好几个团队一上来就选型数据质量平台配置了两个月规则写了三百条最后实际在用的不到三十条。真正有效的路径是从最痛的那张表开始手写几条SQL跑两周看看告警是不是真的被处理了再决定要不要平台化。另外五个指标不要平均用力。找到你们业务当前最痛的那一个集中打透建立起团队对监控的信任再往外扩。信任这东西一旦建立起来后面推什么都容易一开始就搞一堆误报后面再想推就难了。最后一点所有校验结果都要留历史。不要只存最新一天的快照要存每日结果。数据质量的价值不在于某一天发现问题而在于能画出趋势曲线看到某个指标连续三周缓慢下滑然后在上游还没造成大范围影响之前就介入。这个能力比任何一条单点规则都值钱。