第四章:数据密集型架构——数据流处理与批流对比
大家好,我是老张。今天我们来聊聊数据密集型架构里最核心的一块——数据流处理。说实话,这块内容我讲了这么多年,每次都有新感悟。你想想看,现在的系统每天要处理多少数据?几TB都算少的。那怎么让这些数据又快又准地流转起来?这就是我们今天要解决的问题。
4.1 数据流处理的核心:Kafka与Flink
先说说Kafka。我个人习惯把它理解成一个「超级消息队列」。为什么说它超级?因为它能扛住百万级的消息吞吐,还能保证数据不丢。我在项目中遇到过最夸张的场景——双十一大促,每秒几十万条订单消息涌入Kafka,集群稳如泰山。嗯,那会儿我盯着监控面板,手心全是汗。
Kafka的核心概念:
- Topic(主题):消息的分类,就像文件夹
- Partition(分区):每个Topic可以拆成多个分区,实现并行处理
- Consumer Group(消费组):一组消费者协同消费,保证每条消息只被处理一次
- Offset(偏移量):记录消费者读到哪了,支持回溯消费
再来说Flink。Flink是真正的「流处理之王」。它跟Spark Streaming最大的区别是什么?Flink是真正的逐条处理,每条数据来了就处理,延迟在毫秒级。而Spark Streaming是微批次,攒一批再处理,延迟至少几秒。
我曾经帮一个金融客户做实时风控系统。他们之前用Spark Streaming,每次处理延迟5秒。5秒什么概念?黑客都能把数据偷走了。换成Flink后,延迟降到100毫秒以内。客户当场就拍板了。
// Flink 流处理示例:实时计算每分钟的订单金额
DataStream<Order> orderStream = env.addSource(new FlinkKafkaConsumer<>("orders", ...));
orderStream
.keyBy(Order::getUserId)
.window(TumblingProcessingTimeWindows.of(Time.minutes(1)))
.aggregate(new AggregateFunction<Order, Double, Double>() {
@Override
public Double createAccumulator() { return 0.0; }
@Override
public Double add(Order order, Double acc) { return acc + order.getAmount(); }
@Override
public Double getResult(Double acc) { return acc; }
@Override
public Double merge(Double a, Double b) { return a + b; }
})
.print();
避坑指南:我曾经在Kafka消费者里忘记提交offset,结果重启后重复消费了几百万条数据。记住:一定要在业务处理完成后才提交offset,别图省事用自动提交。
4.2 批处理与流处理:到底选哪个?
这个问题我几乎每次面试都会问。说白了,批处理和流处理没有谁更好,只有谁更合适。
| 对比维度 | 批处理 | 流处理 |
|---|---|---|
| 延迟要求 | 分钟级到小时级 | 毫秒级到秒级 |
| 数据范围 | 处理全部历史数据 | 处理当前到达的数据 |
| 典型场景 | 日报、月报、数据仓库ETL | 实时监控、风控、推荐 |
| 计算引擎 | MapReduce、Spark、Hive | Flink、Storm、Kafka Streams |
| 资源消耗 | 集中式高消耗 | 持续低消耗 |
| 容错机制 | 重试整个批次 | 基于状态快照的精确一次 |
你想想看,如果你要做的是「昨天所有订单的汇总报表」,那用批处理就很好。但如果你要做的是「当前用户点击后立即推荐商品」,那必须上流处理。
我记得有个电商客户,他们之前所有报表都是T+1的批处理。后来老板要求实时看大屏数据,他们连夜上了Flink。结果发现,批处理和流处理的数据口径对不上——批处理是最终态,流处理是中间态。嗯,这里要注意,两种模式的数据一致性模型不一样。
重要提醒:不要试图用流处理引擎去跑批处理任务,也不要反过来。我见过有人用Flink跑全量数据ETL,结果内存直接爆了。每种引擎都有自己的适用场景,别乱用。
4.3 数据一致性模型:CAP与BASE
聊到数据一致性,就绕不开CAP理论。CAP说的是:一个分布式系统,最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)中的两个。
为什么会这样?你想想看,当网络分区发生时,节点之间无法通信。这时候你要么选择一致性(拒绝写入,等网络恢复),要么选择可用性(接受写入,但数据可能不一致)。鱼和熊掌不可兼得。
在实际项目中,我们通常选择AP或CP。比如ZooKeeper是CP系统,它宁可不可用也要保证数据一致。而Cassandra是AP系统,它优先保证可用性,数据最终会一致。
这就引出了BASE理论——Basically Available(基本可用)、Soft State(软状态)、Eventually Consistent(最终一致性)。说白了,BASE就是「别那么死板,允许系统暂时不一致,但最终会一致」。
我的实践经验:
- 强一致性场景:银行转账、库存扣减。必须用CP,数据错一点都不行
- 最终一致性场景:用户昵称修改、文章点赞。用AP,几秒后看到最新数据就行
- 弱一致性场景:实时排行榜、在线人数。用AP,有点误差无所谓
我曾经踩过一个坑:做一个电商订单系统,用了最终一致性模型。结果用户下单后,库存扣减延迟了5秒,导致超卖。嗯,从那以后,凡是涉及钱的系统,我都强制用强一致性。
4.4 知识体系总览
下面这张图是我自己画的,把本章的核心知识点串起来了。你仔细看看,就能明白数据密集型架构的全貌。
这张图把数据从产生到消费的完整链路都画出来了。你仔细看,数据源进来后,先经过消息队列缓冲,然后分流到流处理或批处理,最后根据业务需求选择不同的一致性模型。说白了,架构设计就是做选择题——选对了,系统稳如老狗;选错了,天天救火。
我的建议:刚开始做数据架构时,别想着一步到位。先用Kafka做消息缓冲,然后根据延迟需求决定用流处理还是批处理。一致性模型也先选最终一致性,等业务稳定了再考虑要不要升级到强一致性。循序渐进,别一口吃成胖子。
好了,这一章的内容就到这里。数据密集型架构的核心就是「选对工具,做对取舍」。Kafka负责缓冲,Flink负责实时,批处理负责离线,CAP/BASE负责保证数据正确。把这四样东西玩明白了,大部分数据场景你都能搞定。
可可精品资料库