
营销自动化这两年几乎成了增长团队的标配但真正能把自动化跑出效果的项目并不多。我见过太多团队卡在同一个地方活动策划得很漂亮人群圈选很细致触达节奏也排好了结果一到复盘阶段数据从 CRM、广告平台、埋点日志、客服工单好几个系统里拉出来口径对不上时间轴错位转化归因算得乱七八糟。整个营销闭环里数据驱动四个字反而成了最虚的一环。这篇文章不聊营销理论纯粹从数据架构的角度复盘我从零搭建营销自动化数据底座的完整过程。核心线索是一条 OLAP 架构的演进路线从最初的多表 JOIN 硬查到引入列式 OLAP 引擎做宽表加速再到面向多源数据治理的湖仓一体方案。如果你正在为营销活动分析、用户画像查询、实时触达效果监控发愁这篇文章应该能给你一条可以直接落地的路径。1. 多源数据的混乱是营销自动化的隐形瓶颈营销自动化听起来是个业务问题做深了才发现它首先是个数据工程问题。所有自动化策略——无论是用户生命周期触达、流失预警、还是基于人群包的个性化推送——底层都是对数据的实时判断和历史回溯。数据源越是分散这个判断就越难做准。1.1 一套营销活动背后到底牵扯多少个数据源我先列一个典型营销自动化项目的真实数据全景这是我接手过的一个中等规模电商项目不算特别复杂但足够有代表性数据源数据内容更新频率存储形态CRM 系统用户基本信息、会员等级、积分分钟级MySQL 主库订单系统订单明细、支付流水、退款记录实时MySQL Binlog埋点日志页面浏览、按钮点击、曝光秒级Kafka 日志文件广告平台曝光、点击、消耗、转化回传小时级平台报表 API客服工单投诉内容、处理状态、用户反馈分钟级MongoDB短信/推送通道触达记录、送达状态、回执实时通道商回调日志这还只是数据源清单。真正麻烦的是每个系统对用户的标识还不一样CRM 用的是 user_id埋点日志用的是匿名 ID 和登录后的 mid广告平台回传的是设备 IDIMEI/IDFA/OAID客服系统用的又是手机号。要把这些统一成一份干净的、可供分析使用的用户宽表光做 ID 映射就够折腾一阵子。在这个阶段团队犯的第一个错误是试图用一个大宽表解决所有问题。我们把所有维度的数据全部冗余到一张 Hive 表里以为这样查询就快了。结果数据产出延迟从 T1 变成了 T2而且每次某个上游表结构变更宽表就要重刷运维成本直线上升。1.2 数据驱动的前提是先有数据再有驱动营销自动化对数据架构有三个硬性要求缺一个都会让后面的 OLAP 选型走弯路多源实时性用户刚在 App 里加了购物车10 分钟内就要进入待唤醒人群池这是触达窗口期的基本要求。离线 T1 的数据等跑出来用户早就下单了。人群圈选的灵活维度运营同学要的可能不是过去 30 天购买过 3 次以上这么简单的规则而是近 7 天浏览过 A 品类但未购买、且在 B 渠道有过点击、同时客单价在 200 元以上这种多条件组合。这种查询对分析引擎的多维过滤能力要求极高。回溯与归因能力活动结束后要把每个用户的完整行为路径还原出来——什么时候看到推送、什么时候点击、什么时候下单购买、中间经历了几个渠道。这时候需要的是对海量历史明细数据的快速扫描和聚合。带着这三个要求去审视传统的 MySQL 分库分表方案结论很明显撑不住。MySQL 在千万级数据量下做多条件组合过滤还能忍到亿级明细数据做全量扫描聚合时索引基本失效查询响应时间动不动就是几十秒甚至分钟级。这个阶段OLAP 引擎的引入已经不可避免。2. 从 ClickHouse 到 StarRocksOLAP 选型的真实对比在 OLAP 引擎的选型上我经历过一次完整的以为选对了、后来发现不够用的过程。最早我们用的是 ClickHouse后来切换到了 StarRocks。很多文章喜欢把两者放在一起做参数对比但作为真实跑过生产环境的人我想聊一些参数之外、更实际的感受。2.1 ClickHouse 给人的错觉查询很快但生态很硬必须承认ClickHouse 在单表聚合查询上的速度确实惊艳。我们早期把用户行为明细表导入 ClickHouse 后原本需要 30 秒的 COUNT(DISTINCT user_id) 查询压缩到了 2 秒以内。团队一度以为问题已经解决了。但用到后面三个痛点越来越明显多表 JOIN 的能力弱ClickHouse 的 JOIN 实现一直不是强项大表 JOIN 大表时内存消耗极大稍不留神就把查询节点的内存打满。营销分析场景恰恰是典型的多表关联需求——用户表关联订单表、关联行为表、关联触达记录表这正好打在 ClickHouse 的软肋上。并发查询能力受限ClickHouse 是单查询极快多查询互相挤兑的典型代表。运营团队十个人同时刷看板每个查询都在抢 CPU 和内存互相拖慢。高峰期一个 3 秒的查询能被拖到 15 秒以上。数据更新成本高营销场景里用户标签是高频变更的比如是否已领取优惠券这个状态每几分钟就会批量更新一次。ClickHouse 的 MergeTree 家族对更新操作的支持比较别扭需要通过 ReplacingMergeTree 或 CollapsingMergeTree 这类机制实现使用门槛高而且容易出错。2.2 切换到 StarRocks 之后哪些问题真正被解决了StarRocks以及同源的 Doris在架构上比 ClickHouse现代的地方在于它把 MPP 架构和列式存储做了更好的结合。我们迁移过去之后最直观的感受是三个方面JOIN 性能终于能打了StarRocks 的 CBO基于成本的优化器对多表 JOIN 的执行计划优化做得比较成熟相比 ClickHouse 需要手动调整 JOIN 顺序StarRocks 基本可以无脑写优化器自己会选最优执行路径。并发查询稳定很多MPP 架构下查询会被拆分到多个 BE 节点并行执行单查询的资源占用相对可控多查询同时跑的时候不会出现 ClickHouse 那种互相踩踏的现象。我们线上 20 个并发查询P99 延迟基本能稳定在 5 秒以内。主键模型天然适合标签更新StarRocks 的主键模型Unique Key支持真正的行级更新配合内存表Primary Key Index用户标签这种高频小字段更新的场景终于不用再靠重建分区表这种笨办法了。2.3 架构模式的选择为什么我们最终没有走 Lambda在引入 OLAP 引擎的同时同步面临一个架构模式的选择Lambda 还是 Kappa。老实说Lambda 架构批处理 流处理双链路在 2020 年前后还是主流因为它能同时保证数据的准确性和实时性。但它的缺点也很折磨人批链路和流链路的代码要维护两份跑出来的结果经常对不上排查差异要耗费大量精力。我们在营销场景里的数据需求大部分是可以容忍秒级延迟而无需毫秒级延迟的。比如用户下单后 30 秒内更新其标签状态这个要求用 Kafka Flink 实时写入 StarRocks 主键表就能满足完全不需要维护批处理链路做兜底。于是我们选择了 Kappa 架构——统一用实时链路处理所有数据历史数据需要重算时就重新消费 Kafka 里的历史消息再回放一遍。这个决策的执行细节和踩坑过程后面会详细展开但先给结论营销自动化场景Kappa 架构足够用少维护一套批处理能省掉至少 30% 的维护精力。3. 多源数据实时入仓流程中的关键动作与踩坑实录选型定了之后真正的硬骨头在于数据接入。很多人低估了这个环节的工作量觉得不就是从 Kafka 消费数据写进 StarRocks 吗——如果只是这样确实简单。但真实生产环境的麻烦程度远比你想象的复杂。3.1 统一消息格式一个差点被忽略的致命细节多源数据接入最容易踩的第一个坑是各业务系统的消息格式不统一。有的团队用 JSON有的用 Avro有的直接用字符串拼接。当时我们接订单系统的 Binlog 消息时发现消息里某个字段的值在不同时间段居然是不同数据类型——早期是字符串后来改成了整数而下游 Flink 作业的 Schema 没跟着变结果出现了解析异常导致的数据丢失。这个问题的根源在于没有在接入层做 Schema 统一与校验。我们的最终方案是在 Kafka 消息入口处加一层消息适配层基于 Flink 的 MapFunction 实现统一把各源系统的消息转换成内部约定的 JSON Schema并做字段合法性校验。校验不通过的消息进入死信队列Dead Letter Queue方便事后排查而不是直接丢弃或者带着错误数据继续往下游跑。提示死信队列一定要配。没有死信队列的实时链路就像没有日志的线上服务出问题的时候只能靠猜。适配层的另一个职责是完成 ID 映射。我们通过维护一张用户 ID 映射表匿名 ID、user_id、手机号、设备 ID 之间的关联关系在消息进入 OLAP 引擎之前就把 ID 统一成 user_id 作为主键。这张映射表本身也存在 StarRocks 里用 Unique Key 模型实时更新。3.2 Flink 实时写入 StarRocks 的调优经验Flink 写入 StarRocks 的链路我们用的是官方提供的 flink-connector-starrocks。正常情况下写入吞吐是能满足营销场景需求的。但有两个参数值得单独拎出来说因为调不好很容易出事故1. 批量提交大小与间隔的平衡connector 的sink.bulk-flush.max-rows和sink.bulk-flush.interval-ms两个参数直接影响写入频率。一开始我们把 max-rows 设得很大10 万行想提高吞吐结果 StarRocks 的导入事务在高峰期出现积压反而拖慢了整体链路。后来调成了按间隔 按行数双触发max-rows设置为 50000interval-ms设置为 3000。实测下来吞吐和实时性都比较理想也没再出现积压。这个参数不一定是标准答案但可以作为参考基线实际调的时候要看数据量和集群规模。2. 写入失败的重试策略实时链路里StarRocks 偶尔会出现Too many versions错误——这是因为同一分区短时间内导入次数太多超过了版本数限制。遇到这种情况第一个反应不应该是无限重试而是检查上游是否在批量补数比如 Flink 任务重启后从较早的位点开始消费。注意Flink 作业从 checkpoint 恢复时如果 Kafka 位点回退较多会导致 StarRocks 短时间内涌入大量历史数据写入。这种情况应该先暂停作业、待 StarRocks 合并完版本后再恢复而不是把重试参数拉大来硬扛。3.3 数据质量监控营销分析可信度的底线多源数据接进来之后最怕的是数据是错的但没人发现。尤其营销投放入参时如果某个广告平台的回传数据少了一部分ROI 计算就会失真直接导致投放策略误判。我们在接入层和数据应用层之间加了一层数据质量监控核心是三类规则完整性校验每个批次的数据量是否在合理波动范围内。比如一小时前订单量是 5000 条这一小时突然只有 800 条就要告警查原因。空值率校验关键字段如 user_id、order_id、event_time的空值率是否超过阈值。延迟校验Kafka 消息的最晚事件时间和当前时间的差值是否超过了预设范围。如果延迟超过 10 分钟说明链路存在积压要立即排查。这套监控体系用 Prometheus AlertManager 实现阈值是我们根据业务特征反复调的。前期误报很多后来把校验逻辑改成连续 3 个周期异常才告警误报率才降下来。数据质量这块宁可多花两周做扎实也不要省这个时间——毕竟后面所有分析、策略、投放全部建立在这份数据上。4. 数据建模与查询优化营销分析场景的独特应对OLAP 引擎选好了、数据也接进来了但能查和查得快之间还有一段路要走。营销分析场景的数据建模和常规的 BI 报表建模有很不一样的地方值得单独展开。4.1 宽表优先还是星型模型营销场景的答案其实很明确很多数据建模方法论比如 Kimball 维度建模强调用星型模型、雪花模型来规范化数据。但到了 OLAP 场景我的经验是宽表优先模型为辅。原因很简单营销分析的大部分查询都是围绕用户这个主体展开的——用户是谁、什么时候来过、看了什么、买了什么、有没有领券、有没有被触达。如果把这些事实表和维度表拆开建模每次查询都要 JOIN 四五张表即使 OLAP 引擎再能打查询成本也不低。我们的做法是在 StarRocks 里建立用户行为大宽表把常用的维度字段和事实字段全部冗余到一张表里。这张宽表大致长这样字段分类字段示例说明用户维度user_id、性别、年龄、会员等级、注册时间来自 CRM缓慢变化行为维度最近一次访问时间、近 7 天访问次数、近 30 天购买金额来自埋点订单周期性更新营销维度最近一次触达时间、触达渠道、累计优惠券领取数来自触达记录实时更新标签字段是否高潜用户、是否流失预警、所属人群包 ID来自策略引擎异步更新这样做的好处是运营同学写查询时基本不需要 JOIN一两个简单查询就能出结果。缺点是宽表字段多、占用存储大——这个用后面要说的列式存储特性和冷热分层来解决。但同时我们在底层也保留了一份规范化的事实明细表用户行为明细表用于那些宽表覆盖不到的、需要灵活多变的深度分析。宽表负责日常高频查询明细表负责临时深度分析两者并存不冲突。4.2 分区、分桶与排序键性能优化的三板斧宽表建好了查询性能还取决于物理设计。这里分享我们在 StarRocks 上的三个关键实践经验分区策略按日期分区是首选。营销分析的查询基本都带时间范围按天分区能让查询直接裁剪掉无关分区扫描量大幅减少。我们保留 90 天的分区数据更早的数据转入冷存储。分桶策略分桶键的选择直接影响数据分布的均匀性。我们用 user_id 做分桶键因为大多数查询都是按用户维度过滤的这样能把查询范围锁定在少数几个桶内。排序键设计StarRocks 的排序键ORDER BY决定了数据在存储上的物理顺序。我们的排序键是(event_date, user_id, event_time)——日期在最前是因为日期是查询中最常见的过滤条件user_id 和 event_time 紧随其后是因为用户行为分析经常需要按用户和时间两个维度同时过滤。关键点排序键顺序千万不要反。如果让 user_id 排在 event_date 前面那么查询某一天的数据时存储层需要扫描所有用户的数据命中的存储块数量会大幅增加查询性能会显著下降。这个顺序问题花了我一周的时间才彻底想明白。4.3 冷热分层控制成本的重要手段营销数据的体量增长非常快。用户行为明细表每天新增几千万行业务高峰期甚至上亿行。如果全部放在热存储SSD里成本压力巨大。StarRocks 支持数据存储的分层管理。我们做了如下策略近 30 天数据存储在热存储SSD保证查询性能。31-90 天数据迁移到冷存储SATA HDD性能和容量之间的平衡点。90 天以上数据定期导出到对象存储S3/OSS需要时通过外部表查询基本不占用 OLAP 集群存储。这套策略跑下来集群的存储成本压缩了大约 60%同时最常用的近 30 天数据查询性能完全不受影响。4.4 物化视图的取舍加速还是添乱说到 OLAP 优化绕不开物化视图。在营销分析场景物化视图用得好是效率神器用不好就是维护灾难。我们只在两种场景下使用了物化视图高频固定的聚合指标比如每日各渠道 UV/PV这类指标每天都在跑、且查询模式完全固定用物化视图可以把计算提前到数据写入时完成。明细汇总的双轨查询用户看板页同时需要展示汇总数据和最近 N 条明细用物化视图同步维护一份汇总数据明细查询和汇总查询互不干扰。不建物化视图的场景是查询条件变化频繁、维度组合多样的临时分析。这种场景建了物化视图大概率命中不了查询需求反而白白消耗存储和导入性能。营销运营经常会有换个维度看看数据的习惯这块建议忍住物化视图的冲动直接用明细表查询。5. 小样本场景下的数据驱动反思机器学习建模时的实际教训OLAP 架构解决了数据查得动的问题但营销自动化的更高目标是用数据驱动策略决策——这就涉及机器学习建模。在这个环节里我碰到了几个值得深思的问题特别是小样本场景下的模型表现这和我们平常接触的大数据场景很不一样。5.1 小样本场景数据多不代表每个场景都有足够样本营销自动化的机器学习模型比如用户流失预测、高潜用户识别、优惠券核销概率预估通常建模数据量看起来很大——几千万甚至上亿行。但每次做细分的、个性化的模型时问题就来了。举个例子某个小众品类的用户可能全平台只有 2 万个活跃用户真正在最近 30 天内购买过的只有 2000 人而其中响应过某次特定营销活动的人可能只有 200 人。这种场景下训练一个预测谁会对该品类优惠券感兴趣的模型可用的正样本量只有 200 条左右。这种规模的数据放在传统的大数据机器学习框架下非常容易翻车。数据驱动的量并不等于质更不等于代表性。营销场景的细分化和个性化让每一个细分模型都在跟小样本搏斗。5.2 传统数据驱动模型为什么在小样本下容易过拟合这里就要说到热词里那句在小样本场景下传统数据驱动模型易于拟合的真实含义。传统的数据驱动模型特别是深度模型和树模型本质上是在用大量参数去逼近训练数据的分布。当训练样本充足时模型能从中学到真实的数据规律但当样本量很小比如只有几百条模型就会把训练数据里的噪声当成规律来学习表现就是训练集上的 AUC 极高比如 0.98但验证集上只有 0.55几乎等于随机猜模型的预测结果对特定样本非常敏感——换一条数据模型行为就大变在线上真实场景中预测准确率远逊于训练时的表现。这就是典型的过拟合——模型记住了训练数据里的每一条样本而不是学到了真正的规律。5.3 物理模型业务规则模型为什么泛化能力更强与纯粹的数据驱动模型相对的是物理模型。在营销场景里物理模型这个词听起来有点奇怪但实际上是存在的——它指的是基于业务逻辑和先验知识建立的规则模型不依赖样本拟合而是依赖因果关系、业务洞察和领域知识。举个例子识别高价值用户数据驱动模型会从历史数据中学习什么样的用户贡献了更多 GMV而物理模型/业务规则模型则从业务逻辑出发直接定义过去 90 天累计消费超过 5000 元、最近 30 天有至少 3 次购买、退货率低于 10% 的用户属于高价值用户。这两种模型的泛化能力差异在小样本场景下特别明显数据驱动模型在几百条样本下几乎无法学到稳定的规律预测结果波动极大业务规则模型不依赖样本量它的判断依据来源于人对业务的理解在小样本、甚至是零样本完全没有历史数据的新品类场景下依然能给出合理判断。5.4 架构层面的应对融合模型与 OLAP 的交互在搭建营销数据底座时我把这个思路落实成了规则兜底 模型加权的融合策略当某个细分人群有足够的数据量比如正样本 5000 条时优先用数据驱动模型做预测因为统计规律在足够样本下是可靠的当样本量不足比如正样本在 200-5000 条之间时把规则模型的输出作为特征输入到数据驱动模型中或者用规则模型的结果作为模型训练的约束类似先验正则化防止模型在少样本下学到明显反业务直觉的规律当样本极少不足 200 条时直接采用规则模型不训练数据驱动模型。这个策略的实现依赖 OLAP 架构提供的数据服务人群圈选、特征计算、规则引擎全部跑在 StarRocks 之上模型训练和上线则是定期从 OLAP 中拉取批量样本到特征平台完成。数据驱动模型和规则模型的评估、对比、切换也全部在统一的数据工作流里完成。这套融合方案跑了半年实际效果比单纯依赖数据驱动模型稳定得多。特别是在新品类的冷启动营销场景完全零样本和长尾人群的个性化推送场景小样本下规则模型的作用不可替代。6. 架构演进的路标下一步该往哪里走最后聊聊我们对这套架构后续演进的判断。OLAP 只是工具营销自动化的最终目标是把数据能力转化为实时的业务决策力。从当前架构出发我个人认为有三个方向值得深入实时特征平台的建设目前特征计算还是离线批处理为主每天定时从 StarRocks 拉数据算特征但对于用户刚加购、立刻需要判断是否发送限时优惠券这种秒级决策场景离线特征远远不够。下一步计划把高频特征的计算迁移到 Flink 实时链路中特征结果直接写入 StarRocks 供模型实时调用。数据回流的闭环目前 OLAP 是数据汇聚地但策略执行完毕后的结果比如某个用户是否真的被触达、触达后是否转化还没有完全回流到样本库中用于模型再训练闭环做得还不完整。接下来会在数据链路的末端增加策略执行结果的回流机制让每一次触达都成为下一次模型迭代的训练数据。引入语义层随着数据源越来越多业务团队对同一个指标在不同团队看数不一致的抱怨也越来越多。未来计划在 OLAP 之上引入一层语义层Metric Store / Semantic Layer统一指标定义让数据分析真正成为业务团队可自助使用的工具而不是每次都要找数据团队核对口径。从最初 MySQL 硬查到 ClickHouse 初试 OLAP再到 StarRocks 落地多源数据底座最后收敛到规则兜底 模型加权的混合决策体系——这个演进过程其实没有一步是一步到位的每一步都是被业务需求推着往前走再通过实践中踩到的坑来修正方向。如果让我给正在做类似架构的人一句总结那就是营销自动化的核心不是自动化本身而是数据能否在正确的时间、以正确的形态、被正确的模型消费。OLAP 只解决了能查、查得快真正让数据产生业务价值的是整个链条从采集、治理、建模到决策的完整闭环。架构演进没有终点只要业务在变数据底座就永远有可以打磨的空间。