📋广告事件聚合系统设计笔记
type
status
date
slug
summary
tags
category
icon
password
Status

1. 系统概述与目标

系统定义: 广告事件聚合系统是一个用于收集、处理和统计广告相关事件(如展示、点击)数据的系统。其核心目标是提供近乎实时的广告效果指标,并存储历史聚合数据以供分析。
核心挑战: 该系统的主要挑战在于处理海量数据和高并发请求,同时保证数据的准确性和查询的低延迟。
规模假设:
  • 广告数量: 50 million (5000万) 不同的 ad_id
  • 事件量:
    • 点击事件 (Click Events): ~1 billion (10亿) / 天。
    • 展示事件 (Impression Events): ~100 billion (1000亿) / 天 (基于约 1% 的 CTR - Click-Through Rate)。
  • 总事件量: ~101 billion / 天 ≈ 100 billion / 天。
  • 平均 QPS (Queries Per Second): 100B events / (24 * 3600s) ≈ 1.16 million QPS。
  • 峰值 QPS: 假设为平均值的 3-5 倍,约为 3-5 million QPS

2. 功能性需求 (Functional Requirements - FRs)

  1. 实时指标计算: 计算指定 ad_id 在过去一段时间内(例如,最近几分钟)的聚合指标,包括:
      • 点击次数 (Click Count)
      • 展示次数 (Impression Count)
      • 点击率 (CTR = Clicks / Impressions)
  1. Top-K 广告: 展示最近一段时间内(例如,最近1分钟、10分钟、1小时)按特定指标(如点击量)排名的 Top-K 广告列表。
  1. 多维度聚合与过滤: 支持根据不同的维度或属性(例如,国家、用户设备、用户群体)对指标进行聚合和过滤查询。
  1. 历史数据存储: 将聚合后的分钟级指标数据持久化存储,并保留足够长的时间(例如,2年)。

3. 非功能性需求 (Non-Functional Requirements - NFRs)

  1. 可扩展性 (Scalability): 系统必须能够水平扩展以应对未来可能增长的数据量和流量。
  1. 高吞吐量 (High Throughput): 系统需要能够稳定处理峰值达到 3-5 million QPS 的事件写入。
  1. 低延迟 (Low Latency): 对于实时指标查询(FR1 & FR2),要求延迟小于 15 秒。这是一个比较严格的要求,尤其是在高吞吐量下。
  1. 数据准确性/完整性 (Data Correctness/Integrity): 由于广告数据直接关系到计费和预算,数据必须高度准确,不能出错或丢失。需要机制来确保最终一致性或进行对账。
  1. 容错性 (Fault Tolerance): 系统应能在部分组件或节点发生故障时继续运行,或能够快速恢复,保证数据不丢失。
  1. 高可用性 (High Availability): (隐含要求) 系统需要持续可用,尽量减少停机时间。

4. 数据量与带宽估算

  • 单事件数据模型 (Event Data Model):
    • ad_id: 广告ID
    • timestamp: 事件发生时间戳
    • event_type: 事件类型 ('click', 'impression', potentially others)
    • user_id: 用户标识 (抽象表示)
    • ip_address: 用户IP地址
    • dimensions: 其他维度信息 (e.g., country, device_type, demographics)
    • 估计大小: 10 bytes < Event Size < 100 bytes。为计算方便,假设 0.1 KB / event
  • 网络带宽 (Ingestion Bandwidth):
    • 平均: 1 million QPS * 0.1 KB/event ≈ 100 MB/s
    • 峰值: 3 million QPS * 0.1 KB/event ≈ 300 MB/s (峰值按 3M QPS 计算)
    • 结论: 网络带宽本身(几百MB/s)对于现代数据中心来说通常不是主要瓶颈。
  • 原始数据存储 (Raw Data Storage):
    • 日增量: 100 billion events/day * 0.1 KB/event ≈ 10 TB/day。
    • 月增量 (30天): 10 TB/day * 30 days ≈ 300 TB/month。
    • 考虑: 原始数据的存储时间可能受限于数据治理策略(如 GDPR 要求用户数据保留期限),30天是一个可能的参考值。
  • 聚合数据存储 (Aggregated Data Storage): (将在存储层详细计算)5. 高层架构设计 (High-Level Architecture)
一个典型的流式处理系统架构可以分为以下几个主要层次:
notion image
[图解标注 1: 高层架构图]
  • Data Collection: 负责接收来自前端或广告服务器的海量事件流。
  • Data Processing: 实时处理事件流,进行窗口聚合计算。
  • Data Storage: 存储处理后的聚合结果。
  • Query API: 提供接口供前端或其他服务查询聚合结果。

