数据契约设计 是做什么的?
适用场景
- 上游(服务、事件、API、文件)为下游管道提供数据,且变更可能悄无声息地破坏它们。
- 定义生产团队/系统与数据仓库之间的接口。
- 在摄入边界添加模式/质量强制执行。
- 不要用于单个 dbt 项目内部的模型到模型变更(请使用测试 +
处理模式演化)。
契约规范内容
- 模式:字段、类型、可空性和允许值。
- 语义:每个字段的含义及其单位/粒度。
- 保证:新鲜度/SLA、数据量预期、键的唯一性。
- 所有权:谁生产数据以及联系谁。
- 版本控制 + 变更策略:如何传达破坏性变更。
工作流程
- [ ] 将契约编写为版本化、已检入检查的模式(而非部落知识)
- [ ] 在摄入边界强制执行(到达时验证)
- [ ] 分类变更:添加性(安全) vs 破坏性(需要新版本)
- [ ] 违反时,拒绝/隔离并提醒生产者
- [ ] 提前版本化和传达破坏性变更
- 使其显式且版本化。 将契约作为代码(JSON Schema、Avro/Protobuf 模式或 YAML 规范)存储在管道旁边,像任何 API 一样经过审查。
- 在边界强制执行。 在数据到达时根据契约验证传入数据;拒绝或隔离违规数据,而不是加载它们。
- 分类变更。 添加性/可选字段 = 向后兼容。删除字段、重命名、收紧类型/可空性 = 破坏性 → 新版本。
- 向生产者大声失败,而不是在静默的下游失败。
模式
契约作为 JSON Schema(在摄入时强制执行):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["order_id", "amount", "ordered_at"],
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number", "minimum": 0 },
"ordered_at": { "type": "string", "format": "date-time" },
"coupon": { "type": ["string", "null"] }
},
"additionalProperties": false
}
强制执行点——在摄入时验证每条记录;将故障路由到带有原因的 隔离 位置,并提醒生产团队。这将一个无声的下游故障转化为一个立即的、有所有权的源头信号。
模式注册表(Kafka/Avro)——在发布时强制执行兼容性(BACKWARD),使生产者无法发布不兼容的模式。
常见陷阱
- 契约仅作为文档——如果不在代码中强制执行,它会漂移并无声地断裂。
- 在数据仓库深处强制执行——在边界捕获违规,在坏数据扩散之前。
- 无版本控制——每次变更都成为紧急事件;优雅地进行版本控制和弃用。
- 需要严格性时
additionalProperties不受限制——意外字段溜进来;在适当位置将其设为 false。 - 无所有者——被拒绝的批次无人可联系,管道停滞。
- 破坏性变更无提前期——通过版本化模式和弃用窗口协调生产者/消费者。