第二章:数据源与目标——五花八门的“数据老家”

好,咱们进入正题。数据映射,说白了就是搞清楚数据从哪来、要到哪去。这一章,我就带你看看这些“数据老家”都长什么样。

我个人习惯把数据源分成四大类:关系型数据库、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(第三方服务)。映射的时候,得先统一数据格式,再合并。

我建议的步骤是:

  1. 先确定目标格式(通常是JSON或关系表)
  2. 为每个数据源写独立的映射规则
  3. 用唯一标识符(比如用户ID)关联不同数据源
  4. 处理冲突(比如两个数据源都有 user_name,以哪个为准?)

避坑指南: 我曾经在混合映射时,忘了处理时区。MySQL存的是北京时间,MongoDB存的是UTC,CSV文件里没有时区信息。结果合并后的数据,时间对不上。后来我加了一个时区转换层,所有时间统一转成UTC再存。记住:时间问题,永远是映射里的头号杀手。

嗯,这一章的内容就到这里。数据源与目标,说白了就是搞清楚数据的老家在哪、长什么样。下一章,咱们聊聊映射规则怎么写——那才是真正的技术活。