AI经营分析数据中台如何搭建数仓

从 0 到 1:我用 MaxCompute + Hologres + DataWorks 搭 AI 经营分析数据中台

先把话说死:经营分析难的不是 SQL,是口径。GMV 含不含退款、订单算下单还是算支付、跨天订单算哪一天、门店券和平台券算谁的成本------这些没对齐,后面模型再漂亮也是错的。数仓要干的,就是把口径钉死,把数据按时、按分区、按质量交出来。AI 只是这层之上的消费者。

三个产品的分工,我直接给结论:

  • MaxCompute:离线仓。存得起、算得起、扫得起。ODS / DWD / DWS / DIM 主要落在这里。
  • Hologres:在线仓。给 BI、给 API、给 Agent 问数。热数据和高频指标放这里。
  • DataWorks:人干活的地方。同步、开发、调度、质量、服务,全在这一套里编排。

别把 Hologres 当 Hadoop,也别拿 MaxCompute 去扛 200 QPS 的看板刷新。混了,账单和延迟会一起爆炸。

如果你也在做类似项目,可以把我这篇当成一条可复制的路线:

开通并对齐账号 → 分层和命名钉死 → 业务库进 ODS → MaxCompute 做清洗汇总 → 该加速的用外表、该服务的导入 Hologres → DataWorks 把 DAG 和质量规则挂上 → 用数据服务把指标交给应用。


目录

  1. 背景:经营分析中台到底要什么
  2. 三个产品各自干什么,别混
  3. 账号、地域、工作空间,开工前先对齐
  4. 分层、命名、生命周期,先写进规范再建表
  5. 数据接入:业务库怎么进 MaxCompute
  6. MaxCompute 离线加工:从 ODS 到 DWS
  7. Hologres:外表加速 vs 内部表服务
  8. DataWorks 调度:把链路变成每天能跑的 DAG
  9. 数据质量:没规则就别发布
  10. 数据服务:给 AI 和报表一个稳定出口
  11. 权限、成本、监控
  12. 我踩过的坑
  13. 建议的实施顺序

1. 背景:经营分析中台到底要什么

我这套中台服务的场景很具体:老板和运营要看生意,算法和 Agent 要取数。两边问的问题不一样,底层表最好是一套。

业务侧常见的问题:

  • 昨天全渠道实收多少,比上周怎么样
  • 哪个门店、哪个渠道、哪类商品在拖后腿
  • 投放花了钱,ROI 算得清不清楚
  • 库存周转和滞销,别等月底财务才发现

AI 侧常见的用法:

  • 自然语言问数(最终还是打到指标表 / API)
  • 异常检测(日指标偏离基线)
  • 归因和拆解(维度下钻,不是让大模型自己编数字)
  • 给推荐 / 预测提供离线特征

所以中台要同时满足三件事:

诉求 谁来扛 时效
历史可回溯、口径可复算、成本可控 MaxCompute T+1,个别小时级
看板、问数、点查,秒级返回 Hologres 秒到亚秒
任务能排、出错能追、表能管 DataWorks 贯穿全程

第一版我没上 Flink。不是 Flink 不好,是经营分析 80% 的指标,T+1 或 10 分钟调度就够用。实时大屏、风控点查以后再加。这不是「理论上最优」,而是交付速度、口径稳定、运维人数三者平衡后的结果。


2. 三个产品各自干什么,别混

2.1 MaxCompute 是仓,不是服务

MaxCompute(以前叫 ODPS)做三件事就够了:存、算、分区管理。

适合放这里的:

  • 业务库每日全量 / 增量快照
  • 明细清洗后的 DWD
  • 按天、按店、按品类滚起来的 DWS
  • 维度表、码表
  • 给算法用的离线宽表、样本表

不适合放这里的:

  • 前端直接查、QPS 不稳定的接口
  • 「点一下就出图」的自助分析(可以跑,体验很差)
  • 需要主键更新、行级点查的在线业务

计费心里要有数:存储按量,计算按 CU 或按 SQL 扫描。分区裁剪没写好,一次 SELECT * 能把当天预算打穿。

2.2 Hologres 是服务层,顺便能加速仓

Hologres 走 PostgreSQL 协议,BI、JDBC、DataWorks 数据服务都能接。它和 MaxCompute 是亲兄弟,这点是我选它而不是单独再上一套 OLAP 的原因。

两种用法必须分开:

用法 数据在哪 适合什么
外表(Foreign Table) 还在 MaxCompute 即席分析、对账、偶尔下钻,QPS 低
内部表(Internal Table) 拷进 Hologres 看板、API、Agent 问数,要稳、要快

外表查询有缓存,MaxCompute 表更新后,Hologres 侧元数据有延迟(文档写的是大约 10 分钟内)。导入内部表之前,先 IMPORT FOREIGN SCHEMA 刷一下,别拿着旧分区当新数据。

内部表才有分布键、聚簇、bitmap 这些索引。外表没有。拿外表去扛高并发,是我见过最常见的误用。

2.3 DataWorks 是工位,不是引擎

