简介《数据中台实战手把手教你搭建数据中台》电子书面向企业管理者、产品经理、运营设计开发人员及数据领域学习者聚焦数据中台从概念认知到落地搭建的完整路径帮助读者判断企业是否需要中台并掌握采集、存储、打通、应用四个核心环节。压缩包为PDF格式仅含1个文件体积约11.3MB便于在电脑或移动设备上阅读与检索。目前已有402人学习下载具备一定关注度。书中结合作者在大型企业参与中台从0到1建设的实战经历围绕数据埋点、指标管理、分层建模、标签平台、用户与商品流量交易分析、自助分析平台及自动化营销等模块展开配有较多案例与方法论可帮助读者理解中台建设的适用条件、实施流程和常见陷阱也为中台产品经理及企业数据化转型项目提供可操作的实践参考。1. 先别急着买引擎这份大数据中台资料该在什么阶段翻开很多团队一上大数据中台先买计算引擎、再拉埋点最后发现指标口径对不上看板越做越多决策却越来越慢。这份数据中台实战资料的价值不在概念而在它把采集、存储、打通、应用四段拆成了能照着做的工序埋点表怎么设计、ODS 到 ADS 怎么分层、标签平台怎么从宽表长出来、交易看板怎么给不同角色用。适合正在做数据化转型的产品、运营、开发和准备面试数据中台岗位的人不适合只有一条产品线、数据量还没起来的初创团队。读的时候别从第一页顺读先拿自己公司的一条业务线做对照缺哪段补哪段。2. 数据采集埋点表、业务库同步与 T-1 链路的落地拆解采集做不扎实后面所有分层建模都是空中楼阁。资料里反复强调业务中台和数据中台是两条腿前者把通用业务模块收口后者把跨产品线数据收口没有业务中台也能做数据中台但采集阶段要多跑几条产品线做调研沟通成本会高很多。落到执行采集不是“把日志丢进数仓”这么简单至少要同时管住三件事埋点事件有没有统一 schema、业务库同步有没有可重跑的 T-1 链路、采集结果有没有验收标准。2.1 埋点事件表先定 schema再谈采集埋点最容易翻车的地方是事件名和属性名各写各的同一个“提交订单”A 产品线叫submit_orderB 产品线叫order_commit属性里一个用order_id一个用orderNo。资料里的做法是先定事件命名规范再让开发按表填报。常见命名是“业务域_对象_动作”比如trade_order_submit、goods_detail_view、cart_add_click属性分公共属性和业务属性公共属性由 SDK 统一带上业务属性由埋点方案声明。事件表不要一上来就宽表化先保证一行一个事件、事件 ID 可去重、时间字段统一到服务端时间。-- 埋点事件明细表按天分区properties 存业务属性 CREATE TABLE IF NOT EXISTS ods_user_event ( event_id STRING COMMENT 事件唯一ID前端生成服务端补发, event_name STRING COMMENT 事件名域_对象_动作, user_id STRING COMMENT 登录用户ID未登录为空, anonymous_id STRING COMMENT 设备匿名ID未登录时必填, session_id STRING COMMENT 会话ID, page_id STRING COMMENT 页面ID, module_id STRING COMMENT 模块ID, platform STRING COMMENT 端web/app/mini, app_version STRING COMMENT 客户端版本, server_ts BIGINT COMMENT 服务端接收时间戳毫秒, properties STRING COMMENT JSON属性限制2KB以内, dt STRING COMMENT 分区日期 yyyy-MM-dd ) PARTITIONED BY (dt) STORED AS PARQUET;这段建表语句的关键不是语法而是把“可去重”和“可分区”提前写进 schema。event_id用来处理网络重试导致的重复上报user_id和anonymous_id至少有一个不为空否则后续无法做用户行为串联server_ts不要用客户端时间代替客户端时间容易被改properties限制大小避免有人把整个商品对象塞进埋点。分区字段dt按服务端接收日期落跨天补发的事件要单独标记否则日报会对不上。埋点方案评审时我一般会强迫产品把下面这张表填完填不完就不排期。事件名、触发时机、必填属性、属性类型、责任人、验收指标这六列缺一不可。尤其是“触发时机”要写到“点击提交按钮且接口返回成功后上报”不能写“用户下单时”。很多转化率异常不是数仓算错而是前端在按钮点击时就上报接口失败也算了一笔。字段示例约束事件名trade_order_submit小写字母下划线最长 48 字符触发时机提交订单接口返回成功不能写“用户下单时”必填属性order_id、pay_amount、goods_id空值率超过 1% 阻断发布属性类型pay_amount为 decimal(16,2)金额统一单位元责任人某业务线产品负责人变更需同步数据团队验收指标日事件量、UV、空值率、重复率发布后 24 小时看板核对埋点验收不要靠人眼看日志直接跑 SQL 看四个数事件量、去重后事件量、必填属性空值率、event_id重复率。下面这段查询按事件名聚合适合放进每日调度超过阈值就告警。参数上dt取业务日期event_name可以先限定核心转化事件不用全量跑重复率阈值我一般设 0.5%空值率设 1%超过就回滚 SDK 版本或修埋点方案不要先去改数仓清洗逻辑。-- 埋点验收核心事件量、空值率、重复率 SELECT event_name, COUNT(1) AS pv, COUNT(DISTINCT event_id) AS pv_dedup, ROUND(1 - COUNT(DISTINCT event_id) / COUNT(1), 4) AS dup_rate, ROUND(SUM(CASE WHEN user_id IS NULL AND anonymous_id IS NULL THEN 1 ELSE 0 END) / COUNT(1), 4) AS id_null_rate, ROUND(SUM(CASE WHEN properties IS NULL OR properties THEN 1 ELSE 0 END) / COUNT(1), 4) AS prop_null_rate FROM ods_user_event WHERE dt ${biz_date} AND event_name IN (trade_order_submit, goods_detail_view, cart_add_click) GROUP BY event_name;提示埋点验收不通过时先回滚 SDK 版本再查网络重试和页面曝光重复上报不要直接改数仓逻辑把异常藏掉。2.2 业务库同步与日志采集的 T-1 链路业务库数据是交易、订单、商品、用户这些“结果数据”埋点日志是浏览、点击、加购这些“过程数据”。资料里把采集链路分成两条业务库走实时同步加 T-1 全量校对日志走消息队列落地后按天分区。常见做法是业务库变更用 binlog 追增量每天凌晨再跑一次全量或增量校对防止漏数日志采集在前端埋点后先落消息队列再离线落到对象存储或分布式文件系统按dt分区。两条链路最后都收敛到 ODS 层表名和责任人登记到元数据表里。调度脚本不要写成“一把梭”至少把业务日期、任务名、重跑标记、失败告警四个参数留出来。下面是一个通用 T-1 同步脚本骨架job_config指向同步任务配置biz_date由调度系统传入rerun用来控制是否覆盖分区。脚本里先做源表行数探查再执行同步最后写一条元数据日志这样出问题时能查到是源表空了还是同步任务失败了。#!/bin/bash set -euo pipefail BIZ_DATE${1:?业务日期不能为空格式 yyyy-MM-dd} JOB_CONFIG${2:?同步任务配置不能为空} RERUN${3:-false} # 1. 同步前探查源表行数结果写入临时文件 python /opt/sync/bin/check_source.py \ --config ${JOB_CONFIG} \ --biz-date ${BIZ_DATE} \ --output /tmp/source_count_${BIZ_DATE}.json # 2. 执行同步reruntrue 时覆盖目标分区 python /opt/sync/bin/run_job.py \ --config ${JOB_CONFIG} \ --biz-date ${BIZ_DATE} \ --mode overwrite \ --rerun ${RERUN} # 3. 写元数据日志任务名、业务日期、源行数、目标行数、状态 python /opt/sync/bin/write_meta.py \ --job ${JOB_CONFIG} \ --biz-date ${BIZ_DATE} \ --status success参数说明上BIZ_DATE是业务日期不是执行日期很多团队凌晨跑数时把执行日期当业务日期导致 T-1 数据错位一天。RERUN只在补数时传true日常调度传false避免误覆盖。同步任务配置里要明确源表、目标表、主键、增量字段、分区字段、字段映射主键用于去重增量字段用于追变更分区字段用于重跑。日志采集侧则要在 ODS 建外部表或分区表按dt挂载不要每天改表结构。采集完成后必须登记元数据不然三个月后没人知道ods_order是谁负责、几点更新、下游有谁用。资料里把中台团队拆成十类角色从负责人、产品、数据开发、分析师到测试和运维采集阶段至少要有业务责任人、数据开发责任人、运维责任人三方签字。元数据表不用复杂下面这几列就够用表名、来源系统、更新频率、SLA、主键、责任人、下游任务。缺元数据的表我一般不允许进 DWD宁可先卡在 ODS。元数据项示例用途表名ods_order唯一标识来源系统某业务线订单库排查源端问题更新频率每日 T-109:00 前约定 SLA主键order_id去重与对账责任人某数据开发变更联系人下游任务dwd_trade_order影响面分析2.3 采集验收量、质、时三层卡点采集验收要卡三层量、质、时。量看日环比和周同比核心事件量突然掉 30% 或涨 50% 都要查质看主键重复、必填字段空值、枚举值越界时看 SLA比如每天 09:00 前 ODS 分区必须就绪晚了就触发告警。资料里很多实战案例都指向一个结论采集阶段放过的小问题到应用层会变成大问题。比如订单表里混入测试订单交易看板会多算埋点里platform出现unknown流量分析会少一块日志补发没有标记日报和实时看板会对不上。采集验收 SQL 可以固定成模板每个 ODS 表接进来时改表名和主键即可。下面这段检查主键重复、分区行数、关键字段空值结果输出到质量表调度系统根据status决定是否阻断下游。参数上pk换成实际主键dt用业务日期min_rows设一个业务可接受的下限比如订单表日行数不低于 1000低于就告警而不是直接阻断避免节假日误杀。-- 采集质量检查主键重复、行数下限、关键字段空值 SELECT ods_order AS table_name, ${biz_date} AS dt, COUNT(1) AS row_cnt, COUNT(1) - COUNT(DISTINCT order_id) AS pk_dup_cnt, SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) AS pk_null_cnt, SUM(CASE WHEN pay_amount IS NULL THEN 1 ELSE 0 END) AS amount_null_cnt, CASE WHEN COUNT(1) 1000 THEN WARN WHEN COUNT(1) - COUNT(DISTINCT order_id) 0 THEN FAIL WHEN SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) 0 THEN FAIL ELSE PASS END AS status FROM ods_order WHERE dt ${biz_date};这一层做完采集才算有“黑匣子”可查。后面指标对不上时先看 ODS 行数和主键再看 DWD 清洗规则最后才怀疑汇总逻辑。顺序反了会浪费很多时间。3. 存储与计算ODS 到 ADS 的分层建模和指标口径怎么落表分层建模最怕做成表名游戏ODS、DWD、DWS 建了一堆业务一问“昨天支付金额为什么和财务对不上”还是答不出来。资料里的分层不是照搬概念而是把“原始数据、清洗明细、主题汇总、应用结果”分开让每层只干一件事。ODS 保留原样方便回溯DWD 做清洗、去重、统一字段DWS 按主题做轻度汇总ADS 面向看板和接口。层级越往下越稳定越往上越贴近业务变更频率也越高。3.1 分层边界每层只做该做的事ODS 层不要做复杂清洗尤其不要在这里过滤测试订单或修改金额否则回溯时找不到原始值。DWD 层负责把 ODS 的脏数据变成可信明细去重、过滤测试账号、统一金额单位、补全维度、解析 JSON 属性。DWS 层按主题汇总比如交易主题按日、品类、渠道汇总支付金额和退款金额用户主题按日汇总活跃、下单、复购。ADS 层直接服务看板和接口可以宽表化但要在指标口径固定之后再做。选型上离线链路优先保证可重跑和可对账实时链路只承接真正需要秒级响应的场景。资料里把采集、存储、打通、应用串起来但并没有鼓励所有表都实时化。交易看板 T1 足够实时大屏才需要流式计算标签平台大部分标签 T1 更新只有“最近 1 小时活跃”这类标签走实时。分层表建议统一按天分区小文件多的任务加合并步骤冷数据按保留周期归档避免存储成本失控。层级职责典型表更新频率禁止事项ODS原样落地、可回溯ods_order、ods_user_event每日/实时不做业务过滤DWD清洗、去重、统一口径dwd_trade_order每日不直接出看板DWS主题汇总、轻度聚合dws_trade_day每日不堆过多维度ADS看板、接口、人群包ads_trade_board每日/按需不反写明细注意指标口径变更必须留版本否则历史看板会集体失真。DWD 的过滤条件一改DWS 和 ADS 都要重跑不能只改最上层。3.2 指标管理体系一个指标一条 YAML指标口径对不上根因通常不是 SQL 写错而是同一个词在不同团队嘴里含义不同。资料里强调指标要有业务口径、技术口径、计算周期、计算方式、责任人。我的做法是一个指标一条 YAML进 Git 管理变更走评审。指标码用“域_主题_度量_周期”比如trade_order_pay_amt_1d业务口径写清楚“支付成功订单金额扣除退款不含测试订单”技术口径写清楚源表、过滤条件、去重主键、金额单位维度列出常用切分比如日期、品类、渠道、产品线。metric_code: trade_order_pay_amt_1d name: 日支付订单金额 biz_caliber: 支付成功订单金额扣除当日退款不含测试订单和风控拦截订单 tech_caliber: dwd_trade_order 按 order_id 去重 pay_status PAID order_type TEST pay_amount 单位为元保留两位小数 owner: 某交易数据负责人 period: 1d dimensions: - dt - category_id - channel_id - product_line source_table: dwd_trade_order filter: pay_status PAID AND order_type TEST version: v1.2这份 YAML 的关键是“可执行”。biz_caliber不是写给领导看的而是给开发和分析师对照的tech_caliber要能直接翻译成 SQLdimensions决定 DWS 建哪些汇总维度version让历史看板能追到口径版本。指标变更时不要直接改原文件新增版本并保留旧版本ADS 表按版本号切换。否则一个“支付金额”改了口径历史报表全部对不上业务方会直接不信数据。指标管理还有一条血泪经验不要让每个看板自己写 SQL 算指标。看板只消费 ADS 或指标服务指标服务背后统一走 YAML 定义。这样新看板接入时不用重新讨论口径直接选指标码和维度即可。自助分析平台也一样业务人员可以拖拽维度但指标必须是注册过的不能让他们在页面上随便写sum(pay_amount)。3.3 从 ODS 到 ADS 的 SQL 骨架先看 DWD 清洗。下面这段 SQL 从 ODS 订单表取数按order_id去重过滤测试订单统一金额单位补上品类和渠道维度。参数上dt是业务日期row_number()按update_time倒序取最新一条处理业务库同一订单多次变更pay_amount如果源表是分要除以 100这里假设源表已经是元。测试订单的识别条件用order_type TEST不要用订单号前缀猜规则要写在指标 YAML 里。-- DWD 订单明细去重、过滤测试单、统一金额单位 INSERT OVERWRITE TABLE dwd_trade_order PARTITION (dt ${biz_date}) SELECT order_id, user_id, product_line, category_id, channel_id, pay_status, pay_amount, refund_amount, create_time, update_time FROM ( SELECT order_id, user_id, product_line, category_id, channel_id, pay_status, CAST(pay_amount AS DECIMAL(16,2)) AS pay_amount, CAST(refund_amount AS DECIMAL(16,2)) AS refund_amount, create_time, update_time, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY update_time DESC) AS rn FROM ods_order WHERE dt ${biz_date} AND order_type TEST ) t WHERE rn 1;DWS 汇总按日、品类、渠道、产品线聚合支付金额和退款金额分开算最后在 ADS 里做净额。这样退款政策变化时不用重跑 DWD只改汇总逻辑。下面这段 SQL 的pay_amt只统计支付成功订单refund_amt统计当日退款net_amt是净额。参数上dt分区对齐category_id和channel_id允许为空空值单独归到“未知”维度不要直接丢掉否则大盘对不上。-- DWS 交易日汇总按品类、渠道、产品线 INSERT OVERWRITE TABLE dws_trade_day PARTITION (dt ${biz_date}) SELECT ${biz_date} AS dt, product_line, COALESCE(category_id, UNKNOWN) AS category_id, COALESCE(channel_id, UNKNOWN) AS channel_id, COUNT(DISTINCT CASE WHEN pay_status PAID THEN order_id END) AS pay_order_cnt, SUM(CASE WHEN pay_status PAID THEN pay_amount ELSE 0 END) AS pay_amt, SUM(CASE WHEN refund_amount 0 THEN refund_amount ELSE 0 END) AS refund_amt, SUM(CASE WHEN pay_status PAID THEN pay_amount ELSE 0 END) - SUM(CASE WHEN refund_amount 0 THEN refund_amount ELSE 0 END) AS net_amt FROM dwd_trade_order WHERE dt ${biz_date} GROUP BY product_line, COALESCE(category_id, UNKNOWN), COALESCE(channel_id, UNKNOWN);ADS 看板表可以直接从 DWS 取数按角色拆指标。领导层看大盘净额、同比环比运营看品类、渠道、退款率产品看转化漏斗。下面这段只做一张交易看板样例net_amt和refund_rate是核心指标。参数上dt用业务日期refund_rate分母为 0 时要返回 0避免除零同比环比可以在调度里另算不要在看板查询时实时算否则数据量大时页面会卡。-- ADS 交易看板大盘核心指标 INSERT OVERWRITE TABLE ads_trade_board PARTITION (dt ${biz_date}) SELECT dt, SUM(pay_amt) AS pay_amt, SUM(refund_amt) AS refund_amt, SUM(net_amt) AS net_amt, SUM(pay_order_cnt) AS pay_order_cnt, CASE WHEN SUM(pay_amt) 0 THEN 0 ELSE ROUND(SUM(refund_amt) / SUM(pay_amt), 4) END AS refund_rate FROM dws_trade_day WHERE dt ${biz_date} GROUP BY dt;跑完还要做对账。至少查三项DWD 主键是否重复、DWS 汇总金额是否等于 DWD 明细金额、ADS 展示金额是否等于 DWS 汇总金额。下面这段对账 SQL 输出差异行差异不为 0 就阻断下游看板发布。参数上${biz_date}同一天金额比较允许 0.01 的浮点误差超过就查清洗规则或汇总维度是否丢数。-- 交易链路对账DWD 明细 vs DWS 汇总 SELECT d.dt, d.pay_amt AS dwd_pay_amt, s.pay_amt AS dws_pay_amt, d.pay_amt - s.pay_amt AS diff_amt FROM ( SELECT dt, SUM(pay_amount) AS pay_amt FROM dwd_trade_order WHERE dt ${biz_date} AND pay_status PAID GROUP BY dt ) d LEFT JOIN ( SELECT dt, SUM(pay_amt) AS pay_amt FROM dws_trade_day WHERE dt ${biz_date} GROUP BY dt ) s ON d.dt s.dt WHERE ABS(COALESCE(d.pay_amt,0) - COALESCE(s.pay_amt,0)) 0.01;4. 数据打通标签平台从宽表到人群圈选的最小闭环标签平台不是把用户属性拼成一张大宽表就完事真正难的是身份映射和标签更新节奏。资料里把标签平台放在“打通”环节是因为用户行为数据和业务数据往往存在不同产品线、不同系统里行为数据知道用户点了什么业务数据知道用户买了什么两边对不上用户画像就是残缺的。打通的目标不是把所有字段都塞进一张表而是让“用户是谁、干了什么、买了什么”能在同一个用户 ID 下被查询和圈选。4.1 标签体系先分三层基础、行为、业务标签体系建议先分三层基础标签、行为标签、业务标签。基础标签是注册天数、会员等级、所在城市等级这类变化慢的属性行为标签是近 30 天点击品类数、近 7 天访问频次、最近一次活跃时间业务标签是首单金额、近 90 天订单数、退款次数。每类标签都要有元数据标签码、名称、类型、更新方式、源表、责任人、有效期。不要一上来做几百个标签先做 20 个业务真用的跑通“宽表生成—标签计算—人群圈选—营销调用”闭环。宽表按天分区只放常用标签长尾标签按需计算。下面建表语句把用户 ID、基础标签、行为标签、业务标签放在一张日分区表里dt是标签日期user_id是主键。参数上标签列允许为空空值代表“未知”而不是“否”更新方式用全量覆盖避免增量 merge 写错导致标签漂移。宽表字段不要无限扩超过 100 列就拆成基础宽表和行为宽表否则查询和跑批都会变慢。-- 用户标签宽表按天分区常用标签放一张表 CREATE TABLE IF NOT EXISTS dws_user_tag_wide ( user_id STRING COMMENT 统一用户ID, register_days INT COMMENT 注册天数, member_level STRING COMMENT 会员等级, last_active_time STRING COMMENT 最近活跃时间, click_category_cnt_30d INT COMMENT 近30天点击品类数, visit_cnt_7d INT COMMENT 近7天访问频次, first_order_amt DECIMAL(16,2) COMMENT 首单金额, order_cnt_90d INT COMMENT 近90天订单数, refund_cnt_90d INT COMMENT 近90天退款次数, dt STRING COMMENT 标签日期 yyyy-MM-dd ) PARTITIONED BY (dt) STORED AS PARQUET;标签计算不要每个标签一条独立任务按源表分组批量算。行为标签从 DWD 事件表算业务标签从 DWD 订单表算基础标签从用户维度表算最后按user_id全外连接。连接时注意空值行为表里没有行为的用户也要保留否则宽表只剩活跃用户圈选“沉默用户”就圈不出来。标签有效期也要写进元数据比如“近 30 天点击品类数”每天重算“会员等级”每天同步过期标签要标记不可用。4.2 身份映射打通产品线数据的硬骨头跨产品线打通最难的是身份映射。同一个用户在产品线 A 用手机号注册在产品线 B 用设备号浏览在 product_line C 用会员 ID 下单如果没有映射表三个 ID 就是三个人。常见做法是建一张身份映射表字段包括统一用户 ID、手机号哈希、设备 ID、会员 ID、来源产品线、最近活跃时间、分区日期。优先级一般是会员 ID 高于手机号哈希手机号哈希高于设备 ID冲突时按最近活跃时间保留人工可修正。手机号只存哈希不落明文设备 ID 也要做脱敏。-- 身份映射表多产品线 ID 归一到 union_id CREATE TABLE IF NOT EXISTS dim_user_identity ( union_id STRING COMMENT 统一用户ID, user_id STRING COMMENT 产品线内用户ID, phone_hash STRING COMMENT 手机号哈希不存明文, device_id STRING COMMENT 设备ID脱敏后存储, member_id STRING COMMENT 会员ID, product_line STRING COMMENT 来源产品线, last_active STRING COMMENT 最近活跃时间, dt STRING COMMENT 分区日期 ) PARTITIONED BY (dt) STORED AS PARQUET;映射逻辑分两步先按强 ID 配对手机号哈希相同或会员 ID 相同就归到同一个union_id再用设备 ID 补弱关联但设备 ID 冲突多不能单独作为唯一依据。同一设备多账号、同一手机号多产品线都要保留映射明细不能直接覆盖。下面这段 SQL 是归一的骨架ROW_NUMBER()按last_active倒序取最近一条COALESCE按优先级选 ID。参数上dt用映射日期phone_hash要统一哈希算法和盐值否则跨产品线对不上。-- 身份归一强ID优先设备ID兜底 INSERT OVERWRITE TABLE dim_user_identity PARTITION (dt ${biz_date}) SELECT CONCAT(U, MD5(CONCAT(COALESCE(phone_hash, member_id, device_id), ${salt}))) AS union_id, user_id, phone_hash, device_id, member_id, product_line, last_active, ${biz_date} AS dt FROM ( SELECT user_id, phone_hash, device_id, member_id, product_line, last_active, ROW_NUMBER() OVER ( PARTITION BY COALESCE(phone_hash, member_id, device_id) ORDER BY last_active DESC ) AS rn FROM ods_user_identity WHERE dt ${biz_date} ) t WHERE rn 1;提示身份映射表一定要保留来源产品线和最近活跃时间后续本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?