
采用“流计算(Flink)+实时数据库(ClickHouse)”混合架构,通过日志采集(Kafka)、实时窗口计算(Flink)处理用户行为数据,计算次日留存率(今天活跃用户数/昨天活跃用户数)、付费转化率等指标,存储后支持实时API查询,并配置弹性扩容(Kafka分区、Flink并行、ClickHouse分片)应对游戏活动流量峰值。
老师解释,系统分为四层:数据采集、流处理、存储、查询,并配置弹性资源。
数据采集方式对比(日志采集 vs 数据库CDC):
| 方式 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| 日志采集(Flume/Kafka) | 通过SDK收集日志,写入消息队列 | 实时性高,可扩展,支持复杂日志格式 | 游戏实时行为(如点击、登录、充值) | 需处理日志格式统一,过滤无效数据(如无效用户ID、异常事件) |
| 数据库CDC(Debezium) | 监听数据库变更,推送变更数据 | 适用于关系型数据库,数据结构清晰,变更数据实时 | 游戏用户信息变更(如等级、付费记录、用户属性更新) | 需数据库支持CDC(如MySQL Binlog),仅适用于结构化数据变更 |
次日留存率计算伪代码(含数据清洗):
// 数据清洗:过滤无效用户ID(不在用户表中)和无效事件
DataStream<UserEvent> events = kafkaSource()
.filter(event -> event.userId != null && isUserValid(event.userId) && isValidEvent(event.eventType));
// 流处理:按用户ID分组,滑动窗口1天,计算留存率
DataStream<RetentionResult> result = events
.keyBy(user -> user.userId)
.window(TumblingEventTimeWindows.of(Time.days(1), Time.hours(0))) // 昨天的事件时间窗口
.apply(new ProcessWindowFunction<UserEvent, RetentionResult, String, TimeWindow>() {
@Override
public void process(String userId, TimeWindow window, Iterable<UserEvent> input, Collector<RetentionResult> out) {
long todayActive = 0; // 昨天登录且今天登录的用户数
long yesterdayActive = 0; // 昨天登录的用户数
for (UserEvent e : input) {
if (e.eventType == "login" && e.timestamp >= window.getEnd() - Time.days(1)) {
todayActive++; // 今天活跃
}
if (e.eventType == "login" && e.timestamp >= window.getEnd() - Time.days(2)) {
yesterdayActive++; // 昨天活跃
}
}
double retention = yesterdayActive > 0 ? (double) todayActive / yesterdayActive : 0;
out.collect(new RetentionResult(userId, retention));
}
});
result.addSink(new ClickHouseSink()); // 存入ClickHouse
面试官好,我来设计一个游戏用户行为实时分析系统。核心是构建“流计算+实时数据库”架构,用于实时计算用户次日留存率(今天活跃用户数除以昨天活跃用户数)、付费转化率等指标,并支持实时查询。
首先,数据采集:游戏客户端通过SDK发送用户行为日志(如登录、充值、关卡完成),通过Flume或Kafka收集,形成实时数据流,日志先存入Kafka缓冲。然后,流处理:使用Flink对数据流进行窗口计算,比如按用户ID分组,设置滑动窗口(1天),计算每个用户昨天是否活跃(昨天登录)以及今天是否活跃(今天登录),得到留存率;付费转化率则是付费用户数除以活跃用户数。处理结果存入ClickHouse(列式存储,支持高并发查询),原始日志存入Kafka持久化。查询时通过API实时获取指标,例如调用GET /metrics/retention?date=2023-10-01。
应对流量峰值:流处理引擎(Flink)配置高并行度,数据库(ClickHouse)分片,活动期间增加Kafka分区数或启用Redis缓存热点指标(如留存率、转化率),确保系统在高流量下仍能保持低延迟。这样既能保证实时性,又能有效应对游戏活动带来的流量冲击。