引擎是 MaxCompute 和 Hologres。DataWorks 负责:

  • 数据集成(离线同步、整库实时、全增量)
  • 数据开发(ODPS SQL、Hologres SQL、Python、检查点)
  • 调度运维(周期、依赖、补数据、告警)
  • 数据质量(规则挂在节点上,失败能阻断)
  • 数据服务(把 SQL 封成 API)
  • 数据地图 / 血缘(后期治理用)

新项目资源组我直接买 Serverless 资源组,同步、调度、数据服务共用,按 CU 分配。旧的独享资源组还能用,官方已经不推荐新购了。


3. 账号、地域、工作空间,开工前先对齐

这一节最无聊,也最容易返工。地域选错、RAM 没授对,后面全是联不通。

3.1 开通顺序

按这个顺序来,少走弯路:

  1. 确认阿里云主账号,地域选定(后面 MaxCompute、Hologres、DataWorks 必须同一地域)。经营数据一般选离业务库近的,比如业务 RDS 在华东 2,大数据也放华东 2。
  2. 开通 MaxCompute。新项目建议打开 三层模型 (Project / Schema / Table)和 2.0 数据类型。Decimal、TIMESTAMP 这些,同步和 Hologres 外表都依赖 2.0。
  3. 开通 Hologres 实例。规格按「先能跑、再压测」买,别一上来就上很大的计算组。开发、生产尽量两个实例,至少两个 Database。
  4. 开通 DataWorks。标准版够用日常开发;要质量规则阻断、数据服务生产发布,专业版更省心。创建工作空间时绑定刚才的 MaxCompute 项目和 Hologres 实例。
  5. 买 Serverless 资源组,绑定工作空间,再绑 VPC。资源组要能访问 RDS / PolarDB 所在 VPC,否则同步任务连通性测试直接红。

3.2 RAM 别图省事

至少拆三个角色,别所有人用主账号:

角色 干什么 不要给什么
数据开发 DataWorks 开发环境写 SQL、提交 生产表 DROP、RAM 管理
运维 / 值班 运维中心重跑、补数据、看日志 改表结构随便发生产
只读分析 查 Hologres 分析库、查数据地图 写任务、改同步

MaxCompute 用 ACL 或 Policy 控到 Schema 一级。Hologres 用 PG 的 GRANT SELECT ON SCHEMA ...。DataWorks 工作空间成员和引擎账号不是一回事,两边都要加。

Hologres 加速 MaxCompute,需要 Hologres 实例账号对 MaxCompute 项目有读权限。漏了这一步,外表一查就是权限错误,排查能浪费半天。

3.3 工作空间建议标准模式

DataWorks 工作空间用 标准模式(开发 / 生产隔离):

  • 开发环境:mc_biz_dev,随便试、随便 overwrite
  • 生产环境:mc_biz_prod,只能通过发布过去

简单模式省事,三个月后一定后悔。任务直接在生产改,口径一改,昨天的报表和今天的对不上,你都不知道谁发的。


4. 分层、命名、生命周期,先写进规范再建表

表一旦铺开,改名比改代码疼。我现在的习惯是:规范先写成一页纸,建第一张表之前全员对齐。

4.1 分层怎么切

经营分析我用五层,不多不少:

放什么 加工原则 主要引擎
ODS 贴源,字段尽量原样,可加 etl 时间、源表名 不改业务含义 MaxCompute
DIM 商品、门店、渠道、用户、日历 缓慢变化按需拉链,大部分日全量即可 MaxCompute
DWD 一笔订单、一次支付、一条退款,事实已对齐口径 清洗、去重、统一编码、关联维度键 MaxCompute
DWS 按天 × 店 × 渠道 × 品类 的轻度汇总 可加可回滚,禁止在这一层发明新口径 MaxCompute,热表同步 Hologres
ADS 直接给报表 / API / Agent 的宽表或指标表 面向应用,允许适度冗余 Hologres 为主,MaxCompute 留底

有人喜欢再拆一层 STG、一层 MID。团队小于 5 个数据开发,别拆那么细,调度依赖会把人拖死。

Hologres 官方也说过:查询引擎够快的时候,不必为每个报表堆一张 ADS。我认同,但有前提------DWS 粒度对、Hologres 资源够、查询模式可预期。经营看板里那 20 个核心指标,我还是物化成表。View 改口径快,高峰期扛不住。

4.2 命名,写死一套就别换

库 / Schema:

复制代码
ods / dim / dwd / dws / ads / tmp

MaxCompute 三层模型下:

复制代码
mc_biz_prod.ods.ods_trd_order_di
mc_biz_prod.dwd.dwd_trd_order_di
mc_biz_prod.dws.dws_trd_shop_nd
mc_biz_prod.ads.ads_ai_biz_overview_1d

表名约定:

复制代码
{层}_{数据域}_{业务过程}_{刷新周期}
后缀 含义
_di 增量,一天一份增量分区
_df 全量快照,每天一份全量分区
_nd 轻度汇总,按天分区
_1d / _td 最近一天 / 截至当天累计

