数据即产品:像做软件产品一样做数据
好,咱们进入第四讲。前面三讲我们聊了单体数仓的痛点、数据网格的核心理念,以及如何划分数据域。今天要聊的这个话题,我个人认为是整个数据网格落地中最关键、也最容易被忽视的一环——数据即产品。
说白了,就是把数据当成一个软件产品来对待。你想想看,我们做软件产品时,会定义功能需求、性能指标、API 文档、版本号。那数据产品呢?是不是也应该有类似的东西?
我在很多团队里见过这样的场景:数据团队辛辛苦苦搭了个数仓,业务方过来问「这个表的字段是什么意思?」「数据什么时候更新?」「昨天跑的数据怎么和今天对不上?」——没人能说清楚。这就是典型的「数据交付了,但没交付产品」。
核心观点:数据产品不是把数据放到表里就完事了。它需要像软件产品一样,有明确的契约、文档、版本和生命周期管理。
4.1 数据产品的四个核心要素
我习惯把数据产品拆成四个维度来定义:SLA(服务等级协议)、Schema(数据模型)、文档、版本管理。缺一个,这个产品就不完整。
| 要素 | 类比软件产品 | 数据产品中的含义 |
|---|---|---|
| SLA | API 响应时间、可用性 | 数据新鲜度、准确性、可用性承诺 |
| Schema | 接口定义(API Contract) | 字段名、类型、约束、关系 |
| 文档 | API 文档、用户手册 | 数据含义、来源、加工逻辑、使用示例 |
| 版本管理 | Git 版本、语义化版本号 | Schema 变更、数据回溯、兼容性 |
4.2 定义 SLA:给数据产品上「契约」
SLA 是数据产品最容易被忽略的部分。很多团队觉得「数据跑出来了就行」,但从来没承诺过「什么时候跑出来」「跑出来对不对」。
我曾经在一个电商平台遇到过这种事:大促期间,运营团队等着凌晨两点的实时销售报表做决策,结果数据管道挂了,报表到早上八点才出来。运营总监直接冲到数据团队办公室拍桌子。你说这是谁的问题?数据团队觉得「管道故障不可抗力」,运营觉得「你们没说过会延迟」。其实缺的就是一个明确的 SLA。
一个完整的数据产品 SLA 应该包含以下内容:
- 新鲜度(Freshness):数据从产生到可查询的最大延迟。例如「T+1 早上 8 点前完成」或「实时延迟 < 5 分钟」。
- 可用性(Availability):数据服务在指定时间窗口内的可用百分比。例如「99.9% 可用」。
- 准确性(Accuracy):数据质量的最低标准。例如「主键唯一性 100%」「空值率 < 1%」。
- 响应时间(Latency):查询接口的 P99 响应时间。例如「单表查询 < 200ms」。
我的建议:SLA 不是越严格越好。定得太高,维护成本爆炸;定得太低,业务方不买账。我一般会先和业务方聊清楚「最晚什么时候要」「能容忍多少延迟」,然后定一个双方都能接受的基线。后续再逐步优化。
4.3 Schema:数据产品的「接口契约」
Schema 就是数据产品的 API 定义。在数据网格里,每个数据产品都必须暴露一个明确的 Schema,让消费者知道「这个数据集长什么样」。
我推荐使用 Avro 或 Protobuf 来定义 Schema,而不是直接用 SQL DDL。为什么?因为 Avro/Protobuf 支持 Schema 演化,能更好地管理兼容性。
来看一个简单的 Avro Schema 示例:
{
"type": "record",
"name": "OrderFact",
"namespace": "com.company.data.product.order",
"fields": [
{"name": "order_id", "type": "string", "doc": "订单唯一标识"},
{"name": "user_id", "type": "string", "doc": "用户ID"},
{"name": "product_id", "type": "string", "doc": "商品ID"},
{"name": "amount", "type": "double", "doc": "订单金额"},
{"name": "status", "type": {"type": "enum", "name": "OrderStatus",
"symbols": ["PENDING", "PAID", "SHIPPED", "COMPLETED", "CANCELLED"]},
"doc": "订单状态"},
{"name": "created_at", "type": "long", "logicalType": "timestamp-millis",
"doc": "订单创建时间"}
]
}
注意这里每个字段都加了 doc 属性。这就是文档的一部分。Schema 本身就应该携带语义信息,而不是让消费者去猜「这个字段是啥意思」。
避坑指南:我曾经见过一个团队,Schema 里字段名用 a1、b2 这种缩写,理由是「字段名太长影响存储效率」。结果三个月后,连他们自己都搞不清 a1 是啥。记住:Schema 是给人和机器共同读的,可读性永远是第一位的。
4.4 文档:数据产品的「用户手册」
文档这件事,说起来简单,做起来难。难在「写文档」和「维护文档」是两回事。很多团队一开始热情高涨,写了厚厚一本数据字典,半年后没人更新,文档和实际数据对不上,反而成了误导。
我个人的做法是:文档必须和 Schema 绑定,最好能自动生成。比如用 Avro Schema 里的 doc 字段,配合工具自动生成数据字典页面。这样 Schema 更新了,文档也跟着更新,不会脱节。
一个数据产品文档至少应该包含:
- 产品概述:这个数据集是干什么的?谁生产的?谁维护的?
- 字段说明:每个字段的名称、类型、含义、取值范围、示例值。
- 加工逻辑:数据从哪里来?经过了哪些 ETL 步骤?有没有聚合或过滤?
- 使用示例:常见的查询场景和 SQL 示例。
- 变更日志:每次 Schema 变更的时间、原因、影响范围。
嗯,这里要注意:文档不是写给数据团队自己看的,是写给消费者看的。所以语言要通俗,少用内部黑话。我见过一个文档写「本表通过 CDC 从 MySQL 同步至 Kafka,经 Flink 清洗后写入 Hive」,业务方看完一脸懵。改成「订单数据从业务数据库实时同步,每 5 分钟更新一次」是不是好多了?
4.5 版本管理:数据产品的「Git 提交」
数据产品的版本管理比软件版本管理更复杂。软件版本管理主要管代码,数据产品还要管 Schema 和数据本身。
我推荐使用语义化版本号(SemVer)来管理数据产品:
- MAJOR 版本:不兼容的 Schema 变更。比如删除了一个字段、修改了字段类型。
- MINOR 版本:向后兼容的 Schema 变更。比如新增了一个可选字段。
- PATCH 版本:数据修正或加工逻辑优化,Schema 不变。
举个例子:
# 当前版本:1.2.3
# 新增一个可选字段 "discount_amount" → 版本变为 1.3.0
# 删除字段 "old_status" → 版本变为 2.0.0
# 修复一个数据计算 bug → 版本变为 1.2.4
版本管理的关键是兼容性策略。我建议每个数据产品都明确声明自己的兼容性规则:
| 兼容性类型 | 说明 | 示例 |
|---|---|---|
| 向后兼容 | 新版本 Schema 可以读取旧版本数据 | 新增可选字段 |
| 向前兼容 | 旧版本 Schema 可以读取新版本数据 | 删除可选字段 |
| 完全兼容 | 同时支持向后和向前 | 新增默认值字段 |
| 不兼容 | 必须升级消费者 | 删除必填字段 |
我的经验:尽量保持向后兼容。消费者升级数据产品的成本很高,你改一个字段名,下游可能有几十个报表要跟着改。能不 breaking change 就别 breaking change。
4.6 数据产品生命周期管理
有了 SLA、Schema、文档、版本管理,数据产品就有了完整的生命周期。我画了一张图来展示这个流程:
这个流程里,我特别想强调反馈循环。数据产品不是一次性交付就完事了。消费者在使用过程中会发现 Schema 不合理、SLA 不满足、文档不清晰。这些反馈要能回到数据产品团队,驱动下一轮迭代。
4.7 落地建议:从「数据表」到「数据产品」的转变
最后,给正在实践数据网格的团队一些落地建议:
- 别一开始就追求完美。先选一个核心数据域,把它做成一个完整的数据产品。跑通流程比什么都重要。
- SLA 从简单开始。先承诺「T+1 早上 10 点前可用」,等稳定了再逐步缩短到 8 点、6 点。
- Schema 用工具管理。别用手写 JSON 或 SQL。用 Schema Registry(比如 Confluent Schema Registry 或 Apicurio)来管理版本和兼容性。
- 文档自动化。把文档生成集成到 CI/CD 流水线里。Schema 一更新,文档自动重新生成并发布。
- 版本号要严肃对待。MAJOR 版本变更必须通知所有消费者,给足够的迁移窗口。
我曾经踩过的坑:有个团队把数据产品的版本号写在了表名里,比如 order_fact_v1、order_fact_v2。结果消费者直接硬编码表名,每次版本升级都要改 SQL。正确的做法是用 Schema Registry 管理版本,消费者通过逻辑名称引用,版本切换对消费者透明。