
1) 【一句话结论】在Spark Streaming中,通过Sliding Window(5分钟窗口+5分钟步长)结合Stateful Processing的updateStateByKey实现用户会话状态累加(而非仅取最后一个记录),利用Checkpoint机制保障容错,并动态调整Executor资源以适配实时数据量变化,最终输出活跃用户列表。
2) 【原理/概念讲解】老师口吻解释关键概念:
3) 【对比与适用场景】
| 窗口类型 | 定义 | 特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| Sliding Window | 窗口重叠,步长=窗口大小 | 数据可属于多个窗口 | 需跨时间窗口聚合(如每5分钟内访问) | 步长/窗口大小需合理设计,避免数据错配(如步长>窗口大小导致数据无法聚合) |
| Tumbling Window | 窗口不重叠,长度固定 | 数据只属于一个窗口 | 需精确按固定时间间隔聚合(如每5分钟一次) | 不支持跨窗口聚合,适合简单的时间切片 |
4) 【示例】
// 输入:用户访问日志 (userId, timestamp)
val lines = ssc.textFileStream("hdfs://input/user_logs")
val userLogs = lines.map(line => (line.split(",")(0).toInt, line.split(",")(1).toLong))
// 5分钟sliding window
val windowedLogs = userLogs.window(SlidingProcessingTimeWindow("5 minutes", "5 minutes"))
// 状态管理:更新用户访问次数和活跃状态(累加所有记录)
val activeUsers = windowedLogs.updateStateByKey[(Int, Boolean)] { (userId, values) =>
if (values.isEmpty) (0, false) // 初始状态
else {
val total = values.map(_._1).sum // 累加所有记录的访问次数
val isActive = total > 1 // 5分钟内访问>1则活跃
(total, isActive)
}
}
// 输出活跃用户列表
activeUsers.foreachRDD { rdd =>
val activeList = rdd.filter(_._2._2).map(_._1).collect()
// 输出到目标系统
val output = activeList.mkString("\n")
// 容错:Checkpoint保存状态
ssc.checkpoint("hdfs://checkpoint")
// 资源调度:动态调整Executor
val executorCount = rdd.partitions.size * 1.5.toInt // 示例公式
ssc.sparkContext.setExecutorCores(executorCount)
}
5) 【面试口播版答案】
“面试官您好,针对Spark Streaming实现实时用户活跃度分析,核心思路是通过Sliding Window(5分钟窗口+5分钟步长)结合Stateful Processing的updateStateByKey实现用户会话状态累加(而非仅取最后一个记录),利用Checkpoint机制保障容错,并动态调整Executor资源以适配实时数据量变化,最终输出活跃用户列表。具体来说,窗口计算用Sliding Window覆盖5分钟内的访问记录;状态管理通过updateStateByKey累加每个用户的访问次数,当5分钟内访问次数>1则标记活跃;容错方面开启Checkpoint,每10分钟保存一次快照;资源调度根据分区数动态调整Executor数量(如分区数*1.5),避免资源浪费。最后输出活跃用户列表到目标系统。”
6) 【追问清单】
7) 【常见坑/雷区】