数据域我按经营分析常用的切:

  • trd 交易
  • usr 用户
  • itm 商品
  • mkt 营销投放
  • inv 库存
  • fin 财务(毛利、费用,口径要跟财务一起签)
  • chn 渠道 / 门店

字段:

  • 主键、外键用 xxx_id
  • 金额统一 DECIMAL(18,2),单位人民币元,别有的表分、有的表元
  • 业务日期分区一律 ds STRING,格式 yyyyMMdd
  • 技术字段:gmt_creategmt_modifiedis_deletedetl_time

分区字段用 STRING,别用 BIGINT。DataWorks 的 ${bizdate} 是字符串,Hologres 分区也更认文本 / DATE。类型一拧巴,同步映射、外表、补数据全遭殃。

4.3 生命周期,建表那天就带上

MaxCompute 分区表的生命周期按 整表 设,回收按 分区 走。某天分区过期被删,表还在。

我用的默认值(按你们合规要求改,别照抄年限):

LIFECYCLE 理由
ODS 明细 90 ~ 180 天 源库自己有备份,仓里留够回溯和重跑
DWD 365 天或更长 口径对齐后的明细,重算成本高
DIM 日快照 180 天 太久的快照很少有人翻
DWS 730 天 经营同比至少看两年
ADS 底表 按应用,通常 365+ 给算法的样本另算
tmp 3 ~ 7 天 不设的话,临时表会把存储吃满
sql 复制代码
CREATE TABLE IF NOT EXISTS ods.ods_trd_order_di
(
    order_id        STRING        COMMENT '订单号',
    user_id         STRING        COMMENT '用户ID',
    shop_id         STRING        COMMENT '门店ID',
    sku_id          STRING        COMMENT 'SKU',
    order_status    STRING        COMMENT '订单状态源值',
    pay_amount      DECIMAL(18,2) COMMENT '实付金额-元',
    gmt_create      DATETIME      COMMENT '源库创建时间',
    gmt_modified    DATETIME      COMMENT '源库修改时间',
    is_deleted      BIGINT        COMMENT '源库删除标记',
    etl_time        DATETIME      COMMENT '入仓时间'
)
COMMENT '交易订单-增量贴源'
PARTITIONED BY (ds STRING COMMENT '业务日期yyyyMMdd')
LIFECYCLE 180;

Hologres 内部表用 time_to_live_in_seconds,单位是秒。热数据留 90 天:90 * 86400 = 7776000。历史下钻走 MaxCompute 外表,别让 Hologres 变成第二个冷仓。


5. 数据接入:业务库怎么进 MaxCompute

5.1 先盘源,再开同步

经营分析最小闭环,我建议先吃这几张表,不要一上来整库 200 张一起进:

源表(举例) 进仓表 同步策略
order / order_item ods_trd_order_diods_trd_order_item_di 日增量,条件用 gmt_modified
payment / refund ods_trd_pay_diods_trd_refund_di 日增量
sku / category ods_itm_sku_df 日全量
shop / channel ods_chn_shop_df 日全量
user ods_usr_user_df 日全量或增量,看量
投放日报(文件或 API) ods_mkt_ad_cost_di 日全量覆盖

维表日全量,事实表日增量。维表不大,全量省心,不用处理迟到变更。事实表走增量,条件必须是 源库有索引的时间字段

增量 SQL 条件在 DataWorks 里写成:

sql 复制代码
gmt_modified >= '${bizdate} 00:00:00'
AND gmt_modified < '${bizdate1} 00:00:00'

${bizdate} 是调度业务日期,默认 T-1,格式 yyyyMMdd${bizdate1} 要自己在节点参数里算第二天,或者用 DATEADD。千万别写 gmt_modified >= NOW() - 1,补数据、重跑全部错。

有物理删除、源表又没有可靠 gmt_modified 的,别硬做增量,改日全量,或者上整库实时(Binlog)。

5.2 数据源和连通性

DataWorks → 管理中心 → 数据源:

  1. 加 MySQL / PolarDB,填 VPC 内网地址,不要填公网
  2. 加 MaxCompute,绑定工作空间默认项目。
  3. 加 Hologres,填实例、库名、开发 / 生产分别配。
  4. 选 Serverless 资源组,点连通性测试。红了先查:安全组、白名单、RDS 是否允许资源组网段、跨 VPC 有没有对等连接。

RDS 白名单要放资源组弹性网卡的地址段。资源组扩容后 IP 可能变,用 安全组放行资源组所属交换机 比写死几个 IP 稳。

5.3 离线同步:一张表怎么配

数据集成 → 新建同步任务,单表离线即可覆盖第一期。

来源 MySQL,去向 MaxCompute。字段映射逐列看一遍,不要全靠自动:

  • tinyint(1) 当布尔,进仓用 BIGINT,后面 SQL 里 is_deleted = 1
  • datetimeDATETIME,不要先转 STRING 再转回来
  • 金额字段确认单位,源库如果是分,ODS 可以原样留,DWD 再 / 100。我更倾向 ODS 原样、DWD 统一成元,方便对账

