ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

营销自动化OLAP架构演进:从离线数仓到实时决策的实践

营销自动化OLAP架构演进:从离线数仓到实时决策的实践 做营销自动化这事我踩过最深的坑不是人群包跑不出来而是数据底座先散架。最开始线上线下各系统各算各的广告平台说点击二十万埋点平台统计十八万订单库里的关联转化又对不上运营开周会的时候对着三份 Excel 反复拉扯。多源数据要真正驱动营销自动化一个统一的 OLAP 层几乎是绕不开的阶段而这背后就是一套持续演进的架构设计。这篇文章适合正在搭营销数据平台、增长数据中台或者想把离线数仓往准实时 OLAP 架构迁移的工程师。我会把多源数据 OLAP 架构演进里真正踩过的坑和做过的取舍写清楚包括数据源怎么盘、引擎怎么选、分阶段怎么演进、指标怎么建模、一致性怎么处理以及 OLAP 上线后怎么支撑人群圈选和自动触达闭环。目标就一个你读完能直接拿去对号入座而不是看一堆概念名词。1. 营销自动化的数据盘面这五类数据源是架构的起点很多人一开始就把精力放在引擎选型上这是本末倒置。营销自动化的 OLAP 架构要解决什么问题完全取决于要和哪些数据源打交道。我把它盘成了五类每一类的数据特征、接入方式和踩坑点都不一样。1.1 广告平台投放数据高频、强时效、字段多变投放侧每天产生计划、广告组、创意各个层级的消耗、展示、点击、转化回传数据主要来自各家广告平台的 API。这类数据有几个很麻烦的特点。第一接口配额紧。平台一天就给你那么多次调用额度你还要拉好几个维度的报表配额根本不够用。第二字段口径不统一。有的平台叫“展示”有的叫“曝光”有的叫“展示量”同一个词在不同平台语义可能完全不同。第三回传有延迟。广告平台给你回传的转化数据经常是 T1 甚至 T3 补垄的你今天看到的 ROI 明天可能还会变。这类数据的处理方式我建议是定时同步 API落到 OLAP 里用 Unique 模型做 upsert。因为你没法假设平台只产生增量它经常修正历史数据——昨天说消耗一万今天改成九千这种场景 append 型存储根本扛不住。1.2 埋点行为数据流量大、延迟低、是营销漏斗的主体网站、H5、APP 上的埋点事件是营销自动化分析里量级最大的一部分。曝光、点击、浏览、加购、注册、下单这些事件通过 Kafka 实时进入数据链路。营销漏斗的主体就在这里没有它你根本不知道用户从看到广告到最终转化之间经历了什么。埋点数据的接入核心是事件命名治理。我见过太多团队event_name 里混着大小写、空格、中文甚至同一个事件叫三个名字。这件事必须在埋点规范阶段就定死不然后面清洗逻辑会越写越脏。另外埋点数据天然就是明细型的要原样保留不要在上游就做聚合否则后续任何分析都受限。1.3 CRM 与订单业务库事实钱包数据需要 CDC订单、客户、优惠券、会员等级这些数据通常放在 MySQL 或者 PostgreSQL 业务库里。这是“钱”的数据也是转化承接的最终事实。营销自动化里所有 ROI、LTV 的最终计算都要回到这一层。这类数据源的接入千万别用业务库直连。我的做法是用 CDCChange Data Capture从 Binlog 同步交给 Canal 或者 Debezium 解析再进 Kafka最后由 Flink 写入 OLAP。原因很简单一个是不能因为分析查询拖垮线上交易库另一个是营销系统需要准实时看到订单变化而不是每天全量拉一次。1.4 多源数据带来的三类基础问题把这五类数据源放一起你会发现三个绕不开的问题。第一个是口径问题。点击、曝光、ROI在广告平台、埋点系统和财务系统里定义完全不一样。广告平台的“点击”可能是去重后的埋点的“点击”可能是带参数的财务看的“成交”又是指已支付订单。第二个是 ID 打通问题。未登录用户用 device_id登录用户用 uid广告平台又用点击回调的 click_id多套 ID 之间要做映射否则同一个用户在事件表里是三个人。第三个是时效性割裂。报表工具直连业务库数据只有 T1营销自动化想实时圈人群却拿不到数据。这三个问题不是靠堆数据工具能解决的必须靠一个分层合理、口径统一的 OLAP 建模层。这也是为什么我要说架构演进之前先盘数据。2. 引擎选型不是越新越好按营销分析场景倒推 OLAP 方案很多团队一聊 OLAP 就先问“哪个引擎最快”我的答案是先看你的查询长什么样。营销分析场景的查询特征非常鲜明拿这些特征去倒推引擎选型才不会翻车。2.1 营销分析查询的三个典型特征第一个特征是多维明细检索。运营和投放人员要按渠道、计划、日期组合筛选甚至要看某个人群的明细记录。这种查询是典型的 point query 加多维过滤不是单纯的大宽表扫描。第二个特征是高基数精确去重。计算 UV、转化用户数、留存率、LTV都要对 uid 或者 device_id 做海量去重。这里的难点是“精确”两个字。用近似去重运营可能会因为误差多花钱或者少花钱所以金额相关指标必须精确。第三个特征是漏斗和归因计算。从曝光到点击到加购到支付要跨多个事件类型做关联还要把广告平台的点击和后续订单关联起来。这类查询要处理大量的 join 和窗口聚合分析窗口通常是近 7 天、近 30 天。2.2 主流 OLAP 引擎对比ClickHouse、Doris、StarRocks拿这三个引擎对比是因为它们是目前营销数据场景里被讨论最多的。对比维度ClickHouseApache DorisStarRocks明细写入能力MergeTree 高性能写入更新能力弱Duplicate 模型适合明细Unique 支持 upsertPrimary Key 模型支持较好更新与修正ReplacingMergeTree / AggregatingMergeTree需批量Unique Key 模型Merge-on-Write 实时性好主键表默认 Merge-on-Write多表 Join偏弱依赖物化视图或大内存Colocate Join / Bucket Shuffle JoinColocate Join 更成熟高并发点查一般需复杂规划较好适合人群圈选类查询较好高基数精确去重uniqExact 内存开销大可转 bitmapBITMAP 类型 精确去重配合物化视图BITMAP 类型支持较好运维成本单机能力强集群需外部组件支撑FE/BE 两套进程部署较重与 Doris 类似但迭代更快结论很直接营销自动化场景里要频繁处理实时 upsert、多表关联、高基数精确去重、高并发人群圈选Doris 和 StarRocks 会更顺手。ClickHouse 并不是不好它更适合超大规模日志类实时分析但营销分析并不是纯日志场景。2.3 为什么不能只靠离线数仓硬撑有人会问Hive/Spark 数仓不是也能做这些吗确实能做但那是 T1 的节奏。离线数仓解决的是口径和存储问题查询延迟动辄几秒到几分钟没法支撑营销人员在投放过程中做实时决策。营销自动化的特点是“看完数据要马上做动作”——建人群、放量、暂停素材这些动作都要基于分钟级的数据反馈。所以我的建议是离线数仓和实时 OLAP 不是二选一而是并存。离线跑全量重算和对账实时支撑及时决策分层模型两边复用。3. 三阶段演进实录从 Excel 直连到实时 OLAP 的关键转折架构不是一天搭出来的。我复盘自己经历过的升级过程基本可以分成三个阶段每个阶段都有它崩溃的节点和演进的动机。3.1 阶段一业务库直连加报表工具活不过三个月这是很多团队的第一版MySQL 前置BI 工具直接连业务库写 SQL。初期数据量小问题不明显。一旦报表数量上来运营、投放、财务都开始用问题就炸了。频繁的复杂查询直接拖垮业务库慢查询把线上交易的性能都带崩了。每张报表背后是一套 SQL同一个人问我“点击是多少”不同报表能给出三个数运维每天被拉去开会解释数据差异。这个阶段的教训是营销分析查询绝不能和业务在线库共享资源物理隔离是底线。3.2 阶段二T1 离线数仓口径收敛了但时效掉队被阶段一逼着我们上了 Hive/Spark 离线数仓定时任务每天凌晨清洗数据产出统一的宽表和指标。口径确实收敛了报表也稳定了数据团队终于不用天天背锅。但新的问题很快冒出来只能看到昨天的数据。早上数据跑完运营一看已经过时了。A/B 实验要想当天看效果做不到投放要实时看素材消耗也做不到。运营对 T1 数据越来越不信任因为广告平台后台的实时数据和他们手上的报表就是差一截。这个阶段最大的贡献是把口径和分层模型沉淀下来了为实时化铺了路。3.3 阶段三Kafka Flink OLAP 的准实时管道实时链路是我们演进的重头戏它的整体结构是这样的埋点链路APP/Web 埋点 → Kafka → Flink 清洗、ID 映射 → Doris/StarRocks DWD 明细表广告 API 链路定时拉取广告平台数据 → 清洗 → Kafka → Flink → DWS 聚合表 upsert订单库链路MySQL Binlog → Canal/Debezium → Kafka → Flink → DWD 订单事实表加 DWS 聚合分层模型沿用离线数仓的体系DWD 明细层存原始事实DWS 聚合层按天和维度预聚合ADS 应用层服务报表与指标系统。实时链路不是要把离线干掉离线负责全量重算和对账实时负责分钟级增量。阶段时效性查询并发口径一致性维护成本阶段一实时直连但拖垮库极低混乱低阶段二T1中统一中高阶段三分钟级高分层统一中高可控3.4 演进的关键先想清楚要解决什么业务问题数据架构演进不是为了赶时髦。阶段一走到阶段二是因为口径混乱已经影响业务信任阶段二走到阶段三是因为营销自动化的核心诉求从“事后看报表”变成了“实时调整投放”。每走一步我都会问自己三个问题查询耗时是否明显下降报表口径对账是否通过运营人员自助提数是否变快了如果这三个答案都是肯定的说明架构演进的投入是值得的。如果只是把引擎换了指标还是对不上那营销团队很快会失去信任后面再想推动任何改造都很困难。4. 埋点、订单与广告消耗的建模营销指标在 OLAP 里的落地方式架构搭起来了真正的难点在建模。同样的数据模型设计得好不好查询性能能差出几十倍。我挑三个核心场景讲清楚我们是怎么在 OLAP 里落地的。4.1 DWD 明细表营销事件表的建表细节先看我们埋点事件表的 DDL以 Apache Doris 2.x / StarRocks 3.x 语法为准不同版本细节略有差异CREATE TABLE dwd_traffic_event ( event_id VARCHAR(128) NOT NULL COMMENT 事件唯一ID服务端生成, dt DATE NOT NULL COMMENT 事件日期本地时区, event_time DATETIME NOT NULL COMMENT 事件产生时间, uid BIGINT NULL COMMENT 登录用户映射ID, device_id VARCHAR(64) NULL COMMENT 匿名设备ID, event_type VARCHAR(32) COMMENT impression/click/cart/order/payment, campaign_id BIGINT COMMENT 广告计划ID, ad_group_id BIGINT COMMENT 广告组ID, creative_id BIGINT COMMENT 创意ID, channel VARCHAR(64) COMMENT 渠道标识, attributed_campaign_id BIGINT COMMENT 归因后的计划ID, page_url VARCHAR(512) COMMENT 落地页URL, extra_json JSON COMMENT 扩展字段 ) DUPLICATE KEY(event_id) PARTITION BY RANGE(dt)() DISTRIBUTED BY HASH(uid) BUCKETS 48 PROPERTIES ( replication_num 3, dynamic_partition.enable true, dynamic_partition.time_unit DAY, dynamic_partition.start -60, dynamic_partition.end 3 );几个设计决策我逐个解释。明细表用 Duplicate Key 模型不丢任何原始事件。event_id 作为重复键它的作用是业务幂等去重不是数据库唯一约束。按天分区是因为营销查询一定带时间范围分区裁剪能把扫描量减到最小。按 uid 分桶是因为去重、join 都依赖用户维度同一用户能落到同一个桶里。动态分区保留最近 60 天更老的数据转冷存储或者对象存储控制成本。4.2 DWS 聚合表把高频指标提前算好明细表是万能的但它太大不适合高频报表直接扫。所以我们把广告消耗这类高频查询做成 DWS 聚合表CREATE TABLE dws_ad_cost_daily ( dt DATE NOT NULL, platform VARCHAR(32) NOT NULL COMMENT 广告平台, campaign_id BIGINT NOT NULL COMMENT 广告计划ID, cost DECIMAL(12,4) DEFAULT 0, impressions BIGINT DEFAULT 0, clicks BIGINT DEFAULT 0, conversions BIGINT DEFAULT 0, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ) UNIQUE KEY(dt, platform, campaign_id) DISTRIBUTED BY HASH(campaign_id) BUCKETS 16 PROPERTIES (replication_num 3);这里用 Unique Key 模型是因为广告平台的数据会有修正。同一个 dt、platform、campaign_id 下消耗和点击数据会被平台调整Unique Key 模型支持按 key 直接覆盖保证历史修正能落到表里。有团队会纠结用 Aggregate 模型加 SUM 聚合但我建议用 Unique 模型因为有些字段不是简单加法的比如去重后的转化人数你直接 SUM 就重复了。Unique 模型更通用。4.3 漏斗、ROI、LTV 口径怎么固化进数据层口径不统一是营销数据分析最大的痛点。我的经验是口径必须在 DWD 层就打上标签而不是在报表层临时算。拿 ROI 归因来说最怕的就是各系统各算各的。我们实践下来提前确定归因窗口7 天还是 30 天和归因模型首次点击、末次点击、线性归因然后在 Flink 清洗阶段把归因后的 campaign_id 写入事件表的 attributed_campaign_id 字段所有下游报表只认这个字段。这样做的好处是简单粗暴报表层不用再讨论口径。LTV 计算则必须回到订单明细不能只留聚合值。把订单表和事件表按 uid join算出每个用户从首次获客到当前时间产生的累计价值。高基数精确去重要用 BITMAPDoris/StarRocks 里先把 uid 映射成 BIGINT再用 bitmap_union 和 bitmap_count 算精确去重。如果只是要个量级参考用 HLL 近似就够了但涉及金额、费用的指标我一律用精确去重。4.4 维度表变化广告计划改名、渠道调整怎么办营销运营经常改广告计划名称调整渠道分组。如果事件表里只存 campaign_id要知道名称必须关联维表然后维表一变历史统计口径就乱了。我的实践建议是营销分析场景优先宽表冗余。把 campaign 名称、渠道、负责人这些低频变化的字段冗余到事件表或者 DWS 聚合表里查询不用关联口径被冻结在写入那一刻。如果确实需要追踪维表历史变化可以考虑 SCD2但营销分析场景里绝大多数时候宽表冗余是性价比最高的方案。牺牲一点存储换来查询性能和口径稳定非常值得。5. 一致性、迟到数据与大表 JoinOLAP 工程里最硬的四块骨头建模建好了不代表数据就靠谱了。OLAP 上线之后真正考验工程师的是数据一致性、迟到数据、大表关联这些工程细节。这四块骨头我每块都啃过。5.1 重复数据和幂等写入为什么事件表必须有 event_id流式计算一旦重启Kafka 至少一次语义加 Flink 重放很容易造成重复写入。OLAP 引擎本身不会自动识别业务上的重复事件特别是 Duplicate 模型它就是把数据原样存进去。所以我把 event_id 当作硬约束所有事件在源头必须生成唯一 ID下游做一切去重都依赖它。校验方法很简单每天对一遍 DWD 表select dt, count(1) as total_cnt, count(distinct event_id) as unique_cnt, count(1) - count(distinct event_id) as dup_cnt from dwd_traffic_event where dt 2025-01-15 group by dt;差值超过阈值就告警排查是不是 Flink 任务重复写了。把幂等保障放在上游比在 OLAP 里做低效去重要靠谱得多。5.2 迟到数据与归因窗口营销场景的特殊挑战营销数据有一个特点广告平台的转化回传会晚到好几天。用户看了广告5 天后才下单这条转化事件发生在 5 天后但如果按 7 天归因窗口来算它应该归属到 5 天前的那次点击。这个逻辑在离线数仓里很简单跑一次全量重算就完了。但在实时链路里就麻烦了实时表今天写入一条转化你用 Unique 模型把它盖到点击发生那天的分区没问题但如果这个转化后面又被平台修正你要能再次覆盖它。所以我们规定了一个原则实时链路不能设计成“只能追加、不能重算”的形态DWS 聚合表要提供重算接口每天早上对近 7 天的归因窗口做一次校准任务保证历史指标是准的。5.3 大表关联用分桶设计和 Colocate Join 把 SQL 效率跑起来营销分析里最重的查询是事件表 join 订单表算转化两个表都几十亿行join 一不小心就把集群跑挂。Doris/StarRocks 的解决思路是 Colocate Join两张表都用DISTRIBUTED BY HASH(uid)分桶分桶数保持一致再使能 colocate 属性这样关联时数据就在本地桶内完成不需要 shuffle。如果两张表分桶列不一致查询就会退化成大规模分发基本等于告诉集群“我要跑一个全量 join”。如果引擎不支持 Colocate Join或者表实在没法保证分桶一致另一个高性价比思路是宽表把订单表的最新状态实时冗余到事件表查询之前先把需要 join 的字段都塞进去。营销分析的维度相对稳定宽表牺牲一点存储空间换取查询不再 join这是非常划算的取舍。5.4 实时链路可观测性与对账实时数仓比离线数仓更需要监控。离线的坏了第二天看日志能查到实时链路坏了 10 分钟营销自动化系统可能已经发出几万条错误触达。所以我建议至少要监控这几项Kafka topic 的消费延迟、Flink checkpoint 失败率、OLAP 导入失败的条数、导入时延、还有 DWD 表的重复率。另外每天固定时间跑一个对账任务用离线数仓和实时表对比昨天的曝光、点击、订单金额差值超过阈值就告警。这个对账机制非常重要没有它实时数仓上线三个月后没人敢信里面的数据。6. 从报表到决策OLAP 支撑营销自动化的三个闭环场景OLAP 架构的最终价值不是让报表变快而是让数据真正驱动营销决策和自动化动作。我讲三个我们已经跑通的闭环场景。6.1 人群圈选从“数仓取数”变成“服务化查询”以前运营要圈一个“近 30 天点击过 A 活动但未下单”的人群流程是提需求给数仓数仓写 SQL跑出来导出文件再交给触达系统一等就是几小时甚至一天。上了 OLAP 之后人群圈选就是一个带过滤条件的明细查询。把渠道、时间范围、事件类型、行为条件拼成 SQL对 DWD 明细表做过滤返回 uid 集合通过 BITMAP 或者直接导出进入触达系统。几十亿行明细秒级返回这是 OLAP 引擎的高并发点查能力带来的质变。这里我建议做一层 SQL 模板服务不对外暴露原始表由应用层拼接参数防止运营同学一时手滑写了个全表扫描把集群拖垮。6.2 自动化触达与效果回流营销自动化系统定时从 OLAP 取人群和指标执行推送、短信、邮件触达触达结果再回流写入 DWD和后续的转化事件关联。这就是一个完整的数据动作闭环。数据驱动开始变成“系统根据 OLAP 里的实时指标自动决定触达策略”而不是“人看报表再决定”。自动化对数据稳定性的要求急剧上升因为一旦数据出错影响是自动放大的。所以第 5 章里讲的那些监控和对账在这个阶段不是可选项而是必选项。6.3 A/B 测试与素材优化A/B 测试过去很痛苦因为实验数据要等 T1看到结果时投放预算都花完了。有了 OLAP 明细层之后实验组和对照组可以直接对 DWD 表做 SQL 聚合实时看转化率、成本、显著性。这里有一个小建议实验分桶键尽量用 uid因为你后面大概率要 join 订单和事件表算 LTV用 uid 分桶能利用上 Colocate Join 的能力。素材级别的曝光、点击、成本对比就更简单了在 DWS 聚合表里按 creative_id 出报表投放人员自己就能看不再需要每次找数据团队。6.4 架构演进带来的团队与流程变化OLAP 架构落地之后最大的变化是口径收敛。运营不再拿广告平台后台和公司报表对喷因为底层数据已经统一到一个模型里。数据团队从天天写临时 SQL 接需求变成专心维护口径、核对数据质量、优化查询性能。但要清醒一点OLAP 只是存储和查询底座真正让数据可信的是口径定义、数据质量校验和权限治理。未来团队可以考虑在 OLAP 之上加一层语义层或者指标平台让非技术团队不用写 SQL 也能配置指标、自助分析。OLAP 不会消失它会下沉成整个营销数据体系的基座。如果让我重来一次我可能不会在实时链路上一上来就那么激进。踩过几次坑之后我觉得架构演进的节奏比技术选型更关键先让业务方看到 T1 口径被统一的好处再上分钟级实时先打通一个核心场景再铺开群体。每走一步都能被业务验证数据团队才有下一次演进的空间。OLAP 不是终点但它是让营销自动化从口号变成真闭环的那块地基。
返回列表