第四章:数据密集型架构——数据流处理与批流对比

大家好,我是老张。今天我们来聊聊数据密集型架构里最核心的一块——数据流处理。说实话,这块内容我讲了这么多年,每次都有新感悟。你想想看,现在的系统每天要处理多少数据?几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 知识体系总览

下面这张图是我自己画的,把本章的核心知识点串起来了。你仔细看看,就能明白数据密集型架构的全貌。

数据密集型架构知识体系 数据源 日志/DB/传感器/用户行为 消息队列 Kafka / RabbitMQ / Pulsar 流处理 Flink / Storm / Kafka Streams 批处理 Spark / MapReduce / Hive 一致性模型 CAP理论:C/A/P 三选二 BASE:最终一致性 典型应用场景 • 实时风控:毫秒级检测异常交易 • 实时推荐:用户行为触发商品推荐 • 数据仓库ETL:批量清洗和转换数据 • 日志聚合:集中收集和分析日志 • 物联网:海量传感器数据实时处理 • 金融交易:保证数据强一致性 核心原则:选对工具,做对架构 低延迟选流处理,大数据量选批处理,关键数据强一致性

这张图把数据从产生到消费的完整链路都画出来了。你仔细看,数据源进来后,先经过消息队列缓冲,然后分流到流处理或批处理,最后根据业务需求选择不同的一致性模型。说白了,架构设计就是做选择题——选对了,系统稳如老狗;选错了,天天救火。

我的建议:刚开始做数据架构时,别想着一步到位。先用Kafka做消息缓冲,然后根据延迟需求决定用流处理还是批处理。一致性模型也先选最终一致性,等业务稳定了再考虑要不要升级到强一致性。循序渐进,别一口吃成胖子。

好了,这一章的内容就到这里。数据密集型架构的核心就是「选对工具,做对取舍」。Kafka负责缓冲,Flink负责实时,批处理负责离线,CAP/BASE负责保证数据正确。把这四样东西玩明白了,大部分数据场景你都能搞定。

可可精品资料库