数据契约核心要素:Schema定义、SLA承诺、血缘关系、数据质量规则
上一章我们聊了数据契约是什么,以及它为什么能解决数据质量的老大难问题。这一章,咱们把契约拆开看看——它到底由哪些核心要素组成?
我个人习惯把数据契约比作一份「数据产品的说明书+保修单」。你想想看,买台冰箱还有参数页和保修条款呢,数据流转凭什么没有?
一份完整的数据契约,至少包含四个核心要素:Schema定义、SLA承诺、血缘关系、数据质量规则。缺一个,契约就不完整。
核心观点:数据契约不是一张纸,而是一套可执行、可验证、可追溯的元数据体系。四个要素缺一不可。
1. Schema定义:数据契约的「语法层」
Schema定义,说白了就是告诉上下游:数据长什么样。字段名叫什么、类型是什么、是否允许为空、有没有默认值——这些都得写清楚。
我在项目中遇到过最典型的坑:上游改了字段类型,从int改成string,下游消费端直接报错。为什么?因为Schema定义没有纳入契约管理。
一份好的Schema定义,应该包含以下内容:
- 字段名:统一命名规范,比如snake_case还是camelCase
- 数据类型:精确到数据库类型或Avro/Protobuf类型
- 约束条件:主键、外键、唯一性、非空、默认值
- 字段描述:每个字段的业务含义,避免歧义
- 版本号:Schema版本管理,支持向前/向后兼容
我的建议:Schema定义最好用Avro或Protobuf这类结构化格式来写,别用Excel。Excel传着传着就乱了,而且没法做自动化校验。
举个例子,一个用户订单表的Schema定义,用Avro格式写出来大概是这样:
{
"type": "record",
"name": "Order",
"namespace": "com.company.order",
"version": "1.2.0",
"fields": [
{"name": "order_id", "type": "string", "doc": "订单唯一标识"},
{"name": "user_id", "type": "string", "doc": "用户ID"},
{"name": "amount", "type": "double", "doc": "订单金额"},
{"name": "status", "type": {"type": "enum", "symbols": ["PENDING", "PAID", "CANCELLED"]}, "doc": "订单状态"},
{"name": "created_at", "type": "long", "doc": "创建时间戳"}
]
}
嗯,这里要注意:版本号一定要写。没有版本号的Schema,就像没有日期的合同——你根本不知道它什么时候生效、什么时候过期。
2. SLA承诺:数据契约的「服务层」
Schema定义好了,数据长什么样清楚了。但还有一个关键问题:数据什么时候到?
我曾经在一个实时数仓项目里,上游承诺每天凌晨2点前产出数据,结果经常拖到早上6点。下游的报表系统每天凌晨3点跑批,跑出来的全是空数据。这就是典型的SLA缺失。
SLA承诺通常包含以下几个维度:
| SLA维度 | 说明 | 常见指标 |
|---|---|---|
| 时效性 | 数据从产生到可用的时间窗口 | T+1、小时级、分钟级、秒级 |
| 可用性 | 数据服务在约定时间内的可用比例 | 99.9%、99.99% |
| 延迟上限 | 数据延迟的最大容忍值 | 不超过30分钟、不超过2小时 |
| 数据量 | 每次交付的数据记录数范围 | 日增量100万~200万条 |
避坑指南:我曾经见过一个团队,SLA里只写了「T+1」,但没写具体几点。结果上游凌晨4点跑完,下游凌晨2点就开始等。双方扯了两个月才把SLA改成「T+1 08:00前」。记住:SLA一定要精确到具体时间点。
SLA承诺怎么写进数据契约?我个人习惯用YAML格式,清晰且容易自动化解析:
sla:
freshness: "T+1 08:00:00" # 每日8点前必须可用
availability: 0.999 # 可用性99.9%
max_latency_minutes: 30 # 最大延迟不超过30分钟
expected_row_count:
min: 800000
max: 1200000
notification:
on_breach: ["email", "webhook"]
escalation: "15分钟未恢复自动升级"
3. 血缘关系:数据契约的「追溯层」
数据出了问题,最怕什么?最怕不知道它从哪来的。
血缘关系,就是记录数据从源头到终点的完整路径。上游表、ETL脚本、中间表、下游视图——每一步都要记录清楚。
你想想看,一个数据仓库里几百张表、几千个任务,没有血缘关系,排查问题就像大海捞针。我经历过一次线上事故:报表里某个字段突然全是空值,排查了整整两天,最后发现是三个月前一个ETL脚本改了字段映射。如果有血缘关系,十分钟就能定位。
血缘关系在数据契约中应该包含:
- 数据来源:原始表、上游系统、API接口
- 加工过程:ETL脚本、SQL视图、数据管道
- 数据去向:下游表、报表、模型、应用
- 字段级映射:每个字段从哪来、经过什么转换
我的经验:血缘关系不要只做到表级别,一定要做到字段级别。表级别的血缘只能告诉你「这个表有问题」,字段级别的血缘才能告诉你「这个字段为什么有问题」。
血缘关系在契约里的表达方式,我推荐用JSON或YAML记录字段级映射:
lineage:
source: "ods.orders"
target: "dwd.order_detail"
field_mappings:
- source_field: "order_id"
target_field: "order_id"
transform: "直接映射"
- source_field: "user_id"
target_field: "user_id"
transform: "直接映射"
- source_field: "total_amount"
target_field: "amount"
transform: "round(total_amount, 2)"
- source_field: "status_code"
target_field: "status_name"
transform: "CASE WHEN status_code=1 THEN '已支付' ..."
4. 数据质量规则:数据契约的「验证层」
前面三个要素,定义了数据长什么样、什么时候到、从哪来。但还有一个核心问题:数据对不对?
数据质量规则,就是给数据做「体检」的标准。每条规则都是一个可执行的检查项,跑不过就说明数据有问题。
常见的质量规则类型:
| 规则类型 | 检查内容 | 示例 |
|---|---|---|
| 完整性 | 必填字段不能为空 | order_id非空率=100% |
| 准确性 | 数据值在合理范围内 | 金额 > 0 且 < 1000000 |
| 一致性 | 跨表数据逻辑一致 | 订单总数=明细行数之和 |
| 唯一性 | 主键无重复 | order_id唯一性=100% |
| 时效性 | 数据时间戳在合理窗口内 | created_at不晚于当前时间 |
质量规则怎么写进契约?我建议用结构化的方式,每条规则都包含:规则ID、规则类型、检查SQL、阈值、严重级别:
quality_rules:
- rule_id: "QR-001"
type: "完整性"
description: "订单ID不能为空"
check_sql: "SELECT COUNT(*) FROM {table} WHERE order_id IS NULL"
threshold: 0
severity: "CRITICAL"
on_violation: "阻断下游消费"
- rule_id: "QR-002"
type: "准确性"
description: "订单金额必须大于0"
check_sql: "SELECT COUNT(*) FROM {table} WHERE amount <= 0"
threshold: 0
severity: "HIGH"
on_violation: "告警并通知数据负责人"
避坑指南:质量规则不是越多越好。我曾经见过一个团队,一口气写了200多条规则,结果每天告警几百条,最后大家直接忽略告警了。我的建议是:先抓核心规则(完整性、唯一性、关键字段准确性),跑稳了再逐步加。规则太多等于没有规则。
四个要素如何协同工作?
这四个要素不是孤立的,它们是一个整体:
- Schema定义是基础,没有它,其他三个要素无从谈起
- SLA承诺是保障,确保数据在正确的时间到达
- 血缘关系是线索,出了问题能快速定位
- 质量规则是防线,在数据交付前就把问题拦住
我习惯把这四个要素比作一个「数据质量四层模型」:底层是Schema,中间层是血缘和SLA,顶层是质量规则。每一层都依赖下一层,缺一层都不稳。