第二章:数据源与目标——五花八门的“数据老家”
好,咱们进入正题。数据映射,说白了就是搞清楚数据从哪来、要到哪去。这一章,我就带你看看这些“数据老家”都长什么样。
我个人习惯把数据源分成四大类:关系型数据库、NoSQL数据库、文件系统、API接口。每种都有它的脾气,映射的时候得对症下药。
2.1 关系型数据库:MySQL 与 PostgreSQL
关系型数据库,嗯,就是那种规规矩矩的表结构。行是记录,列是字段,主键外键约束得死死的。我最早接触的就是MySQL,那时候觉得它就像个Excel表格,只不过能存几亿行。
MySQL 的特点是轻量、快,适合中小型项目。但要注意,它的数据类型比较“朴实”,比如日期只有 DATE 和 DATETIME,没有 TIMESTAMP WITH TIME ZONE。映射的时候,如果目标系统要求时区信息,你就得自己处理。
PostgreSQL 呢?它更像一个瑞士军刀。支持数组、JSON、自定义类型。我在项目中遇到过,从PostgreSQL的JSON字段映射到MongoDB,反而比从MySQL映射更顺畅——因为PostgreSQL的JSON操作函数几乎和MongoDB一样强大。
核心映射要点:
- 主键映射:MySQL的AUTO_INCREMENT对应PostgreSQL的SERIAL或IDENTITY
- 日期类型:MySQL的DATETIME映射到PostgreSQL的TIMESTAMP,注意时区
- 字符串长度:MySQL的VARCHAR(255)在PostgreSQL里是VARCHAR(255),但PostgreSQL没有长度限制
我的小技巧: 映射关系型数据库时,先画ER图。别嫌麻烦,我吃过亏——有一次没画图,直接写映射脚本,结果漏了三个外键关系,数据对不上,排查了两天。
2.2 NoSQL数据库:MongoDB 与 Redis
NoSQL,说白了就是“不只有SQL”。它放弃了严格的表结构,换来了灵活性和性能。
MongoDB 存的是文档,类似JSON。每个文档的字段可以不一样,这给映射带来了挑战。你想想看,源系统里一个字段叫 user_name,目标系统里可能叫 username,甚至嵌套在 profile.name 里。
我曾经从MySQL迁移到MongoDB,最头疼的是关联查询。MySQL里用JOIN,MongoDB里得用 $lookup 或者干脆把关联数据嵌进去。映射的时候,你得决定:是保持引用关系,还是直接嵌入?
// MySQL 风格(关联查询)
SELECT u.name, o.order_date
FROM users u
JOIN orders o ON u.id = o.user_id
// MongoDB 风格(嵌入文档)
{
"name": "张三",
"orders": [
{ "order_date": "2024-01-01", "amount": 100 },
{ "order_date": "2024-01-02", "amount": 200 }
]
}
Redis 呢?它是个内存数据库,快得离谱。但它的数据结构很特别:字符串、哈希、列表、集合、有序集合。映射的时候,你得把关系型数据库的行转成Redis的哈希,或者把列表转成Redis的列表。
避坑指南: 我曾经把MySQL的一个大表(1000万行)直接映射到Redis的字符串,每个用户一个key。结果内存爆了。后来改成哈希结构,按用户ID分片,才搞定。记住:Redis不是万能的,它适合存热点数据,不是全量数据。
2.3 文件系统:CSV、JSON、XML
文件系统,嗯,最古老也最通用的数据源。你想想看,几乎每个系统都能导出CSV,每个API都能返回JSON。
CSV 是最简单的,但坑也最多。编码问题、逗号转义、换行符处理……我遇到过最离谱的一次,CSV里有个字段包含换行符,结果解析器直接把它当成新行,数据全乱了。映射CSV时,我建议用成熟的库,别自己写解析器。
JSON 是现在的宠儿。结构清晰,支持嵌套。映射JSON时,最常用的工具是 jq(命令行)或者Python的 json 模块。我习惯先看JSON的schema,再写映射规则。
// 源JSON
{
"user": {
"name": "李四",
"contact": {
"email": "lisi@example.com",
"phone": "13800138000"
}
}
}
// 目标JSON(扁平化)
{
"user_name": "李四",
"user_email": "lisi@example.com",
"user_phone": "13800138000"
}
XML 虽然老了,但还在用,尤其是金融、医疗行业。它的命名空间、属性、子元素,映射起来比JSON复杂。我建议用XPath或者XSLT来处理,别手动解析。
文件映射的通用原则:
- 先确定编码(UTF-8是首选)
- 处理空值(CSV的空字段、JSON的null、XML的缺失元素)
- 处理大文件(超过1GB,考虑流式读取)
2.4 API接口:REST 与 GraphQL
API接口,说白了就是通过网络拿数据。REST和GraphQL是两种主流风格。
REST 是资源导向的。每个URL对应一个资源,返回JSON或XML。映射REST API时,你得处理分页、限流、认证。我遇到过最头疼的是分页——有的API用 page 参数,有的用 cursor,有的用 offset。映射脚本得适配不同的分页策略。
GraphQL 呢?它让你只取需要的字段。这听起来很美好,但映射时有个坑:GraphQL的查询结果结构是动态的,你得根据查询语句来解析。我建议用GraphQL的introspection(自省)功能,先拿到schema,再写映射逻辑。
// REST API 响应
GET /api/users/123
{
"id": 123,
"name": "王五",
"email": "wangwu@example.com",
"created_at": "2024-01-01T12:00:00Z"
}
// GraphQL 查询
query {
user(id: 123) {
name
email
}
}
// 响应
{
"data": {
"user": {
"name": "王五",
"email": "wangwu@example.com"
}
}
}
我的经验: 映射API接口时,先写一个适配器层。这样,如果API版本升级或者换了供应商,你只需要改适配器,不用动核心映射逻辑。我吃过这个亏——有一次API从v1升到v2,字段名全变了,我改了三天才改完。后来学乖了,加了一层适配器。
2.5 多数据源混合映射
现实项目中,很少只有一个数据源。我做过一个项目,数据来自MySQL(用户信息)、MongoDB(行为日志)、CSV文件(历史数据)、REST API(第三方服务)。映射的时候,得先统一数据格式,再合并。
我建议的步骤是:
- 先确定目标格式(通常是JSON或关系表)
- 为每个数据源写独立的映射规则
- 用唯一标识符(比如用户ID)关联不同数据源
- 处理冲突(比如两个数据源都有
user_name,以哪个为准?)
避坑指南: 我曾经在混合映射时,忘了处理时区。MySQL存的是北京时间,MongoDB存的是UTC,CSV文件里没有时区信息。结果合并后的数据,时间对不上。后来我加了一个时区转换层,所有时间统一转成UTC再存。记住:时间问题,永远是映射里的头号杀手。
嗯,这一章的内容就到这里。数据源与目标,说白了就是搞清楚数据的老家在哪、长什么样。下一章,咱们聊聊映射规则怎么写——那才是真正的技术活。