目标表分区字段 ds,赋值 ${bizdate}。写入模式:覆盖分区insert overwrite 语义)。增量任务也覆盖当天分区,不要 insert into 往同一个分区里追加------重跑一次就双倍。

通道设置:脏数据阈值先给一个明确数字,比如 0 或 100。阈值设成 -1 等于放弃质检。

5.4 什么时候上整库实时

下面几种情况再上实时,第一期真没必要:

  • 源表变更很频繁,日增量对不上
  • 经营看板要「今天到目前为止」
  • 有人已经在用 Binlog 做其他系统,你只是多一个订阅方

整库实时注意:

  • MaxCompute 必须 2.0 数据类型
  • 目标表建议带主键的 Delta 能力(按产品当前形态选 PK 表),否则 UPDATE / DELETE 落不干净
  • DDL 策略先设成忽略加列以外的破坏性变更,避免源库 DBA 改个字段把同步打挂
  • MySQL 要开 Binlog,binlog_format=ROW,账号有 REPLICATION 权限

实时进仓之后,离线 DWD 仍然按天跑。实时链路给 Hologres 热查询,离线链路给对账和复杂汇总。两套口径必须用同一套代码或同一份指标定义,不然「实时数」和「T+1 数」会打起来。

5.5 文件和埋点

投放报表经常是运营丢 CSV 到 OSS。DataWorks 用 OSS → MaxCompute 离线同步,或者 MaxCompute 外部表直接读 OSS。

sql 复制代码
CREATE EXTERNAL TABLE IF NOT EXISTS ods.ods_mkt_ad_cost_di
(
    account_id   STRING,
    campaign_id  STRING,
    cost_amt     DECIMAL(18,2),
    impress      BIGINT,
    click        BIGINT
)
STORED AS TEXTFILE
LOCATION 'oss://biz-data/ods/mkt_ad_cost/'
LIFECYCLE 180;

外部表省存储拷贝,但 Schema 一变就炸。稳定之后,还是 INSERT OVERWRITE 进内部表,调度好管。


6. MaxCompute 离线加工:从 ODS 到 DWS

6.1 DIM:先把维度做成「能 JOIN 的」

商品、门店、渠道没有统一 ID,后面所有汇总都是错的。

日全量维表示例:

sql 复制代码
INSERT OVERWRITE TABLE dim.dim_itm_sku_df PARTITION (ds = '${bizdate}')
SELECT
    sku_id,
    sku_name,
    spu_id,
    cate_id_l1,
    cate_name_l1,
    cate_id_l2,
    cate_name_l2,
    brand_id,
    brand_name,
    GETDATE() AS etl_time
FROM ods.ods_itm_sku_df
WHERE ds = '${bizdate}'
  AND NVL(is_deleted, 0) = 0;

日历维自己造,别每次用日期函数现场算:

sql 复制代码
-- dim_pub_date 一次生成 10 年即可,按 ds 或 date_key 点查
-- 字段至少:date_key, date_s, week_id, month_id, is_weekend, is_holiday, year_ago_ds

year_ago_ds 提前算好,同比 SQL 能少一层乱日期计算。节假日每年补一次。

6.2 DWD:口径只在这里定一次

订单明细是经营分析的心脏。我只保留 支付成功且未整单关闭 的行作为销售事实,退款走退款事实表,不要在一张表里又加又减,后人看不懂。

sql 复制代码
INSERT OVERWRITE TABLE dwd.dwd_trd_order_di PARTITION (ds = '${bizdate}')
SELECT
    o.order_id,
    o.user_id,
    o.shop_id,
    i.sku_id,
    s.cate_id_l1,
    s.cate_id_l2,
    o.channel_id,
    o.pay_time,
    i.sku_qty,
    i.pay_amount,
    i.cost_amount,
    i.pay_amount - NVL(i.cost_amount, 0) AS gross_profit,
    CASE
        WHEN o.pay_time IS NOT NULL
         AND o.order_status NOT IN ('CLOSED', 'CANCELLED')
        THEN 1 ELSE 0
    END AS is_valid_pay,
    GETDATE() AS etl_time
FROM ods.ods_trd_order_di o
JOIN ods.ods_trd_order_item_di i
  ON o.order_id = i.order_id
 AND i.ds = '${bizdate}'
LEFT JOIN dim.dim_itm_sku_df s
  ON i.sku_id = s.sku_id
 AND s.ds = '${bizdate}'
WHERE o.ds = '${bizdate}';

几点硬规定:

  1. 所有 JOIN 都带分区条件 。维表漏了 ds,就是全表扫,钱和时间一起烧。
  2. INSERT OVERWRITE + 分区,禁止无分区 INSERT INTO
  3. 金额先统一单位,再进 DWD。
  4. is_valid_pay 这种业务旗标,注释写清楚,和产品、财务对过字。
  5. 用户、门店缺失不要默默丢掉。维度缺失打未知件(shop_id = '-1'),否则你会少算钱还不知道。

退款表单独建 dwd_trd_refund_di,关联原订单号、退款时间、退款金额。经营看「净GMV」时在 DWS 减,不要改历史订单分区。

