1. 数据中台到底在搭什么:从一堆报表说起
刚接手数据体系搭建那会儿,我面对的是这样一个局面:业务方要一份月度经营分析,我得从三个不同的系统里导数据,用Excel拼透视表,拼完还要跟财务对口径,对完发现两边算出来的GMV差了小两百万。查了一整天,最后发现是订单系统里“已退款”状态的订单,一边算进去了,另一边没算。这种事儿经历过的都懂,不是能力问题,是数据体系本身就没搭起来。
数据中台这个概念被说了好几年,但落到实操层面,很多人还是把它理解成“搞个大数据库,把数据都塞进去”。真做起来你会发现,数据体系搭建的核心不是技术选型,而是把数据的“产、存、算、用”四个环节串成一条可追溯、可复用、可治理的链路。它要解决的问题很具体:口径不统一、数据找不到、质量没保障、复用成本高。适合谁来参考?如果你是数据团队负责人、数据开发工程师、或者业务侧的数据分析师,正在经历“数据越用越乱”的阶段,那这篇手记里的东西应该能对上你的痛点。
我所在的团队规模不大,数据开发加分析师一共六个人,支撑着公司三条业务线的数据需求。这个体量决定了我们不可能照搬大厂那套动辄上百人维护的中台架构,得走一条“轻量但管用”的路子。下面我把整个搭建过程拆开来讲,包括我们踩过的坑、做过的取舍、以及那些文档里不会写的实操细节。
2. 整体架构设计:为什么我们选了“轻中台”路线
2.1 先想清楚不做什么,比想清楚做什么更重要
一开始我们列了一张大而全的规划图:数据采集层、数据计算层、数据服务层、数据治理层,每层下面又分了好几个模块。看着很专业,但一算人力投入,发现光数据治理层就得专门配两个人,还不一定跑得起来。于是我们做了一个关键决策:第一版数据体系只解决“口径统一”和“核心指标可查”两个问题,其他全部往后放。
这个决策背后的逻辑很简单。数据中台的价值不是一次性建成的,它是随着业务需求逐步长出来的。如果一开始就追求大而全,大概率会陷入“建了没人用,用了不好用,不好用又回头改”的死循环。我们当时定了一个原则:任何数据能力的建设,必须对应一个具体的业务场景,没有场景就不做。
举个例子,业务方最常问的三个问题是“昨天卖了多少”、“这个月完成率多少”、“哪个渠道效果最好”。这三个问题对应的是核心指标口径统一、日报自动化、渠道归因分析三个场景。我们就围绕这三个场景来设计第一版架构,其他的比如用户画像、推荐系统,统统排到二期。
2.2 分层设计:ODS、DWD、DWS、ADS 怎么切
虽然说是轻量,但分层还是得做。我们采用了经典的四层结构,但每一层的内容做了大幅精简:
| 分层 | 全称 | 我们放什么 | 数据量级 |
|---|---|---|---|
| ODS | 操作数据层 | 业务库的每日全量快照 | 约200张表 |
| DWD | 明细数据层 | 清洗后的订单、用户、商品明细 | 约30张核心表 |
| DWS | 汇总数据层 | 按天/按渠道/按品类的轻度汇总 | 约15张表 |
| ADS | 应用数据层 | 直接对接报表和接口的结果表 | 约20张表 |
这个分层看起来标准,但实操中有个关键取舍:ODS层要不要做全量快照。全量快照的好处是能追溯历史任意一天的状态,坏处是存储成本高。我们算了一笔账,核心业务表每天全量快照,一年下来存储成本大概在可接受范围内,但换来的追溯能力对排查数据问题非常关键。所以最终决定:核心表做每日全量快照,非核心表只做增量。
注意:全量快照一定要配合生命周期管理,否则存储会无限膨胀。我们设定了ODS层保留最近90天,超过90天的自动归档到冷存储。
2.3 技术选型:为什么没上实时计算
技术选型上,我们评估过Flink、Spark Streaming等实时计算方案,但最终第一版全部用了离线批处理。原因有三个:第一,业务方对数据时效性的要求是T+1,没有实时场景;第二,实时链路的运维复杂度是离线的三倍以上,我们人手不够;第三,离线链路的排查成本低,出了问题容易定位。
这个选择后来被证明是对的。上线三个月内,离线链路出过两次数据延迟,我们花了半小时就定位到是上游业务库的binlog同步延迟,如果是实时链路,排查时间至少翻倍。技术选型的第一原则是匹配业务需求,而不是追求技术先进性。
3. 数据建模实操:从业务过程到维度模型
3.1 怎么找到核心业务过程
数据建模的第一步不是画ER图,而是跟业务方聊天。我们花了整整一周时间,跟三条业务线的负责人分别做了深度访谈,问的问题包括:你们每天最关注哪些数字?这些数字是怎么算出来的?算的过程中哪些地方容易出错?
聊完之后,我们梳理出了四个核心业务过程:下单、支付、退款、履约。这四个过程覆盖了业务方90%以上的分析需求。每个业务过程对应一个事实表,事实表里放度量值(比如订单金额、支付金额)和维度外键(比如用户ID、商品ID、渠道ID)。
这里有个经验:不要试图一次性把所有业务过程都建模,先做最核心的两三个,跑通之后再扩展。我们第一版只做了下单和支付两个事实表,退款和履约是第二个月才加上的。
3.2 维度表设计:缓慢变化维怎么处理
维度表里最麻烦的是缓慢变化维。比如商品所属的品类,今天属于A类,下个月可能调整到B类。如果直接覆盖,历史数据按新品类回溯就会出错;如果不覆盖,又没法反映当前状态。
我们采用了拉链表的方式,给每个维度记录加上生效日期和失效日期。这样既能追溯历史,又能反映当前。具体实现上,每天跑一个维度更新任务,对比源数据和目标数据,有变化的关掉旧记录、插入新记录。
-- 拉链表更新逻辑示意 -- 1. 关掉变化的旧记录 UPDATE dim_product SET end_date = '2024-01-14', is_current = 0 WHERE product_id IN (SELECT product_id FROM ods_product WHERE updated_at = '2024-01-14') AND is_current = 1; -- 2. 插入新记录 INSERT INTO dim_product SELECT product_id, product_name, category_id, '2024-01-15', '9999-12-31', 1 FROM ods_product WHERE updated_at = '2024-01-14';这个逻辑看起来简单,但实操中有个坑:如果同一天源表里同一条记录被更新了两次,拉链表会插入两条新记录。我们的解决办法是在ODS层做去重,按主键取最新一条。
3.3 指标口径管理:一张表解决扯皮问题
口径不统一是数据体系里最让人头疼的问题。我们的做法是建一张“指标字典表”,每个指标记录名称、口径定义、计算公式、责任人、生效日期。所有报表和接口里的指标,必须引用这张表里的定义,不允许自定义。
| 指标名称 | 口径定义 | 计算公式 | 责任人 |
|---|---|---|---|
| 支付GMV | 用户实际支付金额,不含退款 | SUM(pay_amount) | 财务A |
| 订单GMV | 用户下单金额,含未支付 | SUM(order_amount) | 运营B |
| 有效订单数 | 支付成功且未全额退款的订单 | COUNT(DISTINCT order_id) | 运营B |
这张表看起来不起眼,但它是整个数据体系的“宪法”。有了它,业务方再来说“你们这个数字不对”,我们第一句话就是“您说的是哪个口径”,扯皮时间大幅缩短。
4. 数据质量保障:怎么让数据“可信”
4.1 质量监控的四个维度
数据质量不是靠人工抽查,得靠系统化的监控。我们定义了四个监控维度:完整性、准确性、一致性、及时性。每个维度对应具体的检查规则。
完整性检查:核心字段不能为空。比如订单表的订单ID、用户ID、金额字段,空值率超过0.1%就告警。
准确性检查:数值要在合理范围内。比如订单金额不能为负数,支付金额不能超过订单金额。
一致性检查:关联表之间的数据要对得上。比如DWD层的订单数应该等于ODS层去重后的订单数。
及时性检查:任务要在规定时间内完成。比如日报任务必须在早上8点前跑完,超过8点就告警。
4.2 告警机制:怎么避免“狼来了”
一开始我们设了很多告警,结果每天收到几十条,后来大家都不看了。这是个典型问题:告警太多等于没有告警。我们做了两件事来优化:
第一,分级告警。P0级告警直接打电话,P1级发群消息,P2级只记录不通知。P0的定义是“核心报表数据异常”,P1是“非核心报表异常”,P2是“数据波动但可解释”。
第二,告警收敛。同一个任务连续失败,只发一次告警,恢复后再发一次恢复通知。避免半夜被连续短信吵醒。
实操心得:告警阈值不要拍脑袋定,先跑一周观察数据分布,再根据实际波动情况设定。我们一开始把订单金额波动阈值设为5%,结果大促期间天天告警,后来改成“同比波动超过20%且绝对值超过10万”才告警,效果好很多。
4.3 数据对账:每天早上的第一件事
我们建立了一个对账机制:每天数据任务跑完后,自动对比ODS层和DWD层的核心指标,差异超过阈值就阻断下游任务。这个机制帮我们提前发现了多次上游业务库的同步问题。
对账的逻辑不复杂,就是写几条SQL做全外连接,找出两边不一致的记录。但关键是对账规则要覆盖核心链路,不能只对一两个指标。我们的对账规则覆盖了订单、支付、退款三个核心过程的八个关键指标。
5. 数据服务化:怎么让数据“好拿”
5.1 从“给数据”到“给服务”
数据体系搭好之后,业务方要数据的方式也得变。以前是“帮我导个Excel”,现在是“给我一个接口”或者“给我一个看板”。我们做了两层服务化:
第一层是API服务。把ADS层的结果表封装成RESTful接口,业务系统直接调用。比如“查询某渠道某天的支付GMV”,就是一个GET请求。
第二层是自助分析。给分析师开放DWS层的查询权限,他们可以自己写SQL做探索性分析,不用每次都找数据开发。
5.2 权限管理:谁能看什么数据
数据服务化之后,权限管理变得很重要。我们的原则是“最小必要”:业务方只能看到自己业务线的数据,跨业务线的数据需要申请。
实现上,我们在API网关层做了数据权限过滤。每个API请求都带用户身份,网关根据用户所属业务线,自动在SQL里加上过滤条件。这样业务方拿到的数据天然就是自己范围内的,不用人工审核。
5.3 查询性能优化:怎么让报表秒开
报表查询慢是常见问题。我们的优化手段有三个:
第一,预计算。把常用的汇总指标提前算好,放在ADS层。报表查询直接查结果表,不查明细。
第二,分区裁剪。所有大表都按日期分区,查询时自动裁剪掉不需要的分区。
第三,缓存。对高频查询结果做缓存,缓存有效期根据数据更新频率设定。日报数据缓存到第二天早上,实时数据缓存5分钟。
| 优化手段 | 适用场景 | 效果 |
|---|---|---|
| 预计算 | 固定维度的汇总查询 | 查询时间从30秒降到2秒 |
| 分区裁剪 | 按时间范围查询 | 扫描数据量减少90% |
| 缓存 | 高频重复查询 | 响应时间从2秒降到200毫秒 |
6. 踩坑实录与排查技巧
6.1 数据倾斜:一个让任务跑不完的坑
数据倾斜是离线任务最常见的性能问题。我们有一次跑一个按用户汇总的任务,发现99%的数据都集中在几个大用户上,导致reduce阶段卡死。
解决办法是加盐打散。给大用户的key加上随机后缀,先做局部汇总,再去掉后缀做全局汇总。具体操作:
-- 加盐打散 SELECT user_id, SUM(amount) FROM ( SELECT CONCAT(user_id, '_', CAST(RAND() * 10 AS INT)) AS salted_key, user_id, amount FROM dwd_order ) t GROUP BY salted_key, user_id;这个办法能把数据均匀分散到多个reduce任务上,任务时间从两小时降到二十分钟。
6.2 任务依赖:怎么避免“牵一发动全身”
数据任务之间有依赖关系,A任务失败会导致B、C、D都跑不了。我们一开始没有管理依赖,结果经常出现“一个任务挂了,整个链路都停”的情况。
后来我们引入了依赖管理工具,把任务之间的依赖关系显式配置。同时设置了“依赖超时”机制:如果上游任务超过预定时间还没完成,下游任务可以选择跳过或使用旧数据继续跑。
注意:依赖管理一定要配监控,否则依赖关系多了之后,排查问题会很痛苦。我们后来加了一个依赖关系可视化页面,一眼就能看出哪个任务卡住了。
6.3 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 任务跑得慢 | 数据倾斜 | 看reduce阶段耗时分布 | 加盐打散 |
| 数据对不上 | 口径不一致 | 对比指标字典 | 统一口径 |
| 报表打不开 | 查询超时 | 看查询计划 | 预计算+缓存 |
| 告警太多 | 阈值不合理 | 看告警历史 | 分级+收敛 |
| 维度回溯错误 | 拉链表逻辑问题 | 检查生效失效日期 | 修复拉链逻辑 |
6.4 独家避坑技巧
第一个技巧:所有核心任务都要有重跑机制。数据任务失败是常态,关键是能快速重跑。我们给每个任务都配了重跑按钮,并且重跑时自动清理当天数据,避免数据重复。
第二个技巧:ODS层数据不要直接给业务方用。ODS层是原始数据,没有清洗和口径统一,直接给业务方用会引发无数扯皮。我们的原则是:业务方只能看ADS层和DWS层的数据。
第三个技巧:定期做数据体系健康度检查。我们每个月做一次,检查内容包括:任务成功率、数据延迟情况、告警数量、存储增长趋势。这个检查能提前发现很多潜在问题。
7. 后续扩展方向
数据体系搭到这一步,基本能支撑日常业务需求了。但数据中台的建设没有终点,后续我们计划在三个方向继续扩展:
第一是数据血缘。现在任务之间的依赖是人工配置的,后续想通过解析SQL自动生成血缘关系,这样排查问题会更方便。
第二是数据地图。让业务方自己能搜索到有哪些数据可用,不用每次都问数据团队。
第三是成本优化。随着数据量增长,存储和计算成本会上升,需要建立成本监控和优化机制。
这些方向都不着急,等业务需求推动的时候再做。数据体系是长出来的,不是规划出来的,这个体会在做完第一版之后特别深。