6. 详细组件设计与技术选型

6.1 数据收集层 (Data Collection / Ingestion)

核心挑战: 处理 3-5M QPS 的高并发写入,同时保证数据不丢失和系统稳定。
技术选型分析:
  1. 关系型数据库 (Relational DB - e.g., MySQL, PostgreSQL):
      • 评估: 完全不可行。无法承受百万级 QPS 的写入压力。[排除]
  1. NoSQL 数据库 (e.g., Cassandra, HBase, Time-Series DB):
      • 优点: 设计上支持高写入吞吐和水平扩展。
      • 缺点:
        • 集群规模: 假设单节点 15K W-OPS/sec,需要 3M / 15K = 200 个节点,集群规模庞大,运维复杂。
        • 热点问题 (Hotspot):
          • 写热点: 若以 ad_id 为分区键,热门广告会集中写入少数分区。缓解方法:加随机后缀 (ad_id_randomsuffix),但增加读取复杂度。
          • 读热点: 若后续处理需要按时间顺序读取(如最近5分钟数据),以 timestamp 作为 Sort Key 会导致最新数据集中在分区尾部,读取压力集中。缓解方法:更细粒度的分区(如按小时/分钟分区)、加 bucket。
          • notion image
        • 复杂度: 需要复杂的 Sharding、Bucketing 策略和可能的定制优化,增加系统复杂度和运维成本。[可行性低,复杂度高]
  1. 内存键值存储 (In-Memory KV Store - e.g., Redis):
      • 优点: 极高的读写性能(单节点可达 100K+ OPS/sec),所需集群规模较小(~15-30个节点)。
      • 缺点:
        • 持久化: 基于内存,需配置持久化机制(如 AOF, RDB)。异步持久化有数据丢失风险(在两次持久化间隔内宕机)。
        • 成本: 内存成本相对较高。
        • 数据模型: 主要适合 KV,复杂查询能力弱。[备选,但持久化和成本是顾虑]
  1. 消息队列 / 流处理平台 (Message Queue / Streaming Platform - e.g., Kafka, Pulsar): [推荐选项]
      • 优点:
        • 高吞吐设计: 本身就是为高吞吐、持久化日志流设计的(Kafka 单 Broker 可处理 100K+ events/sec)。集群规模适中(~30 个 Broker)。
        • 解耦与缓冲: 作为生产者和消费者之间的缓冲,削峰填谷,提高系统弹性。
        • 持久化: 提供良好的数据持久化保证(磁盘存储,可配置副本)。
        • 生态系统: 成熟的消费者 API,易于与下游流处理系统(Flink, Spark Streaming)集成。
      • 缺点:
        • 延迟: 相比直接写入 DB 或内存存储,增加了一层网络和处理延迟。
        • 热点问题: 同样存在分区键选择问题。以 ad_id 为 Key 会导致热点。解决方案:ad_id + random_suffix 或其他组合键,确保分区负载均衡。
        • 运维: 需要管理 Broker 集群(及 Zookeeper,如果使用 Kafka 旧版本)。
  1. 直接写日志文件 (Direct Log File Writing):
      • 优点:
        • 减少系统层级: 去掉消息队列层,可能降低端到端延迟。
        • 简化运维: 文件系统通常比分布式消息队列更容易管理(表面上)。
      • 缺点:
        • IO 瓶颈: 可能将瓶颈转移到磁盘 I/O。需要优化写入(如 batch flush),但这会增加延迟。
        • 文件管理: 需要处理文件滚动 (rotation)、合并、分发、跨节点协调等问题,复杂度不低。
        • 消费复杂性: 下游系统消费文件不如消费 Kafka topic 方便。
        • 压缩优势不明显: 存储相对廉价,压缩带来的成本节省可能不足以抵消复杂性增加。[可行性不高,潜在问题多]
结论: Kafka 是此场景下 Data Collection 层的优选方案,它在吞吐量、持久化、解耦和生态系统支持方面取得了较好的平衡。需要注意通过合理的分区策略(如 ad_id 加随机后缀)来避免热点问题。

6.2 数据处理层 (Data Processing / Real-time Aggregation)