6.3 DWS:按查询粒度预聚合

看板最常用的是「天 × 门店 × 渠道」。这一层做出来,Hologres 和 API 都轻松。

sql 复制代码
INSERT OVERWRITE TABLE dws.dws_trd_shop_nd PARTITION (ds = '${bizdate}')
SELECT
    shop_id,
    channel_id,
    COUNT(DISTINCT IF(is_valid_pay = 1, order_id, NULL)) AS pay_ord_cnt,
    COUNT(DISTINCT IF(is_valid_pay = 1, user_id, NULL))  AS pay_usr_cnt,
    SUM(IF(is_valid_pay = 1, pay_amount, 0))             AS gmv_amt,
    SUM(IF(is_valid_pay = 1, gross_profit, 0))           AS gross_profit_amt,
    SUM(IF(is_valid_pay = 1, sku_qty, 0))                AS pay_sku_qty
FROM dwd.dwd_trd_order_di
WHERE ds = '${bizdate}'
GROUP BY shop_id, channel_id;

再做一张品类汇总 dws_trd_cate_nd、一张投放汇总 dws_mkt_campaign_nd。ROI 在 ADS 里用交易 DWS 关联投放 DWS,不要在 DWD 订单上直接 JOIN 广告,粒度对不齐。

用户复购、留存这类,用独立 DWS,按 user_id 分布。别和门店天表揉一张,扫起来又宽又慢。

6.4 SQL 节点里的参数

DataWorks ODPS SQL 节点调度参数:

复制代码
bizdate=$bizdate

代码里只用 ${bizdate}。需要昨天、上周同一天:

sql 复制代码
-- 昨天分区
ds = '${bizdate}'

-- 7 天前,调度参数里加
-- bizdate7=$bizdate-7

赋值节点、条件节点先别玩花的。日任务链路清晰比「智能分支」重要。

6.5 小文件和数据倾斜

经营订单按天分区一般还好。要是某一天做大促,单分区几千亿行,注意:

  • JOIN 小维表让 MaxCompute 走 mapjoin:/*+ MAPJOIN(s) */
  • 大 Key(比如一个超大门店)先打散再聚合
  • 下游 Hologres 导入按天分区,避免一次灌半年

临时表写 tmp. 下,生命周期 7 天。调试用的 CREATE TABLE xxx AS SELECT 跑完就 DROP,别留在生产库。


7. Hologres:外表加速 vs 内部表服务

7.1 外表:给分析师和对数用

在 Hologres 里建一个 schema 专门放映射:

sql 复制代码
CREATE SCHEMA IF NOT EXISTS foreign_mc;

批量映射 MaxCompute 三层模型里的表(项目名、Schema 名换成你的):

sql 复制代码
IMPORT FOREIGN SCHEMA "mc_biz_prod#dws"
LIMIT TO (dws_trd_shop_nd, dws_trd_cate_nd)
FROM SERVER odps_server
INTO foreign_mc
OPTIONS (if_table_exist 'update', if_unsupported_type 'error');

两层模型则 IMPORT FOREIGN SCHEMA mc_biz_prod。三层模型写成 项目#schema,少写一层会报错。

LIMIT TO 一定要加。不加就把整个项目表全映射进来,又慢又乱。

查询:

sql 复制代码
SELECT shop_id, gmv_amt
FROM foreign_mc.dws_trd_shop_nd
WHERE ds = '20260822';

外表适合:

  • 对账:Hologres ADS 和 MaxCompute DWS 对一下
  • 临时下钻到 DWD(能跑,别当接口)
  • 还没决定要不要常驻的表

不适合:DataService 高峰、大屏 5 秒刷一次、Agent 并发问数。

MaxCompute 分区更新后,导入或对账前再跑一遍 IMPORT FOREIGN SCHEMA ... if_table_exist 'update',把元数据刷新。

7.2 内部表:给应用用

核心指标表在 Hologres 物化。建表属性和 CREATE TABLE 放同一个事务里,分布键、聚簇事后改不了(改等于重建)。

sql 复制代码
BEGIN;

CREATE TABLE ads.ads_biz_shop_1d (
    shop_id            TEXT        NOT NULL,
    channel_id         TEXT        NOT NULL,
    ds                 TEXT        NOT NULL,
    pay_ord_cnt        BIGINT,
    pay_usr_cnt        BIGINT,
    gmv_amt            NUMERIC(18,2),
    gross_profit_amt   NUMERIC(18,2),
    pay_sku_qty        BIGINT,
    etl_time           TIMESTAMPTZ,
    PRIMARY KEY (shop_id, channel_id, ds)
);

CALL SET_TABLE_PROPERTY('ads.ads_biz_shop_1d', 'orientation', 'column');
CALL SET_TABLE_PROPERTY('ads.ads_biz_shop_1d', 'distribution_key', 'shop_id');
CALL SET_TABLE_PROPERTY('ads.ads_biz_shop_1d', 'clustering_key', 'ds:asc');
CALL SET_TABLE_PROPERTY('ads.ads_biz_shop_1d', 'segment_key', 'ds');
CALL SET_TABLE_PROPERTY('ads.ads_biz_shop_1d', 'bitmap_columns', 'channel_id');
CALL SET_TABLE_PROPERTY('ads.ads_biz_shop_1d', 'time_to_live_in_seconds', '7776000');

