4. 现有系统架构分析:梳理上下游依赖与数据流向

做数据迁移,最怕什么?

最怕你埋头干了大半个月,上线才发现漏了一个数据消费者。数据对不上,业务方找上门,回滚又得折腾半天。

所以,切入任何 SDK 改造项目,我的第一步永远是:把现有系统架构画出来。不是画给老板看的那种高瞻远瞩的架构图,是画给开发组自己看的、带箭头、带数据表、带调用次数的实打实依赖图。

说白了,你得搞清楚:数据从哪来?经过谁?最终落到哪?

4.1 系统架构全景图:先看全局

先别急着扎进代码细节。我建议你把整个系统的核心模块列出来,至少包含以下几层:

  • 接入层:负责接收客户端 SDK 上报的原始数据。通常是 Nginx 或 API Gateway。
  • 业务逻辑层:对原始数据进行清洗、校验、聚合。常见的是 Java/Go 写的微服务集群。
  • 数据存储层:包括关系型数据库(MySQL、PostgreSQL)、缓存(Redis)、消息队列(Kafka、RabbitMQ)。
  • 数据消费层:下游的数据分析平台、报表系统、推荐系统、运营后台等。

拿我最近改造的一个广告 SDK 项目举例。它的旧架构大概长这样:

旧架构依赖与数据流向概览 客户端 SDK 接入层 (Nginx/API) 业务逻辑服务 MySQL 主库 Kafka 消息队列 Redis 缓存 数据分析平台 实时报表 运营后台 说明 每条连接线代表一个同步或异步的数据流,改造时需评估每个流的切换时机。
个人习惯:画架构图时,我会用不同色块区分模块的“改造风险等级”。红色代表高风险(数据库直接依赖),黄色代表中等风险(消息队列),绿色代表低风险(纯计算服务)。这样一眼就能看出,改造的瓶颈通常在哪。

4.2 梳理上下游依赖:谁是直接依赖?谁是间接依赖?

全景图搭好之后,下一步是枚举每个模块的上下游。

我习惯用一张表格来记录,你想想看,如果靠脑子记,20 个微服务的依赖关系很快就把你搞晕了。

模块名称 上游生产者 下游消费者 依赖类型 数据格式
数据采集服务 客户端 SDK 清洗服务、原始日志库 强依赖(同步) JSON / Protobuf
清洗服务 数据采集服务 分析服务、MySQL 强依赖(同步 + 异步) Avro / Parquet
分析服务 清洗服务 实时报表、推荐引擎 弱依赖(异步可降级) JSON
离线数仓 MySQL Binlog / Kafka BI 报表、运营后台 弱依赖(T+1 任务) ORC / CSV

为什么这张表这么重要?因为我记得接过一个棘手的项目——客户端 SDK 要切换数据上报协议,从 JSON 改成 Protobuf。乍一看很简单,只在接入层改一下解析逻辑就行。但真翻出依赖表才发现,下游有三个历史遗留的日志解析脚本直接解析原始 JSON 字段!如果不在架构分析阶段发现这个问题,上线后那三个脚本会直接报错崩溃。

核心要点:依赖类型决定了你的迁移策略。
  • 强依赖(同步):改造时必须做兼容性测试,尽量采用“双写”或“灰度切换”。
  • 弱依赖(异步/离线):允许一定的延迟,可以先改造核心链路,再慢慢处理离线任务。

4.3 数据流向分析:追踪一条数据的一生

依赖搞清楚了,接下来轮到数据流。我会挑一条最核心的用户行为数据,从客户端生成开始,一直追踪到它最终被哪个报表使用。简单说,就是“数据的一生”。

举个例子,一条“用户点击广告”的事件:

  1. 客户端 SDK 打包点击事件,包含 userId、adId、时间戳、设备信息。
  2. 通过 HTTP POST 发送到接入层(Nginx)。
  3. 接入层校验签名后,写入 Kafka 的 click_raw 主题。
  4. 清洗服务消费 Kafka,调用用户画像服务补齐用户标签,写出到 click_clean 主题。
  5. 分析服务消费 click_clean,聚合出 5 分钟粒度的点击率,存入 Redis。
  6. 实时报表定时从 Redis 拉数据渲染。
  7. 离线数仓每晚从 click_clean 拉全量数据,跑次日留存分析。

这条路径上,哪些环节改造时会受影响?

  • 如果改客户端 SDK 数据格式,步骤 2(接入层)和步骤 3(Kafka 消息体)都要跟着变。
  • 如果只换消息队列(比如 Kafka 迁到 RocketMQ),步骤 3 及之后的所有消费者都得改连接配置。
  • 如果换数据库,步骤 5 的写入逻辑和步骤 7 的离线 ETL 都得调整。

梳理清楚这些,你就能精准评估“牵一发而动全身”的范围。

4.4 核心数据实体定义:数据字典不能省

光知道流向还不够。我见过太多团队,“用户 ID”这个字段,在不同的系统里叫 uid、user_id、userId、userid,甚至有的是 memberId。迁移时各种字段映射错误,数据质量掉到地上。

所以,在动手改造前,我强烈建议你整理一份核心数据字典。不要求全,先把迁移涉及到的字段统一起来。

标准字段名 旧系统命名 新系统命名 数据类型 说明
user_id uid / userid user_id STRING (UUID) 用户唯一标识
ad_id adId / promotion_id ad_id INT64 广告物料 ID
event_timestamp time / create_time event_timestamp INT64 (Unix ms) 事件发生时间戳
我曾经踩过的坑:在某个项目中,旧系统用字符串存时间戳(比如 "1716000000"),新系统统一用 INT64 毫秒级时间戳。迁移时忘了做类型转换,结果下游报表在排序时全部乱套,排查了整整一天才发现是数据解析异常。所以,数据字典不只是文档,它是迁移的“宪法”。

4.5 避坑指南:架构分析阶段最容易忽略的 3 个点

这部分是我自己的血泪史,分享出来给你避开。

  • 忽略隐式依赖。 什么是隐式依赖?代码里没写,但运维脚本、监控告警、定时任务直接读数据库表或日志文件。比如有个 DBA 手写的清理脚本,直接 truncate 了某张旧表。你改造把表名改了,脚本不更新,下周磁盘就可能爆满。
  • 低估消息队列的消费端延迟。 改造期间经常需要“双写”上下游两套系统。很多人只在代码层面做了双写,却没考虑消费者 backlog 堆积。如果双写期间生产速率翻倍,Kafka 的消费 lag 会飙高,造成数据实时性下降。
  • 没留够数据对齐时间。 新系统上线后,通常需要跑一段时间进行数据校验(新 vs 旧)。我对团队的要求是:至少留出 3 个完整的业务周期(比如 3 天)来做对比分析。别想着上线当天就切干净,99% 的情况都会翻车。

好了,架构分析阶段的核心内容就是这些。记住:画全图、列依赖、追数据、定字典。这四步做完,后续的改造方案才能站稳脚跟,不至于做到一半发现方向错了。