2、数据契约的核心要素:Schema定义、语义约束、SLA承诺、版本管理

数据契约这东西,说白了就是一份双方都认的「数据君子协定」。我见过太多团队上来就写契约,结果写出来的东西没人看得懂,更没人执行。为什么会这样?因为核心要素没抓住。

我个人习惯把数据契约拆成四个支柱:Schema定义、语义约束、SLA承诺、版本管理。缺一个,这契约就是废纸。

核心观点:数据契约不是文档,是可执行的约定。四个要素必须同时落地,缺一不可。

数据契约核心要素结构图 数据契约 Schema 定义 语义约束 SLA 承诺 版本管理 四要素缺一不可,共同构成可执行的数据契约

2.1 Schema 定义:契约的骨架

Schema 是数据契约最基础的部分。你想想看,连字段名、类型、长度都没说清楚,后面谈什么约束和 SLA?

我在项目中遇到过最坑的事:生产者和消费者对同一个字段的理解完全不一样。生产者传的是「用户等级编码」,消费者以为是「用户等级名称」。结果数据一落地,全是乱码。

一个合格的 Schema 定义,至少包含这些:

  • 字段名:统一小写蛇形命名,别搞驼峰和帕斯卡混用
  • 数据类型:精确到子类型,比如 decimal(18,2) 而不是 number
  • 是否必填:required 还是 optional
  • 默认值:空值时的兜底策略
  • 枚举范围:如果字段值有限,列出来

我的习惯:Schema 定义用 Protobuf 或 Avro 写,别用 JSON Schema。前者有强类型校验,后者太灵活,容易出幺蛾子。

举个例子,一个用户信息表的 Schema 定义:

syntax = "proto3";

message UserInfo {
  string user_id = 1;          // 用户ID,必填
  string user_name = 2;        // 用户名,必填
  int32 age = 3;               // 年龄,可选
  string email = 4;            // 邮箱,必填
  UserLevel level = 5;         // 用户等级,枚举
  string created_at = 6;       // 创建时间,ISO8601格式
}

enum UserLevel {
  LEVEL_UNKNOWN = 0;
  LEVEL_NORMAL = 1;
  LEVEL_VIP = 2;
  LEVEL_SVIP = 3;
}

2.2 语义约束:契约的灵魂

Schema 只说了「长什么样」,语义约束才告诉你怎么用。嗯,这里要注意——很多团队把语义约束写在文档里,而不是代码里。这等于没写。

语义约束包括:

  • 业务规则:比如「订单金额必须大于0」、「年龄必须在0-150之间」
  • 关联约束:比如「如果用户等级是VIP,积分不能低于1000」
  • 时序约束:比如「创建时间不能晚于更新时间」
  • 唯一性约束:比如「同一个订单号不能出现两次」

我曾经在一个金融项目中吃过亏。生产者说「交易金额是正数」,消费者以为「交易金额可以是负数(退款)」。两边都没在契约里写清楚,结果线上对账差了300多万。排查了三天,最后发现是语义约束没落地。

避坑指南:语义约束必须写在契约定义里,用断言或校验规则表达。别指望口头约定或 Wiki 文档,人脑靠不住。

语义约束的写法,我推荐用 JSON Schema 的 if-then-else 或者 Protobuf 的 custom option:

{
  "type": "object",
  "properties": {
    "order_amount": { "type": "number" },
    "order_status": { "type": "string", "enum": ["pending", "paid", "cancelled"] }
  },
  "if": {
    "properties": { "order_status": { "const": "paid" } }
  },
  "then": {
    "properties": { "order_amount": { "minimum": 0.01 } }
  }
}

2.3 SLA 承诺:契约的牙齿

没有 SLA 的数据契约,就像没有牙齿的老虎——看着吓人,咬不了人。

SLA 承诺要回答三个问题:

  1. 数据多久到?——延迟要求
  2. 数据准不准?——质量要求
  3. 数据能不能用?——可用性要求

我建议用表格把 SLA 指标写清楚:

SLA 维度 指标 典型值 衡量方式
延迟 P99 延迟 < 5 秒 端到端时间戳
吞吐 每秒处理条数 > 1000 TPS 监控系统
数据质量 空值率 / 异常率 < 0.1% 质量巡检
可用性 服务可用时间 99.9% 健康检查

关键点:SLA 必须可量化、可监控、可追责。写「尽快送达」这种模糊词,等于没写。

我个人习惯在契约里加一个 SLA 违约条款:比如连续3个周期不达标,消费者有权暂停消费,生产者必须在24小时内修复。别怕写得狠,契约就是用来约束双方的。

2.4 版本管理:契约的生命线

数据契约不是写一次就完事的。业务在变,Schema 在变,约束在变。怎么管?

版本管理要解决三个核心问题:

  • 向前兼容:新版本能不能被老消费者消费?
  • 向后兼容:老版本的数据能不能被新消费者处理?
  • 版本演进:怎么从 v1 平滑过渡到 v2?

我记得有一次,团队直接改了生产者的 Schema,把字段类型从 int 改成了 string。消费者那边没收到通知,结果消费端全部报错。这就是典型的版本管理缺失。

我推荐的版本管理策略:

  1. 语义化版本号:MAJOR.MINOR.PATCH
    • MAJOR:不兼容变更(删字段、改类型)
    • MINOR:向后兼容的新增(加可选字段)
    • PATCH:内部修复(改注释、改约束描述)
  2. 版本共存期:大版本升级时,新旧版本至少共存 2 个发布周期
  3. 废弃通知:废弃字段要提前 3 个版本标记 deprecated

我的习惯:用 Schema Registry 管理版本。每次变更自动生成 diff,通知所有消费者。别靠人工发邮件,人总会忘。

版本管理的代码示例(Protobuf 版本标注):

message UserInfo {
  string user_id = 1;
  string user_name = 2;
  int32 age = 3 [deprecated = true];  // 废弃,改用 birth_date
  string email = 4;
  UserLevel level = 5;
  string created_at = 6;
  string birth_date = 7;              // v2.0.0 新增
}

小结一下

四个要素,各有各的用处:

  • Schema 定义——让数据长什么样,双方看得见
  • 语义约束——让数据怎么用,双方说得清
  • SLA 承诺——让数据好不好,双方量得出
  • 版本管理——让数据怎么变,双方控得住

你想想看,这四个要素都落地了,数据契约还会是一纸空文吗?