COMMIT;

怎么选键,别背概念,按查询来:

属性 我怎么选
orientation 经营分析几乎全是列存。点查用户画像再考虑行存或行列共存
distribution_key JOIN / GROUP BY 的大表关联键。门店分析用 shop_id,用户分析用 user_id。不要选日期,日期太集中,shard 会偏
clustering_key / segment_key 时间,加速 ds BETWEEN
bitmap 低基数列等值过滤:渠道、订单类型、是否会员
PRIMARY KEY 有主键才能按键更新。日指标表用业务键 + ds

分区表:Hologres 一级 LIST 分区。MaxCompute 多级分区进 Hologres,只留一级(通常是 ds),其余当普通列。

sql 复制代码
CREATE TABLE ads.ads_biz_shop_1d (
    ...
    PRIMARY KEY (shop_id, channel_id, ds)
) PARTITION BY LIST (ds);

子表可以手建,也可以开动态分区,让它按天自动建子表。调度导入前确认当天子表在,否则 INSERT 直接失败。

7.3 从 MaxCompute 灌进 Hologres

量大、要快,用 Hologres SQL 从外表 INSERT,比再走一遍数据集成快。

DataWorks 里建 Hologres SQL 节点:

sql 复制代码
-- 刷外表元数据
IMPORT FOREIGN SCHEMA "mc_biz_prod#dws"
LIMIT TO (dws_trd_shop_nd)
FROM SERVER odps_server
INTO foreign_mc
OPTIONS (if_table_exist 'update');

-- 先清当天再写入,避免重跑双份
DELETE FROM ads.ads_biz_shop_1d WHERE ds = '${bizdate}';

INSERT INTO ads.ads_biz_shop_1d
(
    shop_id, channel_id, ds,
    pay_ord_cnt, pay_usr_cnt, gmv_amt,
    gross_profit_amt, pay_sku_qty, etl_time
)
SELECT
    shop_id,
    channel_id,
    ds,
    pay_ord_cnt,
    pay_usr_cnt,
    gmv_amt,
    gross_profit_amt,
    pay_sku_qty,
    NOW()
FROM foreign_mc.dws_trd_shop_nd
WHERE ds = '${bizdate}';

ANALYZE ads.ads_biz_shop_1d;

有主键的表也可以 INSERT ON CONFLICT 做 upsert。日批次我更喜欢 删当天再插,语义清楚,补数据不会留残行。

导入前对参与表跑 ANALYZE,优化器才有统计信息。大表导入 OOM 时,把外表执行并行度调低(hg_experimental_foreign_table_executor_max_dop),别和在线查询抢同一坨内存。生产实例建议开 计算组:导入走离线计算组,查询走在线计算组。

7.4 给 AI 用的宽表单独做

Agent 问数不要让它 JOIN 五张 DWS。给一张当天经营概览:

sql 复制代码
CREATE TABLE ads.ads_ai_biz_overview_1d (
    ds                   TEXT PRIMARY KEY,
    gmv_amt              NUMERIC(18,2),
    gmv_amt_wow          NUMERIC(18,2),
    gmv_wow_rate         NUMERIC(10,4),
    gross_profit_amt     NUMERIC(18,2),
    gross_profit_rate    NUMERIC(10,4),
    pay_usr_cnt          BIGINT,
    pay_ord_cnt          BIGINT,
    atv_amt              NUMERIC(18,2),   -- 客单价
    ad_cost_amt          NUMERIC(18,2),
    roi                  NUMERIC(10,4),
    etl_time             TIMESTAMPTZ
);

字段名用业务语言,别用源库缩写。问数产品匹配「客单价」「毛利率」时,宽表字段就是答案,少一层翻译,幻觉会少一点。

口径变更只改 MaxCompute DWS / ADS 任务,Hologres 重导当天。不要在 Java 里二次计算毛利率,应用层一算,数仓对账永远对不齐。


8. DataWorks 调度:把链路变成每天能跑的 DAG

8.1 业务流程怎么串

一个经营分析日任务,从左到右:

复制代码
[同步 ODS 订单]─┐
[同步 ODS 订单明细]─┼─[DIM 商品]─[DWD 订单]─[DWS 门店天]─[Holo 导入门店天]─[质量校验]─[数据服务用的表已就绪]
[同步 ODS 商品]──┘                              └─[DWS 品类天]─[Holo 导入概览]
[同步投放]────────────────────────────────────────┘

依赖用 产出表 来挂,不要靠「估计 3 点跑完」。上游节点输出 dwd.dwd_trd_order_di,下游依赖这张表。换人接任务时,血缘还在。

