简介:这份《数据质量管理平台需求文档》面向数据治理产品经理、数据架构师及企业信息化建设人员,用于梳理数据质量平台从立项到功能落地的完整需求。文档围绕数据质量六要素——准确性、完整性、一致性、时效性、有效性与可追溯性展开,依次阐述项目背景与目标、模块化系统架构与整体要求,并细化模板管理中的内置模板、创建、查询、修改与审批流程,以及规则管理、任务管理等设计要点,同时对数据源管理、元数据管理、异常检测、性能监控和权限控制提出要求,可作为需求评审、方案编写与选型的参考底稿。资源为1个PDF文件,压缩包约1.81MB,内容以目录化章节组织,便于按模块快速定位。目前已有443人学习浏览,适合需要搭建数据质量体系或撰写同类需求文档的读者对照使用。
1. 数据质量管理平台需求文档到底该定什么
需求评审会上最常出现的场景:业务方在文档里写「数据要准、要全、要及时」,研发追问「准到几个九、哪个字段、几分钟内到」,然后会议就卡住了。数据质量管理平台需求文档的真正价值,不是描述这个平台有哪些菜单和看板,而是把「数据好」这句形容词拆成可判定、可执行、可验收的条目。它必须回答三个问题:规则从哪里来、规则用什么跑、跑出不合格之后谁在多久内处理。这三件事没写清,后面无论用多贵的调度和存储,平台都只是个报表工具。适合读这份文档的人包括数据开发、数据治理、测试,以及握着指标口径的业务负责人——尤其是最后这一类,因为准确性规则的定义权往往在他们手里,而不是在数仓团队手里。判断一份需求文档能不能落地,最简单的标准是:把文档交给一个不熟悉业务的数据开发,他能不能在不追问的情况下写出一条校验 SQL。
2. 把需求文档拆成六维数据质量规则表
2.1 六个维度的判定口径和最容易混的地方
数据质量维度是需求文档的骨架,但写得最乱的也是它。常见做法是固定六个:完整性、唯一性、准确性、一致性、及时性、有效性。每个维度的判定必须落到一个可计算的表达式上,否则就是形容词。
| 维度 | 判定问题 | 典型表达式 | 常见误用 |
|---|---|---|---|
| 完整性 | 该有的值有没有 | col IS NULL OR TRIM(col)=''的空值率 | 把「默认值 -1」当成有值 |
| 唯一性 | 主键/业务键是否重复 | GROUP BY key HAVING COUNT(*)>1 | 用自增主键查重,永远通过 |
| 准确性 | 值与真实世界是否一致 | 值域、正则、与主数据比对 | 没有真值来源就声称「准确率 99%」 |
| 一致性 | 同一事实在不同表是否相同 | 跨表 JOIN 差异率 | 把跨系统一致性当跨表一致性 |
| 及时性 | 数据在 SLA 内是否就绪 | 分区就绪时间 vs 承诺时间 | 只看任务成功,不看数据到达 |
| 有效性 | 值是否符合业务规范 | 枚举码表、格式、区间 | 与完整性混为一谈 |
准确性和有效性是最容易写虚的两项。准确性依赖外部真值,如果需求文档给不出真值来源,就应该把它降级成「与上游系统对账一致」,而不是硬凑一个准确率。有效性则要有明确的码表或格式定义,例如手机号^1[3-9]\d{9}$、订单状态属于{10,20,30,40}。
2.2 需求条目到规则元数据的字段映射
需求文档里一句话,最终要变成一条结构化记录。字段不齐的规则在执行层一定会出问题——最常见的是漏了「责任人」和「例外清单」,导致告警发出来没人认领,或者把内部测试账号当成脏数据反复报警。
| 文档里的写法 | 规则元数据字段 | 是否必填 |
|---|---|---|
| 「订单表用户 ID 不能为空」 | table/column/dimension | 必填 |
| 「空值率不超过 0.5%」 | threshold/compare_op | 必填 |
| 「每天 T+1 校验」 | schedule/biz_date_offset | 必填 |
| 「P1 级问题 30 分钟内响应」 | severity/sla_minutes/owner | 必填 |
| 「测试账号除外」 | exception_list | 选填 |
| 「超过 1000 万行抽样」 | sample_rate/max_scan_rows | 选填 |
把这套映射写进文档附录,评审时逐条对照,能省掉后面大量的返工。经验上,一份规则条数在 200 条左右的平台需求,元数据字段缺项超过三成,执行层就必然要打补丁。
2.3 用 pdfplumber 把规则表抽成 JSON 配置
需求文档以 PDF 交付时,规则表往往是最难处理的部分——跨页表头、合并单元格、全角空格。常见做法是先抽出表格再人工核对,而不是指望一次全自动。下面这段脚本把 PDF 中的规则表提取成待确认的 JSON 列表:
import json import re import pdfplumber # 需要抽表的页码范围,1-based,跨页表格要连续写 PAGES = list(range(6, 15)) COLUMNS = ["rule_id", "domain", "table", "column", "dimension", "expression", "threshold", "severity", "owner"] def clean(cell): if cell is None: return "" # 去掉换行、全角空格和多余空白,PDF 里这几种字符混用很常见 return re.sub(r"[\s\u3000]+", " ", str(cell)).strip() rules = [] with pdfplumber.open("数据质量管理平台需求文档.pdf") as pdf: for pno in PAGES: page = pdf.pages[pno - 1] # 默认按线框识别;无框线表格可改用 {"vertical_strategy": "text"} table = page.extract_table({ "vertical_strategy": "lines", "horizontal_strategy": "lines", "snap_tolerance": 3, }) if not table: continue for row in table: cells = [clean(c) for c in row] if len(cells) != len(COLUMNS): continue # 跳过重复表头 if cells[0] in ("规则编号", "rule_id"): continue rules.append(dict(zip(COLUMNS, cells))) with open("dq_rules_draft.json", "w", encoding="utf-8") as f: json.dump(rules, f, ensure_ascii=False, indent=2) print(f"抽出 {len(rules)} 条待确认规则")逻辑说明:PAGES按 PDF 实际页码填写,表头所在页和跨页续页都要包含;extract_table的策略参数决定识别方式,有线框的表格用lines,靠空白对齐的表格改成text;snap_tolerance控制线框容差,设得过大相邻两列会被合并,设得过小则同一行被拆成多行。
参数说明:clean()里同时处理半角空白和全角空格\u3000,这是 PDF 抽表最典型的坑,TRIM不掉的「空值」多半是它;列数校验len(cells) != len(COLUMNS)用来丢弃合并单元格导致的错行,宁可漏抽也不要错抽;抽出的结果命名为_draft,强制走一遍人工确认,因为规则表达式一旦抽错,后续校验结果会静默失真。
3. 数据质量管理平台校验执行层的 SQL 编写
3.1 单表字段级校验的 SQL 模板
执行层的第一原则是:校验 SQL 必须与业务查询共用同一套表,不要再复制一份数据。完整性校验的模板如下,返回的是一行统计结果而不是明细,这样即使表很大也不会把结果集撑爆。
-- 完整性:按业务日期分区统计空值率 SELECT '${bizdate}' AS biz_date, 'dwd_order' AS tbl_name, 'user_id' AS col_name, COUNT(*) AS total_cnt, SUM(CASE WHEN user_id IS NULL OR TRIM(user_id) = '' THEN 1 ELSE 0 END) AS null_cnt, ROUND(SUM(CASE WHEN user_id IS NULL OR TRIM(user_id) = '' THEN 1 ELSE 0 END) / COUNT(*), 6) AS null_rate FROM dwd_order WHERE dt = '${bizdate}';逻辑说明:整条语句只扫一个分区,${bizdate}由调度系统注入;null_rate保留 6 位小数,避免小样本下 0.0001 被判成 0。参数说明:若表没有dt分区,必须补一个时间范围条件,否则全表扫描会拖垮集群;当单分区行数超过max_scan_rows时,加TABLESAMPLE (10 PERCENT)并同步把阈值判定改成「抽样空值率」,同时在告警文案里标明是抽样结果。
唯一性校验要返回重复键样本,否则问题无法定位:
-- 唯一性:找出重复的业务主键,LIMIT 防止明细过多 SELECT order_id, COUNT(*) AS dup_cnt FROM dwd_order WHERE dt BETWEEN '${start_date}' AND '${end_date}' GROUP BY order_id HAVING COUNT(*) > 1 ORDER BY dup_cnt DESC LIMIT 100;这里用业务主键order_id而不是自增 ID。窗口建议取 7 天而非单日,因为重复数据常常跨分区产生,只看当天会漏掉。
3.2 跨表一致性校验与口径对齐
一致性规则最难的不是 SQL,而是对齐两边的口径:一边统计的是支付成功金额,另一边统计的是下单金额,差异率天然存在,规则上线第一天就会误报。写规则前必须确认四件事——过滤条件、时间口径、币种/单位、是否含退款。
-- 一致性:订单表与支付表的金额差异率 SELECT COUNT(*) AS join_cnt, SUM(CASE WHEN ABS(o.pay_amt - p.pay_amt) > 0.01 THEN 1 ELSE 0 END) AS diff_cnt, ROUND(SUM(CASE WHEN ABS(o.pay_amt - p.pay_amt) > 0.01 THEN 1 ELSE 0 END) / COUNT(*), 6) AS diff_rate FROM dwd_order o JOIN dwd_pay p ON o.order_id = p.order_id AND p.dt = '${bizdate}' WHERE o.dt = '${bizdate}';参数说明:0.01是浮点容忍度,若两列都是DECIMAL(18,2)可直接用<>比较;使用JOIN而非LEFT JOIN时,join_cnt会小于订单总数,只能反映「两边都存在的记录」,如果要看支付缺失,必须再单独加一条左连接统计p.order_id IS NULL的规则。这两条规则经常被合并成一条写,结果既测不出缺失也测不出金额差。
3.3 调度、增量与采样参数怎么设
全量校验在数据量上到千万级之后基本不可持续,主流做法是走增量水位线,只校验新变更的数据:
-- 增量水位线:取上一次成功校验之后的最大更新时间 SELECT COALESCE(MAX(update_time), '1970-01-01 00:00:00') AS watermark FROM dwd_order WHERE dt = '${bizdate}';| 参数 | 作用 | 建议取值 |
|---|---|---|
concurrency | 同时执行的规则数 | 集群队列的 1/3,留出业务任务余量 |
retry_times | 单条规则失败重试 | 2 次,且重试前先判断上游任务状态 |
timeout_minutes | 单条规则超时 | P1 规则 10 分钟,P2 规则 30 分钟 |
sample_rate | 大表抽样比例 | 单分区超 5000 万行时取 10% |
biz_date_offset | 校验 T+N 数据 | 核心链路 T+1,外部接入 T+2 |
超时值和重试次数要跟上游任务的就绪时间绑定:上游还在跑就触发校验,得到的空值率是假的,这类误报占线上告警的一半以上。
4. 质量分、告警收敛与问题闭环的设计
4.1 DQI 加权计算与阈值基线
质量分的作用是给管理层一个可比较的数字,但它极容易被误用成考核指标,所以计算方式必须写进需求文档并公开。常见做法是规则通过率按维度加权,再对维度加权:
# 数据质量指数:规则通过率 -> 维度得分 -> 总分 RULE_WEIGHTS = {"P1": 3, "P2": 2, "P3": 1} # 按严重级别加权 DIM_WEIGHTS = { "completeness": 0.30, "accuracy": 0.30, "consistency": 0.20, "timeliness": 0.10, "uniqueness": 0.10, } def dimension_score(rules): """rules: [{dimension, severity, pass_rate}]""" weight_sum = sum(RULE_WEIGHTS[r["severity"]] for r in rules) if weight_sum == 0: return None # 该维度无规则,返回空而不是 0 return sum(RULE_WEIGHTS[r["severity"]] * r["pass_rate"] for r in rules) / weight_sum def dqi(rule_results): scores = {} for dim in DIM_WEIGHTS: subset = [r for r in rule_results if r["dimension"] == dim] s = dimension_score(subset) if s is not None: scores[dim] = s total_w = sum(DIM_WEIGHTS[d] for d in scores) return round(sum(DIM_WEIGHTS[d] * s for d, s in scores.items()) / total_w * 100, 2)逻辑说明:没有规则的维度返回None并在总分中剔除,而不是记 0 分——否则每上线一个新维度,历史分数会凭空下跌,业务方会立刻不信任这个指标。阈值基线建议取上线前 30 天历史数据的分位数,例如 P1 规则以历史 P5 分位作为告警线,而不是拍一个 99%。
4.2 告警抑制、聚合与升级策略
同一个字段的规则在批量补数期间可能连续失败上百次,不做收敛的告警系统会被直接静音,等于没有。策略参数需要在文档里写死:
| 策略 | 触发条件 | 参数建议 |
|---|---|---|
| 抑制 | 同一rule_id重复失败 | 30 分钟内只发一次 |
| 聚合 | 同一业务域多条规则同时失败 | 按domain合并成一条消息 |
| 升级 | 连续 3 个业务日期未修复 | 通知上级与值班群 |
| 静默 | 补数窗口或发布窗口 | 维护静默白名单,按任务实例 ID 而非表名 |
静默白名单要按任务实例 ID 匹配,按表名静默会把真实问题一起屏蔽掉,这是运维阶段最常见的自伤操作。
4.3 问题工单落库与根因归因
告警必须落到一张可查询的工单表,否则闭环无从谈起。最小可用的表结构如下:
CREATE TABLE dq_issue ( issue_id BIGINT COMMENT '工单ID', rule_id STRING COMMENT '规则ID', biz_date STRING COMMENT '业务日期', severity STRING COMMENT 'P1/P2/P3', metric_value DOUBLE COMMENT '触发时的指标值', threshold DOUBLE COMMENT '阈值', status STRING COMMENT 'OPEN/PROCESSING/FIXED/IGNORED', root_cause STRING COMMENT '上游变更/代码缺陷/业务异常/规则误配', owner STRING COMMENT '责任人', created_at TIMESTAMP COMMENT '创建时间', fixed_at TIMESTAMP COMMENT '修复时间' );根因分类建议固定成四五个枚举值,别让责任人自由填写。字段root_cause稳定之后,可以按周统计「规则误配」占比,如果这个比例超过两成,说明需求文档里的规则定义质量有问题,应该回头改文档而不是改阈值。
5. 验收条款变成可回放检查项的具体做法
需求文档里最没约束力的句子是「数据准确率不低于 99%」。它没法验证,因为它没定义分母。改写方式是补齐三个要素:统计口径、样本范围、验证手段。例如改成「以财务系统 T+1 对账文件为真值,取 2024 年 1 月至 3 月共 90 个自然日的订单金额合计,日粒度差异绝对值不超过 0.1%,差异天数不超过 2 天」。这样一句话就能直接写成脚本。
真值样本建议在平台上线前就固化下来,也就是一份「黄金数据集」:人工核对过的、包含已知脏数据的少量记录。它的作用不是覆盖全量,而是确保规则引擎本身没算错——很多上线事故不是数据脏,而是校验 SQL 写错了。回放脚本的骨架如下:
import json import pandas as pd # 黄金数据集:label 为人工判定结果,1 表示合格,0 表示不合格 golden = pd.read_csv("golden_dataset.csv") with open("dq_rules_draft.json", encoding="utf-8") as f: rules = json.load(f) hit, miss, false_alarm = 0, 0, 0 for r in rules: if r["rule_id"] not in golden["rule_id"].values: continue expected = int(golden.loc[golden["rule_id"] == r["rule_id"], "label"].iloc[0]) actual = int(r["exec_result"]) # 回放得到的实际判定 if expected == 1 and actual == 1: hit += 1 elif expected == 0 and actual == 1: miss += 1 # 漏报,最危险 elif expected == 1 and actual == 0: false_alarm += 1 # 误报,影响信任 precision = hit / (hit + false_alarm) if hit + false_alarm else 0 recall = hit / (hit + miss) if hit + miss else 0 print(f"precision={precision:.3f} recall={recall:.3f}")逻辑说明:漏报意味着真实脏数据被判为合格,直接进业务报表,所以验收时优先看recall;误报会消耗责任人耐心,precision低于 0.9 就说明阈值或口径需要重新标定。参数上,黄金数据集每条规则至少覆盖 30 条样本,且必须包含边界值——空字符串、全角空格、超出区间的负数、时间戳跨天。回放时把校验 SQL 里的${bizdate}替换成历史分区,跑完对比判定结果,差异条目逐条定位到 SQL 的哪一段。
一个容易忽略的验收点是及时性规则本身的可观测性:校验任务自己是否在承诺时间内跑完,需要单独埋一个监控,否则数据质量平台会以「校验任务没跑」的方式失灵,而这件事没有任何告警会告诉你。
本文还有配套的精品资源,点击获取