事件分析平台设计

在广告业务中,我们的服务器每天都会收集到很多条用户行为日志。我们希望能有效地利用这些日志来揭示用户行为模式,以解答以下问题:

  • 在过去的三个月中,哪个广告渠道的用户注册量最大?
  • 上周,北京地区浏览广告的用户年龄分布是怎样的?
  • 最近一周,通过广告注册的用户,他们的一周留存率是多少?有何变化趋势?
  • 在过去的一周中,用户从接触广告到完成下单的各个环节的转化率如何?

为解答这些问题,我构建了一个事件分析平台。本文将首先概述平台的功能,然后深入讨论平台架构的一些思考

功能

总结来说,为了解答各种商业分析问题,事件分析平台提供了基于事件的指标统计、属性分组、条件筛选等功能的查询。在这里,“事件”是指用户行为,如点击广告、浏览商品、购买商品等。具体来说,事件分析平台支持「事件分析」,「漏斗分析」和「留存分析」这三种分析方式。

事件分析

事件分析是一种分析方法,用户可以通过设定一系列条件,查询特定指标,以解答特定的分析问题。这些条件包括:

  • 事件类型:这是指用户的行为,这些行为数据来自埋点数据。例如,点击某个广告,或者购买某个产品。
  • 指标:指标可以分为基础指标和自定义指标。
    • 基础指标:总次数(页面浏览量,PV),总用户数(独立访客,UV),人均次数(PV/UV)。
    • 自定义指标:事件属性 + 计算类型,例如「用户购买金额」的「总额/平均值/最大值」。
  • 过滤条件:这是用于筛选出我们关心的用户群体。
  • 维度分组:基于分组,可以进行组间的对比。
  • 时间范围:设定事件发生的时间范围。

以一个具体的例子来说明。假设我们想要解答这个问题:「最近一周,在北京地区,不同年龄段的用户在点击某个广告后,购买产品的平均金额是多少?」这个问题可以拆解为以下的事件分析:

  • 事件类型 = 点击某个广告并购买产品
  • 指标 = 购买产品的平均金额
  • 过滤条件 = 用户所在地区为北京
  • 维度分组 = 根据用户年龄段进行分组
  • 时间范围 = 最近一周

漏斗分析

漏斗分析是用于研究在一个多步骤的过程中,每一步的转化率和流失率。

以广告业务为例,用户的完整广告互动和购买流程可能包括以下步骤:浏览广告 -> 点击广告 -> 查看产品详情 -> 购买产品。我们可以将这个流程设定为一个漏斗,以便分析整个过程以及每个步骤的转化情况。

此外,漏斗分析还需要定义「窗口期」,即整个过程必须在设定的窗口期内完成,才算作一次成功的转化。与事件分析相似,漏斗分析也支持选择维度分组和时间范围。

留存分析

在留存分析中,用户定义初始事件和后续事件,并计算在发生初始事件后的第 N 天,发生后续事件的比率。这个比率能够有效地衡量广告用户的粘性。

举例来说,我们可能想要了解某个广告是否足够吸引用户。因此,我们可以设置初始事件为点击广告,后续事件为购买产品,留存周期为 7 天,进行留存分析。

架构

在架构上,广告业务的事件分析平台分为两个主要模块,如下图所示:

数据写入:广告点击或浏览的日志数据从客户端或服务器端上报后,经过 Kafka 消息队列,由 Flink 完成 ETL,然后写入 ClickHouse。
分析查询:用户通过前端页面,进行事件、条件、维度的选择,后端将这些条件组合成 SQL 语句,从 ClickHouse 中查询数据,然后展示给前端页面。

使用 ClickHouse 存储广告点击事件数据

广告业务的事件分析平台的数据来源主要有两类:

一类是来源于广告点击和浏览的日志数据,另一类是来源于「用户画像平台」的用户属性数据。本文只讨论关于广告点击和浏览日志数据的存储

