
1) 【一句话结论】在实时计算选型时,需从业务需求中提取延迟阈值、吞吐量、状态计算复杂度、容错要求等关键指标,Flink适合低延迟、高吞吐且需复杂状态计算的场景(支持Exactly-once语义),Spark Streaming适合对延迟要求不高、易用性优先的场景(支持At-least-once语义),最终选择需匹配业务核心指标和资源约束。
2) 【原理/概念讲解】老师口吻解释:流处理的核心是处理时间(数据到达时立即处理,延迟稳定但可能重复)与事件时间(数据产生的时间,延迟低但需时间戳)。Flink基于事件时间,数据流像“持续流动的河流”,状态(如用户行为计数)随数据更新持续维护,支持毫秒级延迟;Spark Streaming基于处理时间,将流切分为固定时间窗口(如秒级)的批处理任务,数据像“定时取水库的水”,延迟稳定但实时性弱于Flink。容错机制方面,Flink通过检查点实现Exactly-once(数据不重复不丢失),Spark Streaming通过重试实现At-least-once(数据可能重复)。
3) 【对比与适用场景】
| 对比维度 | Flink | Spark Streaming |
|---|---|---|
| 定义 | 基于事件时间的流处理引擎,支持持续计算和分布式状态管理 | 基于微批处理的流处理,将流切分为固定时间窗口的批处理任务 |
| 延迟 | 毫秒级(低延迟),事件时间处理 | 秒级(中等延迟),处理时间处理 |
| 吞吐量 | 高,无数据倾斜,资源利用率高 | 较高,可能存在数据倾斜,资源利用率一般 |
| 状态管理 | 内置RocksDB等持久化状态后端,Exactly-once语义 | 依赖外部存储(如HDFS),At-least-once语义 |
| 易用性 | 学习曲线陡峭,API复杂 | 易用性高,基于Spark生态,开发门槛低 |
| 容错机制 | Exactly-once(数据不重复不丢失) | At-least-once(数据可能重复) |
| 适用场景 | 金融风控、物联网实时分析、复杂状态计算 | 日志处理、简单实时报表、易用性优先场景 |
| 注意点 | 需关注状态存储(如RocksDB的I/O瓶颈) | 需处理数据倾斜(导致延迟波动) |
4) 【示例】
假设业务场景:金融交易实时风控,需每秒处理百万级交易并实时计算用户风险分数。
// 读取交易数据流
DataStream<Transaction> transactionStream = env.socketTextStream("localhost", 9999);
// 按用户ID分组,5秒滑动窗口聚合交易次数
transactionStream
.keyBy(Transaction::getUserId)
.window(TumblingEventTimeWindows.of(Time.seconds(5)))
.aggregate(new AggregateFunction<Transaction, UserAgg, UserStats>() {
@Override public UserAgg createAccumulator() { return new UserAgg(); }
@Override public UserAgg add(Transaction tx, UserAgg acc) { acc.transactionCount++; return acc; }
@Override public UserStats getResult(UserAgg acc) { return new UserStats(acc.transactionCount); }
@Override public UserAgg merge(UserAgg a, UserAgg b) { return new UserAgg(a.transactionCount + b.transactionCount); }
})
// 处理窗口结果,计算风险分数(假设风险模型为交易次数超过阈值则标记高风险)
.process(new ProcessWindowFunction<UserStats, RiskResult, String>() {
@Override public void process(String userId, Context ctx, Iterable<UserStats> inputs, Collector<RiskResult> out) {
UserStats stats = UserStats.from(inputs);
if (stats.transactionCount > THRESHOLD) {
out.collect(new RiskResult(userId, "High Risk"));
} else {
out.collect(new RiskResult(userId, "Low Risk"));
}
}
});
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
sc = SparkContext("local[2]", "TransactionRisk")
ssc = StreamingContext(sc, 1) # 1秒批次间隔
lines = ssc.socketTextStream("localhost", 9999)
user_events = lines.map(lambda line: line.split(",")[0]) # 解析用户ID
user_events_count = user_events.map(lambda uid: (uid, 1)).reduceByKeyAndWindow(lambda a, b: a + b, lambda a, b: a - b, 5, 0) # 5秒窗口
user_events_count.pprint()
(注:Spark Streaming的窗口聚合可能因处理时间导致延迟,且状态管理依赖HDFS,恢复成本高)5) 【面试口播版答案】
面试官您好,针对实时计算选型问题,我的决策过程是先从业务需求中提取关键指标:比如延迟要求(毫秒级)、吞吐量(百万级交易/秒)、状态计算复杂度(用户风险模型)、容错需求(Exactly-once)。首先,Flink基于事件时间处理,支持毫秒级延迟和复杂状态计算,且通过RocksDB持久化状态实现Exactly-once语义,适合金融风控这类对可靠性要求高的场景;而Spark Streaming基于处理时间,延迟稳定但可能存在数据重复,适合日志处理这类对延迟要求不高的场景。结合项目需求,比如我们项目需要每秒处理百万级交易并实时计算用户风险分数,Flink的低延迟、高吞吐和Exactly-once语义更匹配,所以最终选择Flink。在实际项目中,我曾通过将Flink的状态存储从内存改为RocksDB,并调整并行度为128,将延迟从150ms降低到30ms,同时保证数据不重复。
6) 【追问清单】
7) 【常见坑/雷区】