4、数据契约定义规范:命名规范、数据类型规范、业务含义规范、血缘关系规范

数据契约不是写出来就完事的。你得让它可执行、可校验、可追溯。

我见过太多团队,契约写了一大堆,结果字段名随意、类型对不上、业务含义全靠猜。说白了,这样的契约就是一张废纸。

这一章,我们聊聊怎么把契约写「硬」。四个维度:命名、数据类型、业务含义、血缘关系。一个一个来。

4.1 命名规范:让字段名自己会说话

命名这件事,我踩过不少坑。早期做数据仓库时,有个字段叫 amt,后来发现有的表里是美元、有的是人民币、有的是分。你想想看,一个字段名省了几个字母,后面要花多少时间去排查?

核心原则:命名即文档。字段名应该能直接反映业务含义、数据类型和粒度。

我个人习惯用这套命名体系:

类别 命名规则 示例
业务实体 业务域_实体名_属性 order_total_amount
度量指标 指标名_聚合方式_时间粒度 revenue_sum_daily
维度属性 维度名_属性名 user_register_channel
时间字段 时间类型_业务事件 dt_order_created

避坑指南:我曾经接手过一个项目,字段名混用了驼峰、下划线和匈牙利命名法。一个字段在A表叫 userName,在B表叫 user_name,在C表叫 strUserName。结果ETL脚本里全是 COALESCE 和 IFNULL 的补丁。所以,契约里必须强制统一命名风格,我推荐全小写下划线。

另外,保留字和特殊字符要提前列清单。比如 date、name、type 这些,很多数据库里是关键字,最好加前缀避免歧义。

4.2 数据类型规范:别让类型成为隐形的坑

数据类型这事,看起来简单,实际上一堆细节。我见过最离谱的,是把金额字段定义成 FLOAT,结果对账永远差几分钱。

为什么会这样?因为浮点数有精度问题。金融场景必须用 DECIMAL(18,2) 或 DECIMAL(18,4)。

这里我列一个常用数据类型对照表:

业务场景 推荐类型 不推荐类型 原因
金额/价格 DECIMAL(18,2) FLOAT / DOUBLE 浮点数精度丢失,对账必出问题
用户ID/订单ID STRING INT ID可能含前导零或字母,整数会截断
百分比/比率 DECIMAL(5,4) INT 整数存储会丢失小数精度
时间戳 TIMESTAMP STRING 字符串无法做时间运算和时区转换
枚举状态 STRING + 枚举约束 INT 整数枚举可读性差,容易映射错误

小技巧:在契约里,我习惯给每个字段加一个「类型约束说明」。比如 order_status 字段,除了写 STRING,还要注明枚举值列表:['pending', 'paid', 'shipped', 'completed', 'cancelled']。这样下游消费方不用猜。

4.3 业务含义规范:消除「你以为我以为」

命名和类型都对了,但业务含义不一致,照样出问题。

举个例子:active_user 这个字段,运营部门定义为「近30天有登录的用户」,产品部门定义为「近7天有任意操作的用户」,技术部门定义为「当天有API调用的用户」。同一个字段,三个口径,你说数据能对齐吗?

所以,业务含义规范要包含三要素:

  • 定义描述:一句话说清楚这个字段「是什么」和「不是什么」。
  • 计算口径:如果是衍生字段,写明计算公式或SQL逻辑。
  • 业务负责人:谁对这个字段的业务含义负责,有问题找谁。

我建议在契约里用这种格式:

字段: active_user_count
定义: 近30天内至少登录过一次的唯一用户数
口径: SELECT COUNT(DISTINCT user_id) FROM login_log WHERE login_date >= DATE_SUB(CURRENT_DATE, 30)
负责人: 张伟(数据产品)
变更记录: v1.0 2024-01-15 初始定义;v1.1 2024-03-01 从7天调整为30天

避坑指南:我曾经遇到一个经典案例——「GMV」字段。销售部认为GMV是「下单金额」,财务部认为是「已支付金额」,运营部认为是「已发货金额」。三个部门开会吵了一下午。最后我们在契约里拆成了三个字段:gmv_order、gmv_paid、gmv_shipped,各自带口径说明。从此天下太平。

4.4 血缘关系规范:让数据流动看得见

数据血缘,说白了就是「这个字段从哪来、经过谁、到哪去」。没有血缘的契约,就像没有地图的导航——你只知道目的地,但不知道怎么走。

我个人习惯在契约里记录三级血缘:

  1. 上游来源:原始数据来自哪个业务系统、哪张表、哪个字段。
  2. 加工过程:经过了哪些ETL任务、使用了什么转换逻辑。
  3. 下游消费:被哪些报表、指标、模型所引用。

这里我画了一张数据血缘的流转图,帮你直观理解:

数据血缘关系流转图 业务系统 订单表.order_amount 埋点系统 行为日志.event_value ETL 加工层 dwd_order_daily 清洗·转换·聚合 指标集市 gmv_sum_daily 报表系统 经营日报·GMV看板 血缘关系记录要点: 1. 每个字段必须标注上游来源表 + 字段名 2. 加工过程记录ETL任务ID + 转换逻辑摘要 3. 下游消费方列出所有引用该字段的报表/模型 4. 血缘变更时,必须同步更新契约中的血缘记录

嗯,这里要注意一点:血缘关系不是一次性的。数据链路会变,ETL逻辑会改,下游消费方也会增加。我建议把血缘记录放在契约的「元数据」部分,每次变更都更新版本号。

实战建议:如果你们公司有数据血缘工具(比如Atlas、DataHub),可以直接把工具里的血缘ID关联到契约里。如果没有,那就手动维护一张血缘关系表,至少把「上游表→当前字段→下游表」这个链路写清楚。我曾经在一个项目里,靠着手动维护的血缘表,三天定位了一个跨5层ETL的数据质量问题。

4.5 规范落地:契约模板示例

说了这么多,最后给一个完整的契约字段定义模板。你直接拿去用:

字段名: order_paid_amount
数据类型: DECIMAL(18,2)
业务含义: 用户实际支付金额(含优惠券抵扣,不含退款)
计算口径: SUM(payment_detail.paid_amount) WHERE payment_detail.status = 'success'
枚举/约束: 非负,保留两位小数
上游来源: payment_detail.paid_amount
加工过程: dwd_payment_daily (ETL job id: etl_023)
下游消费: gmv_daily_report, finance_settlement_view
业务负责人: 李四(财务数据产品)
版本: v2.1
变更历史: 
  v1.0 2023-06-01 初始定义
  v2.0 2023-12-15 增加优惠券抵扣说明
  v2.1 2024-02-10 修正上游来源字段名

这个模板,我用了三年。团队从10人扩到80人,数据字段从200个涨到2000个,但数据质量问题反而下降了60%。

为什么?因为每个字段都有「主人」,每个口径都有「证据」,每次变更都有「记录」。数据契约,说到底就是给数据立规矩。规矩立好了,治理就成功了一半。