日调度时间:

  • 同步:凌晨 01:30 左右,避开业务库高峰,也避开 RDS 备份窗
  • 计算:同步成功之后,不要固定 02:00 硬跑,用依赖
  • 要求 9 点老板看数,倒推最晚完成时间,设超时告警

跨周期:今天的 DIM 依赖昨天 DIM 的场景不多。用户留存这类,依赖历史分区时,用 跨周期依赖 昨天自己,别在 SQL 里碰运气扫 180 个分区还不写 ds

8.2 发布和生产

标准模式:开发环境跑通 → 提交 → 发布到生产。发布单写清楚改了哪张表、口径有没有变。口径变更当天要同步改质量规则的期望值,否则规则会误杀。

补数据:运维中心选节点、选业务日期范围。INSERT OVERWRITE 分区表才敢补。INSERT INTO 无分区表,补一次脏一次。

8.3 失败怎么收

告警挂值班表,钉钉 / 短信都行。我分两级:

  • 核心链路(订单 DWD、门店 DWS、概览 ADS):失败立即打,夜班要起来
  • 边缘表(某条投放渠道、某张实验宽表):早会看

重跑次数不要无限。连续失败说明是数据问题或源库变了,不是偶发抖动。


9. 数据质量:没规则就别发布

经营数字错一次,信任没了。质量规则和节点绑在一起,强规则失败阻断下游

我最少会挂这些:

规则 强弱
ODS 订单 当天行数 > 0
ODS 订单 主键 order_id 唯一
DWD 订单 pay_amount >= 0
DWD 订单 当天 GMV 相对 7 日均值波动不超过阈值(比如 50%,大促调大) 弱,告警不阻断
DWS 门店 shop_id 不为空
ADS 概览 有且仅有一行当天记录
ADS 概览 gmv_amt 与 DWS 汇总差的绝对值 < 0.01

波动规则别设太死。周一和周末差一倍很正常,用同比、用工作日基线,比用「环比 20%」少误报。

空值、枚举值(订单状态不在码表里)也要管。源系统多一个状态,DWD 的 CASE 没接住,指标会悄悄变少。

DataWorks 数据质量里把规则挂到对应 ODPS / Hologres 节点。强规则失败,下游 Holo 导入不要跑,避免把错数推到看板上。


10. 数据服务:给 AI 和报表一个稳定出口

Hologres 内部表就绪后,上层有三条路,按场景选,不要全走 JDBC 裸连。

10.1 BI

Hologres 兼容 PostgreSQL,DataV、Quick BI、Tableau、Metabase 都能连。账号只读、只开放 ads 和必要的 dws。分析师要下钻 DWD,走申请,别默认全开。

10.2 DataWorks 数据服务

把稳定查询封 API,给运营后台和 Agent 工具调用。

示例:按门店查某天经营。

sql 复制代码
SELECT
    shop_id,
    channel_id,
    pay_ord_cnt,
    gmv_amt,
    gross_profit_amt
FROM ads.ads_biz_shop_1d
WHERE ds = ${ds}
  AND shop_id = ${shop_id}

发布前:

  • 参数做校验,ds 正则 \d{8}
  • 超时设短(1~3 秒),比让接口挂着强
  • 限流,Agent 一并发,Hologres 一样会顶不住
  • 生产走数据服务的资源组,和同步任务隔离

返回字段加一层中文说明,问数那边少做映射。金额还是数字,单位在文档里写死「元」。

10.3 AI 怎么接,才不像在碰运气

这一段是中台和「Chat 直接连库」的差别。

  1. 先指标表,后自由 SQL。 Agent 工具白名单只给 ads_* 和几个 API。不要把 MaxCompute 项目级 SELECT 扔给模型。
  2. 口径文档当系统提示的一部分。 GMV、实收、净GMV 各是什么,写在指标字典里,随 API 一起版本化。
  3. 数字必须出仓,不允许模型口算。 同比、占比在 ADS 就算好。模型只负责选指标和读结果。
  4. 问不到就说没有。 当天分区没产出(质量阻断),API 返回明确错误,前端显示「数据未就绪」,比返回半截数好看。

异常检测用日指标序列即可:读 ads_ai_biz_overview_1d 近 30 天,规则或模型在应用侧跑。特征可以在 MaxCompute 离线算好再导入,不必在线 JOIN DWD。


11. 权限、成本、监控

11.1 权限

  • 生产 MaxCompute:开发人员默认没有 DROP。要删表走工单或运维账号。
  • Hologres:ads 写权限只给调度账号。人用只读账号。
  • 数据服务 AppKey 按应用拆,不要全公司共用一把。
  • 手机号、身份证进 ODS 可以,进 DWS / ADS 要脱敏或直接去掉。经营分析绝大多数场景用不到。

11.2 成本,三条最管用

  1. 分区条件写进每一条 SQL。 这是 MaxCompute 账单第一杀手。
  2. Hologres 只存热数据和高频表。 两年明细全进口,存储和实例规格会一起涨。冷查询走外表。
  3. 临时表和失败重跑。 任务失败循环重跑,等于按失败次数付计算费。先修数据再重跑。