核心挑战: 在 < 15 秒的延迟要求下,对来自 Kafka 的 ~1M QPS (平均) 数据流进行聚合计算。
技术选型分析:
  1. 批处理 (Batch Processing - e.g., Hadoop MapReduce, Spark Batch):
      • 评估: 延迟太高(小时级或天级),不满足 < 15 秒要求。[排除]
  1. 微批处理 (Mini-batch Processing - e.g., Spark Streaming):
      • 评估: 延迟可以做到秒级。理论上可能满足 15 秒要求,但窗口处理和批次间隔需要精心调优,在高负载下可能延迟抖动较大。[备选,但流处理更优]
  1. 流处理 (Streaming Processing - e.g., Apache Flink, Kafka Streams, Storm): [推荐选项]
      • 优点:
        • 低延迟: 提供毫秒级到秒级的事件处理能力,最适合 < 15 秒的延迟要求。
        • 事件驱动: 真正的按事件处理,状态管理更灵活。
        • 窗口计算: 内建对时间窗口(滚动、滑动、会话)的强大支持,契合需求(计算过去 N 分钟指标)。
        • 状态管理与容错: Flink 等框架提供强大的状态管理和 Checkpoint 机制,保证 Exactly-Once 或 At-Least-Once 语义。
      • 缺点:
        • 复杂度: 开发和运维门槛相对较高。
        • 资源消耗: 状态管理和 Checkpoint 会带来额外的资源开销。
流处理关键问题与解决方案:
  • 处理速度 < 输入速度 (Backpressure):
    • 依赖上游 Kafka 作为缓冲。
    • 流处理系统(如 Flink)应具备反压机制,通知上游减慢发送速度。
    • 配置自动扩缩容 (Auto-scaling) 处理节点以匹配负载。
  • 节点故障与恢复 (Fault Tolerance):
    • Checkpointing: 定期将算子状态快照持久化到外部存储(如 HDFS, S3)。节点故障后,从最近的成功 Checkpoint 恢复状态并重新处理后续数据。
    • 外部 Checkpoint 存储: 必须将 Checkpoint 存在独立于计算节点的可靠存储上。
  • Checkpoint 频率 vs. 延迟 vs. 恢复时间:
    • 高频 Checkpoint: 增加处理延迟和存储开销,但恢复快。
    • 低频 Checkpoint: 减少正常处理开销,但恢复慢,需重算更多数据。
    • 需要根据延迟要求和可接受的恢复时间进行权衡。
  • 是否需要 Flink Checkpoint (如果 Kafka 已有 Offset 管理):
    • Kafka Offset: 记录了消费到哪个位置,保证了数据源的不丢失不重复(如果消费者幂等或事务性写入)。
    • Flink Checkpoint: 保存的是 计算状态 (如窗口内的部分聚合值)。
    • 对于聚合计算: 必须使用 Flink Checkpoint (或类似机制)。如果 Flink Task 失败,仅从 Kafka Offset 恢复会丢失内存中的中间聚合状态,导致结果错误。
    • 优化可能: 如果聚合窗口很短(如1分钟),且上游 Kafka 数据保留时间足够长,理论上 可以在 Task 失败后,从 Kafka 上一个窗口的起始 Offset 开始重新计算整个窗口的数据来恢复状态。这避免了 Flink 自身状态持久化的开销,但恢复时间会变长(需要重读并计算整个窗口的数据)。考虑到 15 秒延迟要求,频繁的小窗口计算+快速恢复可能更倾向于使用 Flink Checkpoint。
    • notion image
  • 资源估算:
    • 假设 Flink 单 Task Manager (TM) 核心能处理 50K events/sec。
    • 需要 3M QPS / 50K events/sec/core ≈ 60 个处理核心 (分布在多个 TM 上)。集群规模可接受。
  • 热点问题 (Hotspot in Processing):
    • 如果上游 Kafka 通过加随机后缀打散热点,Flink 收到的数据应该是相对均匀的,处理层热点风险降低。
    • Flink 内部也可以进行 rebalance 或 keyBy 操作后的多并行度处理。
  • 窗口策略:
    • 使用滑动窗口 (Sliding Window),例如:窗口大小 1 分钟,滑动步长 10 秒。每 10 秒输出一次过去 1 分钟的聚合结果,满足 15 秒的刷新需求。
    • notion image
结论: Apache Flink 是此场景下 Data Processing 层的优选方案,其低延迟特性和强大的状态管理、窗口机制非常适合需求。需要仔细配置 Checkpoint 和资源。

6.3 数据存储层 (Data Storage / Aggregated Data)

