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 血缘关系规范:让数据流动看得见
数据血缘,说白了就是「这个字段从哪来、经过谁、到哪去」。没有血缘的契约,就像没有地图的导航——你只知道目的地,但不知道怎么走。
我个人习惯在契约里记录三级血缘:
- 上游来源:原始数据来自哪个业务系统、哪张表、哪个字段。
- 加工过程:经过了哪些ETL任务、使用了什么转换逻辑。
- 下游消费:被哪些报表、指标、模型所引用。
这里我画了一张数据血缘的流转图,帮你直观理解:
嗯,这里要注意一点:血缘关系不是一次性的。数据链路会变,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%。
为什么?因为每个字段都有「主人」,每个口径都有「证据」,每次变更都有「记录」。数据契约,说到底就是给数据立规矩。规矩立好了,治理就成功了一半。