MaxCompute 作业看执行计划:有没有分区裁剪、有没有长尾 Map。Hologres 看慢查询日志,缺索引就补,补不了就预聚合。

11.3 监控

最低配置:

  • DataWorks 基线:核心 ADS 9:00 前产出
  • MaxCompute 当日计算消费阈值
  • Hologres CPU / 内存 / 连接数
  • 同步延迟(实时链路才需要)
  • 质量规则失败率

基线比单点告警有用。单点失败你知道某个节点挂了;基线告诉你「老板 9 点要看的数赶不上了」。


12. 我踩过的坑

  1. ${bizdate} 和自然日混用。 调度 8 月 23 日跑的任务,业务日期默认是 22 日。运营说「今天的数」时,先问他要的是自然日还是业务日。
  2. 增量条件用了没有索引的列。 源库被扫爆,DBA 会来找你。先 EXPLAIN,再上同步。
  3. Overwrite 写成 Append。 重跑后 GMV 翻倍,质量规则如果只查非空,查不出来。用「行数相对昨日」能兜住一部分。
  4. Hologres 外表当 API。 演示很快,一并发全超时。服务表必须内部表。
  5. 建内部表忘了同一事务里设 distribution_key。 事后改不了,只能新建表、导数据、改下游。
  6. 维表漏分区。 dim_itm_sku_df 不写 ds,作业从 30 秒变成 30 分钟。
  7. 金额有的分有的元。 对账对了三天,最后发现投放表是分、订单表是元。DWD 统一。
  8. 生命周期没设。 半年后 ODS 把存储打满。建表语句必须带 LIFECYCLE
  9. 开发直接连生产 Hologres。 一条没 WHERE 的 SQL 把在线查询打挂。计算组隔离,或者开发实例隔离。
  10. 口径口口相传。 人一走,指标就变成传说。字典表或文档仓库里写死公式,API 注释里抄一份。

13. 建议的实施顺序

别并行铺 20 条链路。按这个顺序,两到三周能看到第一张能用的经营日报(人够、源表干净的前提下)。

阶段 交付物 完成的标志
1 地域、账号、标准模式工作空间、Serverless 资源组、MC / Holo 绑定 连通性测试全绿
2 分层命名规范、指标口径一页纸(GMV / 毛利 / 客单) 业务、财务、数据三方签过
3 ODS 订单、订单明细、商品、门店 四张同步 连续 3 天分区行数合理
4 DIM + DWD 订单,质量规则(主键、金额、非空) 能对上源库抽样
5 DWS 门店天、品类天 抽两天人工加总一致
6 Hologres 内部表 + 导入任务 + ANALYZE BI 连上,秒级返回
7 概览 ADS + 数据服务 API 应用或问数打通一个接口
8 基线、告警、补数据演练 人为失败一次,下游阻断且能重跑恢复
9 投放、退款、库存,按同样套路加域 新域不破坏老 DAG
10 要「今日实时」再上 Binlog / Flink 实时数和 T+1 对账误差可接受

第一张表不要追求「企业级数仓全景图」。先把订单链路做成样板:同步覆盖分区、SQL 全是 INSERT OVERWRITE、质量强规则、Holo 内部表、API 只读 ADS。后面库存、投放、用户都按这套复制。


这套中台对我而言,难点不在开通三个产品,而在 口径、分区、服务边界 叠在一起。MaxCompute 负责把历史和复杂计算按天算对;Hologres 负责把人要看的、程序要查的那一层读快;DataWorks 负责让这条链路每天自己跑、跑错能停。

AI 经营分析不是让模型去翻业务库。模型能问得准,是因为你把「昨天门店实收」变成了一张有分区、有质量、有 API 的表。表不可信,生成再流畅也是在一本正经地胡说。

能跑通订单域,再扩。不要一上来就画一张包打天下的架构图------图很好看,数对不上。

相关推荐
熊出没7 小时前
解密数仓中的ODS、DWD、DWS、ADS
数据库·数据仓库
等一个人的@1 天前
数睿通2.0更新:可视化ETL引擎重构与部署优化
数据仓库·重构·etl
学代码的CJY2 天前
Codex + Obsidian:搭建你的 AI 知识库(保姆级教程)
数据仓库·人工智能
糖醋_诗酒3 天前
数据仓库 - 转转 - 一面凉经
数据仓库
醉颜凉3 天前
数据仓库实战:自动化数据质量检测全流程——精准提升数据准确性与完整性
大数据·数据仓库·自动化
RestCloud3 天前
信创数据集成实践:某央企ETL全链路国产化迁移架构拆解
数据仓库·架构·数据安全·etl·etlcloud·数据传输·国产化替代
SelectDB技术团队5 天前
雨润集团 统一实时数据仓库:Apache Doris / SelectDB 的技术能力与实践
数据仓库·实时数仓·apache doris·selectdb
Irene19915 天前
剑指大数据:企业级数据仓库项目实战(金融租赁版)读书笔记__大数据测试集群的服务器节点服务分配规划表__解读(附:技术选型清单)
大数据·数据仓库·金融