核心挑战: 存储长达 2 年的分钟级聚合数据,并支持对近期数据的快速查询 (< 15 秒)。
数据模型 (Aggregated Table):
存储量估算:
  • 行数/分钟: 最多 50 million ad_id (实际上远小于,只有活跃的广告才产生数据)
  • 行数/天: 50M * 24 * 60 = 72 Billion (理论上限,非常夸张)
  • 更现实的估计:假设峰值时段有 10% 的广告活跃,平均每天有 1% 的广告活跃。活跃广告每分钟产生一条聚合记录。
  • 日增聚合记录数: (50M * 1%) * 24 * 60 ≈ 720 million rows/day (假设每个活跃广告每分钟都有数据)
  • 单行聚合数据大小: 假设包括各种维度和指标,估计为 100 bytes (0.1 KB)。 (用户估算的 50KB 可能过大,除非包含非常多的维度信息或者原始事件样本)
  • 日增存储 (聚合): 720M rows/day * 100 bytes/row ≈ 72 GB/day。
  • 两年总存储 (聚合): 72 GB/day * 365 days/year * 2 years ≈ 52.5 TB。 (这个数量级是合理的,远小于原始数据量) 如果按用户之前估算的 3-5TB,意味着每天活跃的广告或聚合粒度更粗,或单行更小。我们暂按 50TB 级考虑,更具挑战性。
技术选型分析:
  1. 关系型数据库:
      • 评估: 存储 50TB+ 数据并进行快速聚合查询(尤其带过滤条件)性能会很差。[排除]
  1. NoSQL (KV Store, Document DB):
      • 评估: 适合单点查询,但对于聚合、范围扫描、多维度过滤分析能力较弱。[不适合主要存储]
  1. OLAP (Online Analytical Processing) 数据库: [推荐选项]
      • 例子: ClickHouse, Apache Doris, Apache Pinot, Druid。
      • 优点:
        • 列式存储: 高效压缩,查询时只读取所需列,非常适合聚合计算。
        • 查询性能: 专门为分析查询优化,支持 SQL-like 接口。
        • 可扩展性: 支持分布式部署和水平扩展。
      • 缺点:
        • 单点写入/更新性能通常不如 OLTP 或 NoSQL。但我们主要是批量写入聚合结果,可以接受。
OLAP 查询性能优化 (< 15 秒):
notion image
  • 数据分区 (Partitioning): 必须按时间分区 (e.g., 按天或按月分区)。查询近期数据时,只需扫描少量分区。
  • 数据排序/索引 (Sorting/Indexing): 在分区内根据常用查询维度(如 timestamp_minute, ad_id)排序或建立索引(如 ClickHouse 的主键/跳数索引)。
  • 冷热数据分离 (Hot/Cold Data Tiering):
    • 热数据: 最近 7-30 天的数据(大约 0.5 - 2 TB)存储在高性能介质上(SSD)。
    • 内存加速: 针对极热数据(如最近1天,约 72GB)或常用维度组合的查询结果,可以考虑放入内存(如 ClickHouse 的内存表或操作系统的 Page Cache)。100-200GB 内存对于现代服务器是可行的。
    • 冷数据: 超过 30 天的数据存储在成本较低的 HDD 上。
  • 预聚合/物化视图 (Pre-Aggregation / Materialized Views):
    • 如果存在固定的、高频的查询模式(例如,按国家统计的总点击量),可以创建物化视图提前计算好结果。
  • 缓存 (Caching):
    • 在查询层(API Gateway 或应用层)增加缓存,缓存高频查询的结果。
    • 利用 OLAP 数据库自身的查询缓存。
结论: 选择一个高性能的 OLAP 数据库 (如 ClickHouse 或 Doris) 作为聚合数据存储层,并结合时间分区、排序键/索引、冷热分离、可能的物化视图和缓存策略,来满足 2 年存储和 < 15 秒查询延迟的需求。

6.4 查询接口层 (Query Interface / API)

  • 提供一个 API 服务(例如,基于 RESTful 或 gRPC)。
  • 该服务接收来自前端(仪表盘)或其他后端服务的查询请求。
  • 将请求转换为底层 OLAP 数据库的 SQL (或特定 DSL) 查询。
  • 执行查询并返回结果。
  • 实现认证、授权、限流等标准 API 网关功能。
  • 可以集成缓存逻辑。

7. 数据准确性与对账 (Data Correctness & Reconciliation)

