数据源接入策略:结构化、半结构化与非结构化
聊到数据源接入,我脑子里第一个蹦出来的词就是「五花八门」。做了这么多年ETL,接过的数据源少说也有上百种。MySQL、Oracle、JSON、XML、CSV、日志文件、图片……每种都有它的脾气。
今天我就把这几类数据源的接入策略掰开揉碎了讲。你想想看,如果连数据都接不进来,后面的清洗、建模全是空谈。
结构化数据接入:MySQL/Oracle
结构化数据,说白了就是那种规规矩矩待在关系型数据库里的数据。有表、有字段、有主键、有外键。这类数据接入相对简单,但坑也不少。
全量接入 vs 增量接入
我个人习惯,第一次接入时用全量。把整张表拉过来,做个快照。但后续怎么办?
我建议用增量接入。为什么?因为全量太慢了。我在项目中遇到过一张订单表,每天新增200万条记录,全量拉一次要40分钟。后来改成增量,5分钟搞定。
增量接入的常见策略:
- 时间戳增量:利用表中的update_time或create_time字段,只拉取最近变更的数据
- 自增ID增量:记录上次拉取的最大ID,下次只拉取大于该ID的数据
- 日志解析增量:解析MySQL的binlog或Oracle的redo log,实时捕获变更
核心原则:能增量就别全量,能实时就别离线。但增量接入必须保证数据一致性,否则会出现数据丢失或重复。
连接池与并发控制
嗯,这里要注意。直接连数据库拉数据,很容易把源库拖垮。我曾经见过一个新手,开了50个线程同时拉Oracle,结果源库CPU直接飙到100%,业务系统挂了半小时。
正确的做法:
- 使用连接池,控制最大连接数(一般建议5-10个)
- 分页拉取,每页1000-5000条,避免一次拉取过多数据
- 设置超时时间,防止连接长时间占用
-- 分页拉取示例(MySQL)
SELECT * FROM orders
WHERE id > :last_id
ORDER BY id
LIMIT 1000;
数据类型映射
MySQL的datetime和Oracle的TIMESTAMP,到了大数据平台怎么映射?我踩过这个坑。
| 源数据类型 | Hive/Spark类型 | 注意事项 |
|---|---|---|
| MySQL DATETIME | STRING / TIMESTAMP | 注意时区转换,建议统一转为UTC |
| Oracle NUMBER(18,2) | DECIMAL(18,2) | 精度不能丢,否则金额对不上 |
| MySQL VARCHAR(255) | STRING | 长度限制可以放宽,但注意特殊字符 |
| Oracle CLOB | STRING | 大字段要单独处理,避免影响性能 |
避坑指南:我曾经因为没处理好Oracle的NUMBER类型,导致金额字段精度丢失,对账差了3万多块钱。从那以后,所有金额字段我都强制用DECIMAL(18,4)来存。
半结构化数据接入:JSON/XML/CSV
半结构化数据,说白了就是「有结构但不那么死板」。JSON可以嵌套,XML有属性,CSV字段顺序可能变。这类数据接入,核心在于「解析」和「扁平化」。
JSON数据接入
JSON现在太常见了。API接口返回的基本都是JSON。我建议的做法:
- 先做Schema推断,看看JSON里到底有哪些字段
- 嵌套结构要扁平化,比如把address.city变成address_city
- 数组类型要炸开(explode),一行变多行
-- Spark SQL解析JSON示例
SELECT
id,
name,
get_json_object(detail, '$.address.city') as city,
get_json_object(detail, '$.address.street') as street
FROM raw_json_table;
我在项目中遇到过一个问题:JSON里某个字段有时是字符串,有时是数组。比如tags字段,有时是"tag1,tag2",有时是["tag1","tag2"]。这种不一致最头疼。我的处理方式是:统一用数组解析,如果是字符串就先用split转成数组。
XML数据接入
XML现在用得少了,但金融、保险行业还在大量使用。XML的难点在于:
- 属性(attribute)和元素(element)混在一起
- 命名空间(namespace)处理麻烦
- 层级深,解析性能差
我建议用XPath来提取数据,比逐层解析快得多。
-- Hive解析XML示例
SELECT
xpath_string(xml_data, '//order/@id') as order_id,
xpath_string(xml_data, '//order/items/item/name') as item_name
FROM raw_xml_table;
CSV数据接入
CSV看着简单,其实坑最多。你想想看,字段里如果包含逗号怎么办?字段值里有换行符怎么办?
我总结了几条铁律:
- 指定分隔符,不要依赖默认(常见的有逗号、制表符、竖线)
- 处理引号转义,字段值用双引号包裹时,内部的双引号要转义
- 处理换行符,字段值内部可能包含换行,不能简单按行切分
- 处理BOM头,Windows生成的CSV文件开头可能有个不可见字符
小技巧:我一般用Spark的DataFrameReader来读CSV,设置option("escapeQuotes", "true")和option("multiLine", "true"),能解决90%的问题。
非结构化数据接入:日志/图片
非结构化数据,说白了就是「没有固定格式」的数据。日志文件一行一个格式,图片更是连文本都不是。这类数据接入,核心在于「提取有价值的信息」。
日志文件接入
日志文件是最常见的非结构化数据。Nginx日志、应用日志、系统日志……每种的格式都不一样。
我的接入策略:
- 先确定日志格式(Apache格式、JSON格式、自定义格式)
- 用正则表达式提取关键字段(时间、级别、来源、消息)
- 处理异常行(格式不对的行不能直接丢弃,要单独存储)
-- 解析Nginx日志示例(正则表达式)
^(\S+) (\S+) (\S+) \[([^\]]+)\] "([^"]*)" (\d{3}) (\d+) "([^"]*)" "([^"]*)"$
-- 提取字段:
-- $1: IP地址
-- $4: 时间戳
-- $5: 请求方法+路径
-- $6: 状态码
-- $7: 响应大小
嗯,这里要注意。日志文件接入最大的问题是「乱序」。比如凌晨2点的日志,可能因为网络延迟,凌晨3点才到达。我建议用事件时间(event_time)而不是处理时间(process_time)来做分区。
图片文件接入
图片接入,说白了就是「存起来,等后面用」。图片本身没有结构化信息,但它的元数据(文件名、大小、创建时间)是有价值的。
我建议的做法:
- 图片文件存到对象存储(HDFS、S3)
- 元数据存到数据库(文件名、路径、大小、MD5、上传时间)
- 后续通过OCR、图像识别提取内容
核心思路:非结构化数据接入,不是要把数据本身结构化,而是要把「描述数据的数据」(元数据)结构化。图片的内容,留给AI去处理。
接入策略总结
说了这么多,我最后总结一下:
| 数据源类型 | 接入方式 | 核心关注点 | 常见工具 |
|---|---|---|---|
| MySQL/Oracle | JDBC直连、CDC | 增量策略、连接控制、类型映射 | Sqoop、DataX、Canal |
| JSON/XML/CSV | 文件解析、API调用 | Schema推断、扁平化、异常处理 | Spark、Flume、Logstash |
| 日志/图片 | 文件采集、流式接入 | 格式解析、元数据提取、乱序处理 | Flume、Filebeat、Kafka |
数据源接入,说白了就是「把数据从A搬到B,同时保证数据不丢、不重、不乱」。听起来简单,做起来全是细节。我做了这么多年,每次接新数据源还是会遇到新问题。但只要你掌握了这些核心策略,大部分场景都能应对。
下一章,我会讲数据清洗的实战技巧。到时候再聊。