在进行广告点击和浏览日志的存储选型前,我们首先明确了几个核心需求:

  • 支持海量数据的存储。广告业务每天产生的点击和浏览日志数据是海量的
  • 支持实时聚合查询。由于广告策略和运营团队会使用事件分析平台来探索多种用户行为模式,分析引擎必须能灵活且高效地完成各种聚合。

ClickHouse 在海量数据存储场景被广泛使用,高效支持各类聚合查询,配套有成熟和活跃的社区,促使我最终选择 ClickHouse 作为存储引擎。

根据对真实广告点击和浏览数据的测试,亿级数据的简单查询,例如广告点击量和独立访客数量,都能在 1 秒内返回结果;对于留存分析、漏斗分析这类的复杂查询,可以在 10 秒内返回结果。

解决了「数据存在哪里」的问题后,接下来回答「如何存储」的问题。ClickHouse 的列式存储结构非常适合存储大宽表,以支持高效查询

建表如下(简化了大部分字段):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE event_analytics
(
app_id String COMMENT '应用 ID',
open_id String COMMENT '用户 OPEN ID',
media String COMMENT '媒体',
channel String COMMENT '渠道',
event String COMMENT '事件类型',
event_date Date COMMENT '事件日期',
city String COMMENT '城市',
age UInt8 COMMENT '年龄',
...
)
engine = MergeTree
PARTITION BY toYYYYMMDD(event_date)
ORDER BY open_id
SETTINGS index_granularity = 8192
COMMENT '用户事件表';
查询 ClickHouse

可以使用 ClickHouse 的内置函数,轻松实现广告业务的事件分析平台所需要提供的事件分析、漏斗分析和留存分析三个功能。

事件分析可以用最朴素的 SQL 语句实现。例如,最近一周,北京地区的,发生过广告点击行为的用户,按照年龄段的分布,可以表述为:

1
2
3
4
5
6
7
8
9
10
SELECT
count(1) as cnt,
event_date as date,
age
FROM event_analytics
WHERE
event = "click_ad" AND
city = "beijing" AND
event_date >= toDate('2023-01-01') AND event_date <= toDate('2023-01-08')
GROUP BY (date, age);

留存分析使用 ClickHouse 提供的 retention 函数。例如,查看广告后,计算点击广告的次日留存、7日留存可以表述为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT
sum(ret[1]) AS original,
sum(ret[2]) AS next_day_ret,
sum(ret[3]) AS seven_day_ret
FROM
(SELECT
open_id,
retention(
event = "view_ad" AND event_date = toDate('2023-03-01'),
event = "click_ad" AND event_date = toDate('2023-03-02'),
event = "click_ad" AND event_date = toDate('2023-03-08')
) as ret
FROM event_analytics
WHERE
event_date >= toDate('2023-03-01') AND event_date <= toDate('2023-03-08')
GROUP BY open_id);

漏斗分析使用 ClickHouse 提供的 windowFunnel 函数。例如,在 查看广告 -> 点击广告,窗口期为2天的这个转化路径上,转化率的计算可以被表达为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SELECT
array( sumIf(count, level >= 1), sumIf(count, level >= 2) ) AS funnel_uv,
FROM (
SELECT
level,
count() AS count
FROM (
SELECT
open_id,
windowFunnel(2)(
event_date, event = "view_ad" AND event_date >= toDate('2023-03-01') AND event_date <= toDate('2023-03-08'), event = "click_ad") AS level
FROM
event_analytics
WHERE
event_date >= toDate('2023-03-01') AND event_date <= toDate('2023-03-08')
GROUP BY open_id
)
GROUP BY level
)

总结

在海量的数据集下使用 ClickHouse 自带 retention/windowFunnel 等函数运行速度更快、更高效。提升了现有技术中用户留存率、漏斗分析的计算方式速度慢效率低的问题,进而达到了提高计算速度和计算效率的效果


事件分析平台设计
https://sugayoiya.github.io/posts/45405.html
作者
Sugayoiya
发布于
2024年1月15日
许可协议