问题背景: 流处理系统为了追求低延迟,可能面临事件乱序、事件迟到、处理错误、节点故障导致状态丢失(即使有 Checkpoint 也可能存在窗口边缘问题)等情况,导致实时结果与“真实”情况存在细微偏差。对于计费敏感的广告系统,需要机制来保证最终的数据准确性。
解决方案: 引入对账 (Reconciliation) 机制,定期基于原始数据进行全量或增量计算,修正实时结果。
两种主要架构模式:
  1. Lambda 架构:
      • 结构: 同时运行两条处理链路:
        • Speed Layer (速度层): 实时流处理(Kafka -> Flink -> OLAP),提供快速但可能不完全准确的结果。
        • Batch Layer (批处理层): 定期(如每天)运行批处理作业(如 Spark/MapReduce),读取一天内收集到的所有原始数据(可能存储在 HDFS 或对象存储中),进行精确计算。
        • Serving Layer (服务层): OLAP 数据库。批处理层的结果会 Upsert (Update or Insert) 到 OLAP 数据库中,覆盖或修正速度层写入的数据。
      • 优点: 鲁棒性高,批处理层作为“黄金标准”保证最终准确性。技术成熟。
      • 缺点:
        • 复杂度高: 需要开发和维护两套逻辑相似但技术栈不同的代码(流处理逻辑 + 批处理逻辑)。
        • 资源消耗大: 需要维护两套计算集群。
        • 逻辑同步困难: 保持两套代码逻辑完全一致是个挑战。
      • [图解标注 2: Lambda 架构图] (展示 Speed Layer 和 Batch Layer 并行处理,最终写入 Serving Layer)
      notion image
  1. Kappa 架构:
      • 结构: 只有一条流处理链路。对账通过数据回放 (Data Replay) 实现。
        • 原始数据存储在具有长保留时间的消息队列(如 Kafka)或日志存储中。
        • 当需要修正历史数据或进行对账时,从某个历史时间点开始,将原始数据重新注入(回放到)同一个流处理系统 (Flink) 中。
        • 流处理系统以“重算模式”运行,计算出修正后的聚合结果,并 Upsert 到 OLAP 数据库中。
      • 优点:
        • 架构简化: 只需要维护一套代码和一套处理引擎。
        • 资源效率: 避免了常驻的批处理集群。
      • 缺点:
        • 对流处理系统要求高: 需要流处理框架支持高效的数据回放、强大的状态管理和 Exactly-Once 语义保证。
        • 回放可能影响实时处理: 需要隔离回放任务与实时任务的资源,或在低峰期进行。
        • 无独立验证: 缺少了 Lambda 架构中独立的批处理层作为交叉验证。
      • [图解标注 3: Kappa 架构图] (展示单一流处理路径,并有从 Data Collection 回放数据到流处理引擎的循环路径)
      notion image
选择考虑:
  • 如果团队对流处理技术(如 Flink)掌握深入,且框架能力足够强大,Kappa 架构因其简洁性更受青睐。
  • 如果对数据准确性要求极高,且希望有独立的验证机制,或者流处理技术栈不够成熟,Lambda 架构可能是更稳妥的选择。
  • 对于广告计费场景,通常对准确性要求极高,可能会倾向于 Lambda 或具备非常强一致性保证的 Kappa 实现。

8. 总结与权衡

  • 广告事件聚合系统设计的核心在于平衡高吞吐 (3-5M QPS)、低延迟 (<15s) 和数据准确性这三个关键 NFR。
  • 推荐技术栈: Kafka (收集) -> Flink (处理) -> ClickHouse/Doris (存储) 是一个常见的、能够满足需求的组合。
  • 关键设计点:
    • 在 Kafka 层使用分区键+随机后缀缓解热点。
    • 在 Flink 层使用滑动窗口满足实时性,并配置外部 Checkpoint 保证容错。
    • 在 OLAP 层使用时间分区、冷热分离、索引/排序优化查询性能。
    • 引入Lambda 或 Kappa 架构进行数据对账,保证最终数据准确性。
  • 重要权衡 (Trade-offs):
    • 延迟 vs. 成本/复杂度: 选择流处理 (Flink) 获得了低延迟,但带来了更高的开发和运维复杂度。使用内存存储 (Redis) 可能延迟更低,但持久化和成本是问题。
    • 简单性 vs. 准确性: 简单的流处理可能无法保证 100% 准确,引入 Lambda/Kappa 增加了系统复杂度以换取准确性。
    • Checkpoint 频率 vs. 性能/恢复速度: 需要根据实际需求调整。
 
 
风控系统架构梳理 后端系统容灾与高可用设计指南
Loading...