数据采集与接入:把非结构化数据“请”进系统
数据采集,说白了就是解决一个问题:数据在哪,怎么把它弄进来。非结构化数据的来源五花八门,不像关系库那样规规矩矩。我做了这么多年数据架构,见过最头疼的项目,往往不是存储不够,而是采集阶段就翻车了。
这一章,咱们就把常见的几种数据源、采集策略,以及怎么搭一条靠谱的数据管道,掰开揉碎聊清楚。
常见数据源:你面对的不是一个“数据库”
非结构化数据的来源,我习惯分成四大类。每一类的脾气都不一样,接入方式也天差地别。
| 数据源类型 | 典型代表 | 接入难点 |
|---|---|---|
| 文件系统 | NAS、HDFS、S3、本地磁盘 | 文件数量大、目录深、格式杂 |
| API | RESTful、GraphQL、gRPC | 限流、鉴权、数据量不可控 |
| 消息队列 | Kafka、RabbitMQ、Pulsar | 顺序保证、回溯、积压处理 |
| 数据库CDC | MySQL Binlog、MongoDB Oplog | 日志解析、断点续传、DDL兼容 |
1. 文件系统:最“原始”也最“坑”
很多企业的非结构化数据,最初就是一堆文件躺在服务器上。日志文件、图片、PDF、音视频……你想想看,一个目录下几十万个文件,光遍历一遍就要命。
我个人习惯的做法是:先做“文件清单”扫描,再按需拉取。别一上来就全量拷贝,网络带宽和磁盘IO都扛不住。
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。我建议:消息体只放元数据和存储路径,真正的文件内容走对象存储。
4. 数据库CDC:捕获“变化”的艺术
很多业务数据虽然存在关系库里,但关联的非结构化内容(比如用户头像、合同附件)往往只存了个URL。CDC(Change Data Capture)就是用来捕获这些URL的变化,然后触发后续的文件采集。
嗯,这里要注意:CDC不是银弹。MySQL的Binlog有ROW、STATEMENT、MIXED三种格式,非结构化数据场景下,我强制要求用ROW格式,否则你拿不到变更前后的具体值。
采集策略:全量、增量、实时,怎么选?
策略选择,取决于你的数据新鲜度要求和系统承载能力。我一般这么判断:
- 全量采集:首次建仓、数据迁移、离线分析。优点是简单,缺点是耗时、占资源。
- 增量采集:日常同步,只拿变化的数据。优点是轻量,缺点是需要“标记位”或“时间戳”。
- 实时采集:秒级甚至毫秒级响应,比如风控、推荐。优点是快,缺点是对系统压力大。
实际项目中,我很少只用一种策略。比如:首次全量 + 后续增量 + 关键业务实时。这叫“混合策略”,能平衡成本和时效。
数据管道设计:从源头到目标的“流水线”
数据管道,就是把采集、处理、传输、存储串起来的一条线。设计得好,事半功倍;设计得烂,天天救火。
我总结了一个“四层管道模型”,分享给你:
- 接入层:对接各种数据源,做协议适配、鉴权、限流。
- 缓冲层:用消息队列或内存队列做削峰填谷,防止下游被冲垮。
- 处理层:做格式转换、清洗、压缩、加密。
- 存储层:写入目标系统(对象存储、数据湖、搜索引擎)。
下面这张图,是我常用的一个非结构化数据管道架构,你可以参考:
管道设计的关键原则
- 解耦:层与层之间不要直接调用,用消息队列或缓冲区隔开。这样任何一层挂了,不会影响上下游。
- 可回溯:数据在管道里流转,必须能“重放”。我习惯给每条数据一个全局ID,方便排查问题。
- 监控告警:管道的每个环节都要有指标:吞吐量、延迟、错误率。我曾经因为没监控Kafka消费延迟,导致积压了3天的数据,业务方差点掀桌子。
避坑指南:那些年我踩过的“采集坑”
最后,分享几个真实教训,希望能帮你少走弯路:
- 文件采集时,别忘了“空文件”和“软链接”。我曾经扫目录时没处理软链接,结果循环引用,把磁盘空间撑爆了。
- API采集,一定要处理“分页断层”。有些API的分页是基于偏移量的,如果中间有数据删除,下一页就会漏数据。我后来改用基于游标的分页,才解决这个问题。
- CDC采集,别忽略“大事务”。MySQL一个事务更新了100万行,Binlog会生成巨量事件。如果不做拆分,内存直接OOM。我现在的做法是:按表或按行数拆分事务,分批处理。
数据采集是地基,地基不稳,上层建筑再漂亮也是白搭。把这一章的内容吃透,后面的存储、处理、分析才能站得住脚。