实时数据技术栈入门:消息队列、流处理引擎与实时存储
实时数据产品化,说白了就是让数据从产生到被消费的时间窗口缩到最短。这个链条上有三个核心环节:消息队列负责传输,流处理引擎负责计算,实时存储负责落地。我这些年做过的实时项目,几乎都绕不开这三件套。
今天咱们就把这三个东西拆开看看。不追求大而全,只讲你上手必须知道的核心概念。
消息队列(Kafka)基础概念
消息队列是整个实时 pipeline 的「血管」。数据从各个源头打进来,先到消息队列里排队,下游的消费者再按需取走。Kafka 是当前最主流的选型,没有之一。
Kafka 的核心角色
- Producer(生产者):把数据发到 Kafka 的客户端。比如埋点日志、数据库变更事件。
- Broker(代理节点):Kafka 集群里的每一台服务器。数据就存在 Broker 上。
- Consumer(消费者):从 Kafka 拉取数据的客户端。比如 Flink 任务、日志采集程序。
- Topic(主题):数据的逻辑分类。好比数据库里的表,所有消息按 Topic 归类。
- Partition(分区):Topic 的物理分片。一个 Topic 可以拆成多个 Partition,分布在不同的 Broker 上,实现并行读写。
一句话记住:Producer 往 Topic 写消息,Consumer 从 Topic 读消息,Broker 负责存,Partition 负责拆。
为什么 Kafka 这么快?
我刚开始接触 Kafka 时也有这个疑问。后来看了它的存储设计才明白——顺序写盘 + 零拷贝。
传统消息队列写一条消息要随机寻道,Kafka 直接往文件末尾追加,磁盘顺序写的速度比随机写快好几个数量级。消费时又通过零拷贝技术直接把数据从磁盘映射到网卡,绕过了用户态内存拷贝。
嗯,这里要注意:Kafka 不保证消息的全局有序,只保证同一个 Partition 内的消息有序。如果你需要全局有序,就只能用单 Partition,但那样就失去了并行能力。这是个 trade-off。
Consumer Group 与 Offset
Consumer Group 是 Kafka 实现「一条消息被多个业务复用」的关键机制。同一个 Group 内的消费者共同消费一个 Topic,每条消息只会被 Group 内的一个消费者处理。不同 Group 之间互不干扰。
每个消费者会记录自己消费到了哪个位置,这个位置叫 Offset(偏移量)。Kafka 把 Offset 保存在一个内部 Topic 里(__consumer_offsets),所以消费者重启后能接着上次的位置继续消费。
避坑指南:我曾经遇到过一个线上事故——消费者处理消息时抛了异常,但代码里先提交了 Offset 再处理业务逻辑。结果消息没处理完,Offset 已经往前跳了,重启后那些异常消息再也消费不到了。正确的做法是:先处理业务,再提交 Offset,或者开启 Kafka 的「自动提交」但把间隔设得足够长。
流处理引擎(Flink / Spark Streaming)简介
消息队列里的数据是「死」的,需要流处理引擎把它「活」起来。目前市面上最主流的两个引擎是 Flink 和 Spark Streaming。
Flink:真正的实时流处理
Flink 的设计哲学是「每条数据来了就处理」,延迟通常在毫秒级。它的核心概念包括:
- DataStream:无限流数据的抽象。你写的所有算子(map、filter、keyBy)都是作用在 DataStream 上。
- Checkpoint:Flink 的容错机制。它会定期把算子的状态快照保存到外部存储(比如 HDFS)。如果任务挂了,重启后能从最近的 Checkpoint 恢复,做到 Exactly-Once 语义。
- Watermark:处理乱序事件的「时钟」。比如你统计过去 5 分钟的订单量,但有些订单因为网络延迟晚到了 2 分钟。Watermark 告诉 Flink:「等到某个时间点,不再等更晚的数据了,开始计算。」
个人经验:我做过一个实时大屏项目,要求展示「过去 5 分钟的用户点击量」。一开始没配 Watermark,结果晚到的数据导致指标反复跳动。后来加了 30 秒的 Watermark 延迟,大屏终于稳了。记住:Watermark 不是用来消除乱序的,而是用来定义「什么时候该出结果」。
Spark Streaming:微批处理的代表
Spark Streaming 的思路是把连续的数据流切成一个个小批次(Micro-Batch),每个批次作为一个 RDD 来处理。默认批次间隔是 1 秒,所以延迟在秒级。
它的核心概念:
- DStream:Discretized Stream,本质上是连续的 RDD 序列。
- Structured Streaming:Spark 2.x 之后推出的新 API,把流数据当作「不断追加的无限表」来处理,写起来跟批处理几乎一样。
Flink vs Spark Streaming:怎么选?
| 对比维度 | Flink | Spark Streaming |
|---|---|---|
| 处理模式 | 真正的逐条流处理 | 微批处理(本质是批) |
| 延迟 | 毫秒级 | 秒级(取决于批次间隔) |
| 状态管理 | 原生支持,功能强大 | 依赖外部存储或 Checkpoint |
| 乱序处理 | Watermark + 窗口机制,成熟 | 基于时间戳的窗口,稍弱 |
| 生态兼容 | 与 Kafka、Hive 等集成良好 | 与 Hadoop 生态无缝对接 |
| 学习曲线 | 中等偏难 | 相对平缓(有批处理基础) |
我的建议:如果你的业务要求毫秒级延迟(比如实时风控、交易监控),无脑选 Flink。如果延迟容忍度在 3-5 秒以内,且团队已经有 Spark 技术栈,Spark Streaming 完全够用。没必要为了「实时」而实时。
实时数据存储(Redis / ClickHouse)概览
数据算完了,得找个地方存起来供查询。实时场景下的存储选型跟离线完全不同——它要求写入快、查询也快,而且往往要处理高并发。
Redis:缓存与状态存储的首选
Redis 是内存数据库,读写都在内存里完成,单机 QPS 能到 10 万+。在实时链路里,Redis 最常见的两个角色是:
- 缓存层:把热数据(比如用户画像、商品详情)缓存起来,避免每次查询都穿透到后端数据库。
- 状态存储:Flink 任务的状态如果太大,可以配置用 Redis 作为状态后端。不过我个人更推荐 RocksDB,因为 Redis 的内存成本太高。
Redis 的数据结构很丰富:String、Hash、List、Set、Sorted Set、HyperLogLog……每个结构都有对应的使用场景。比如实时排行榜用 Sorted Set,UV 统计用 HyperLogLog。
注意:Redis 是单线程模型(6.0 之前),虽然处理单个请求很快,但如果有慢查询(比如 KEYS *),会阻塞所有后续请求。我在生产环境见过因为一个 KEYS 命令导致整个 Redis 集群雪崩的事故。线上永远不要用 KEYS,用 SCAN 代替。
ClickHouse:实时分析型数据库
ClickHouse 是列式存储数据库,专为 OLAP 场景设计。它的写入速度极快(每秒百万行级别),查询速度也快(秒级响应几十亿行数据的聚合查询)。
在实时数据产品里,ClickHouse 通常扮演「实时数仓」的角色:
- Flink 把清洗后的数据实时写入 ClickHouse
- BI 工具或大屏直接查询 ClickHouse 做聚合分析
- 支持 SQL 语法,业务方上手成本低
ClickHouse 的几个关键特性:
- MergeTree 引擎族:最核心的存储引擎,数据按主键排序,分区存储,支持 TTL 自动过期。
- 向量化执行:利用 CPU 的 SIMD 指令集,一次处理一批数据,而不是一行一行处理。
- 物化视图:预计算聚合结果,查询时直接读物化视图,速度更快。
避坑指南:ClickHouse 不适合高频的 point lookup(比如查某一条订单的详情),它更适合大范围的聚合查询。我曾经见过有人拿 ClickHouse 当 MySQL 用,每条查询都带 WHERE id = xxx,结果性能惨不忍睹。记住:ClickHouse 是分析引擎,不是事务数据库。
本章知识体系总览
下面这张图把三个核心组件的关系串起来了。你可以把它当作实时数据 pipeline 的「骨架」:
这张图里我刻意把 Flink 和 Spark Streaming 并列放在一起,因为在实际项目中,你很少会同时用两个引擎。选一个就行,关键是跟上下游打通。
好了,实时数据技术栈的三大件就聊到这儿。消息队列负责传输,流处理引擎负责计算,实时存储负责落地——这三者配合好了,你的实时 pipeline 就稳了八成。剩下的两成,靠的是监控、容错和持续调优。