2、数据采集层设计:全量/增量同步策略、Sqoop与DataX对比、Flume日志采集架构设计、Kafka高吞吐原理

数据采集层,说白了就是整个大数据 pipeline 的「水龙头」。水龙头开得不好,后面再牛逼的计算引擎也是白搭。我这些年踩过的坑,有一半都出在这一层——要么数据丢了,要么重复了,要么把源库压垮了。今天咱们就把这块掰开揉碎了讲清楚。

2.1 全量同步 vs 增量同步:什么时候该用哪个?

先问个问题:你第一次从业务库往数仓导数据,是全量跑还是增量跑?

答案其实很简单——第一次必须全量。但之后呢?

我个人习惯把同步策略分成三种场景:

  • 全量同步:数据量小(百万级以下),或者业务表没有时间戳、没有自增ID。比如一些配置表、字典表,每天全量拉一遍也没压力。
  • 增量同步:数据量大,且有明确的变更标记。最常见的是 update_time 字段或自增主键。我建议你优先用 update_time,因为自增ID在分库分表场景下容易出问题。
  • 全量+增量混合:比如第一次全量,之后每天增量。但要注意——如果业务库有物理删除,增量同步是感知不到的。我曾经遇到过客户删了订单,结果数仓里还躺着那条数据,对账怎么都对不上。

避坑指南:我曾经接手过一个项目,增量同步只依赖 update_time,结果业务方在凌晨批量修改了历史数据,update_time 被更新了,但增量任务已经跑完。第二天数据对不上,排查了整整两天。后来我加了一个「延迟15分钟启动」的机制,给业务变更留出缓冲窗口。

2.2 Sqoop vs DataX:两个工具的真实对比

这两个工具我都深度用过。说实话,没有绝对的好坏,只有合不合适。

对比维度 Sqoop DataX
底层原理 MapReduce 任务,依赖 Hadoop 环境 独立进程,纯 Java 实现
数据源支持 主流关系型数据库 + HDFS/Hive 更丰富,支持 ES、MongoDB、HBase 等
增量同步 支持 --incremental 参数,但只支持 append 和 lastmodified 需要自己写 SQL 条件,灵活性更高
性能 依赖 YARN 资源,大集群下吞吐高 单机多线程,适合中小规模
运维成本 需要维护 Hadoop 集群 轻量,一个进程搞定

说说我的真实感受。Sqoop 在 1.4.x 时代还算稳定,但到了 1.4.7 之后基本不更新了。而且它有个很坑的地方——内存控制不好。我记得有一次同步一张 5 亿行的表,Sqoop 的 mapper 直接把 YARN 的队列打爆了。后来我改成 DataX,用 channel 参数控制并发,稳稳的。

DataX 的优点是断点续传做得不错。你想想看,同步一张大表跑了 3 个小时,最后 5 分钟网络闪断,Sqoop 会直接报错退出,而 DataX 可以记录读取位置,下次从断点继续。这个功能在离线同步场景下太重要了。

我的建议:如果你的公司已经有 Hadoop 集群,且数据量在 TB 级以上,用 Sqoop 没问题。但如果是中小团队、数据量在 GB 级,或者需要对接多种数据源,直接上 DataX。我现在的新项目全部用 DataX,省心。

2.3 Flume 日志采集架构设计

Flume 这东西,说白了就是「日志搬运工」。但怎么搬得稳、搬得快,是有门道的。

先看一个典型的架构:

Agent1 (Web Server) --> Agent2 (聚合层) --> Kafka
         |                      |
    Source: TailDir       Channel: Kafka Channel
    Channel: File Channel Sink: Kafka Sink
    Sink: Avro Sink

这里有几个关键点:

  • Source 选型:采集日志文件,优先用 TailDir Source。它支持断点续传,不会丢数据。我见过有人用 SpoolDir Source,结果日志文件被误删了,数据直接断流。
  • Channel 选型:如果对数据可靠性要求高,用 File Channel。虽然慢一点,但数据不会丢。如果追求吞吐,用 Memory Channel。但要注意——Memory Channel 在 Agent 宕机时会丢数据。我曾经在线上环境吃过这个亏,后来全部改成 File Channel。
  • Sink 选型:最终数据进 Kafka,用 Kafka Sink。如果中间有聚合层,用 Avro Sink 做跨网络传输,性能比 Thrift 好。

注意:Flume 的 File Channel 默认 checkpoint 目录在 /tmp 下。我曾经遇到过服务器重启后 /tmp 被清空,导致 checkpoint 丢失,数据重复消费。一定要把 checkpoint 目录配置到持久化磁盘上。

2.4 Kafka 高吞吐原理:零拷贝与页缓存

Kafka 为什么能扛住百万级 TPS?说白了就两个核心:零拷贝页缓存

先说说传统的数据传输流程。假设你要把磁盘上的一个文件通过网络发出去:

磁盘 --> 内核缓冲区 --> 用户缓冲区 --> Socket 缓冲区 --> 网卡

数据在内核态和用户态之间来回拷贝了 4 次,CPU 全浪费在拷贝上了。

Kafka 用了 sendfile 系统调用,流程变成这样:

磁盘 --> 内核缓冲区 --> 网卡

数据直接从内核缓冲区发到网卡,完全绕过了用户态。这就是零拷贝。我做过压测,同样硬件条件下,开启零拷贝后吞吐提升了 30% 以上。

再说页缓存。Kafka 不把数据直接写到磁盘,而是先写到操作系统的页缓存(Page Cache)里。你想想看,内存的读写速度是磁盘的几百倍。消费者读数据时,如果数据还在页缓存里,直接从内存返回,根本不用碰磁盘。

个人经验:我曾经调优过一个 Kafka 集群,发现磁盘 IO 利用率一直很低,但吞吐就是上不去。后来排查发现是页缓存命中率太高了——数据刚写进去就被消费了,根本没机会落盘。后来我调整了 log.flush.interval.messages 参数,让数据在内存里多攒一会儿,批量刷盘,吞吐反而更高了。

还有一个容易被忽略的点:顺序读写。Kafka 的日志文件是追加写的,磁盘顺序写的速度比随机写快几个数量级。我见过有人把 Kafka 的数据目录放在机械硬盘上,结果性能惨不忍睹。后来换成 SSD,问题迎刃而解。

总结一下:Kafka 高吞吐的三大法宝——零拷贝减少 CPU 开销、页缓存利用内存加速、顺序读写发挥磁盘极限。这三者缺一不可。你在设计采集层时,一定要把 Kafka 的这些特性考虑进去,比如分区数、副本因子、消息大小等,都会影响最终性能。

好了,数据采集层就讲到这里。下一章咱们聊聊数据存储层的选型——HDFS、HBase、ClickHouse 到底该怎么选?到时候我会分享一些真实的踩坑经历。