数据采集与接入:把非结构化数据“请”进系统

数据采集,说白了就是解决一个问题:数据在哪,怎么把它弄进来。非结构化数据的来源五花八门,不像关系库那样规规矩矩。我做了这么多年数据架构,见过最头疼的项目,往往不是存储不够,而是采集阶段就翻车了。

这一章,咱们就把常见的几种数据源、采集策略,以及怎么搭一条靠谱的数据管道,掰开揉碎聊清楚。

常见数据源:你面对的不是一个“数据库”

非结构化数据的来源,我习惯分成四大类。每一类的脾气都不一样,接入方式也天差地别。

数据源类型 典型代表 接入难点
文件系统 NAS、HDFS、S3、本地磁盘 文件数量大、目录深、格式杂
API RESTful、GraphQL、gRPC 限流、鉴权、数据量不可控
消息队列 Kafka、RabbitMQ、Pulsar 顺序保证、回溯、积压处理
数据库CDC MySQL Binlog、MongoDB Oplog 日志解析、断点续传、DDL兼容

1. 文件系统:最“原始”也最“坑”

很多企业的非结构化数据,最初就是一堆文件躺在服务器上。日志文件、图片、PDF、音视频……你想想看,一个目录下几十万个文件,光遍历一遍就要命。

我个人习惯的做法是:先做“文件清单”扫描,再按需拉取。别一上来就全量拷贝,网络带宽和磁盘IO都扛不住。

我的经验: 曾经有个客户,NAS上存了500万张医疗影像。他们直接用Python os.walk()去扫,跑了三天没跑完。后来我改成用Spark批量读取目录元数据,配合增量监听(inotify),半小时搞定全量扫描,后续增量秒级感知。

2. API:看似简单,实则“暗流涌动”

调用API拿数据,听起来很标准。但非结构化数据场景下,API返回的往往是JSON里嵌着Base64编码的图片,或者分页拉取时数据量忽大忽小。

这里有个避坑指南:一定要做“背压”和“重试”。我曾经对接一个第三方舆情API,对方限流策略是“1秒10次”,但返回的数据包大小从1KB到10MB不等。如果不做动态限流,要么被封,要么内存溢出。

// 伪代码:带背压的API采集
while (hasNextPage) {
    try {
        response = httpClient.get(url, params);
        if (response.status == 429) {
            // 被限流了,指数退避
            sleep(pow(2, retryCount) * 1000);
            retryCount++;
            continue;
        }
        process(response.body);
        // 动态调整请求间隔
        adjustRate(response.size());
    } catch (Exception e) {
        log.error("API采集失败,写入死信队列", e);
        deadLetterQueue.push(request);
    }
}

3. 消息队列:流式数据的“高速公路”

消息队列特别适合处理持续产生、需要低延迟的非结构化数据。比如用户上传的图片、IoT设备上报的日志、点击流数据。

Kafka是我用得最多的。但要注意,非结构化数据往往体积大,直接塞进消息体里会拖垮Broker。我建议:消息体只放元数据和存储路径,真正的文件内容走对象存储。

核心原则: 消息队列负责“传信”,对象存储负责“传货”。别让消息体超过1MB,否则性能断崖式下跌。

4. 数据库CDC:捕获“变化”的艺术

很多业务数据虽然存在关系库里,但关联的非结构化内容(比如用户头像、合同附件)往往只存了个URL。CDC(Change Data Capture)就是用来捕获这些URL的变化,然后触发后续的文件采集。

嗯,这里要注意:CDC不是银弹。MySQL的Binlog有ROW、STATEMENT、MIXED三种格式,非结构化数据场景下,我强制要求用ROW格式,否则你拿不到变更前后的具体值。

我曾经踩过的坑: 用Debezium监听MySQL Binlog,结果源库做了DDL操作(加了一列),CDC任务直接挂了。后来我加了DDL过滤和Schema Registry,才稳住。记住:CDC必须考虑Schema演化。

采集策略:全量、增量、实时,怎么选?

策略选择,取决于你的数据新鲜度要求和系统承载能力。我一般这么判断:

  • 全量采集:首次建仓、数据迁移、离线分析。优点是简单,缺点是耗时、占资源。
  • 增量采集:日常同步,只拿变化的数据。优点是轻量,缺点是需要“标记位”或“时间戳”。
  • 实时采集:秒级甚至毫秒级响应,比如风控、推荐。优点是快,缺点是对系统压力大。

实际项目中,我很少只用一种策略。比如:首次全量 + 后续增量 + 关键业务实时。这叫“混合策略”,能平衡成本和时效。

一个实用技巧: 增量采集时,别只依赖时间戳。万一服务器时钟不准,或者数据被修改但时间戳没变,你就漏数据了。我习惯加上“版本号”或“校验和”双重保障。

数据管道设计:从源头到目标的“流水线”

数据管道,就是把采集、处理、传输、存储串起来的一条线。设计得好,事半功倍;设计得烂,天天救火。

我总结了一个“四层管道模型”,分享给你:

  1. 接入层:对接各种数据源,做协议适配、鉴权、限流。
  2. 缓冲层:用消息队列或内存队列做削峰填谷,防止下游被冲垮。
  3. 处理层:做格式转换、清洗、压缩、加密。
  4. 存储层:写入目标系统(对象存储、数据湖、搜索引擎)。

下面这张图,是我常用的一个非结构化数据管道架构,你可以参考:

非结构化数据管道架构图 非结构化数据管道架构 文件系统 NAS / HDFS / S3 API REST / GraphQL 消息队列 Kafka / RabbitMQ 数据库CDC Binlog / Oplog 接入层:协议适配 / 鉴权 / 限流 / 路由 缓冲层:削峰填谷 / 消息持久化 / 回溯 处理层:格式转换 / 清洗 / 压缩 / 加密 存储层:对象存储 / 数据湖 / 搜索引擎 说明:每层都可以独立扩展,层与层之间通过消息队列解耦,方便水平扩容

管道设计的关键原则

  • 解耦:层与层之间不要直接调用,用消息队列或缓冲区隔开。这样任何一层挂了,不会影响上下游。
  • 可回溯:数据在管道里流转,必须能“重放”。我习惯给每条数据一个全局ID,方便排查问题。
  • 监控告警:管道的每个环节都要有指标:吞吐量、延迟、错误率。我曾经因为没监控Kafka消费延迟,导致积压了3天的数据,业务方差点掀桌子。
一句话总结: 数据管道设计,本质是“空间换时间”和“异步换稳定”。别追求一步到位,先跑通,再优化。

避坑指南:那些年我踩过的“采集坑”

最后,分享几个真实教训,希望能帮你少走弯路:

  • 文件采集时,别忘了“空文件”和“软链接”。我曾经扫目录时没处理软链接,结果循环引用,把磁盘空间撑爆了。
  • API采集,一定要处理“分页断层”。有些API的分页是基于偏移量的,如果中间有数据删除,下一页就会漏数据。我后来改用基于游标的分页,才解决这个问题。
  • CDC采集,别忽略“大事务”。MySQL一个事务更新了100万行,Binlog会生成巨量事件。如果不做拆分,内存直接OOM。我现在的做法是:按表或按行数拆分事务,分批处理。

数据采集是地基,地基不稳,上层建筑再漂亮也是白搭。把这一章的内容吃透,后面的存储、处理、分析才能站得住脚。