架构设计技巧——数据迁移与架构推导

数据迁移与架构演进是系统升级的核心挑战,二者必须协同设计。以下是结合实战经验的数据迁移与架构推导方法论:


一、数据迁移与架构演进的共生关系

维度

架构演进驱动数据迁移

数据迁移反哺架构演进

目标

实现新架构落地

保障业务连续性

约束条件

新架构的技术栈、数据模型

存量数据结构、数据量、一致性要求

关键决策点

数据库选型、服务拆分策略

迁移窗口期、数据校验机制

失败影响

新系统无法正常运行

业务数据丢失/错乱

核心原则:迁移方案必须与新架构匹配,架构设计需兼容迁移路径


二、数据迁移关键流程(四阶法)


三、架构推导驱动的迁移设计(核心场景)

场景1:单体拆微服务

迁移挑战

解决方案

架构配合点

共享数据库解耦

按领域拆分数据库

定义服务边界(Bounded Context)

跨服务数据一致性

采用Saga分布式事务

事件驱动架构设计

历史数据归属划分

数据所有权映射表+数据路由中间件

服务发现机制扩展

迁移路径示例:

  1. 先拆读流量:双写旧库+新库 → 读请求路由到新库 → 验证 → 停旧库读
  2. 再拆写流量:事务日志捕获(CDC) → 新服务消费 → 灰度切流 → 旧服务下线

场景2:数据库转型(SQL→NoSQL)
# 数据结构转换示例(关系型→文档型)
def convert_user(sql_data):
    nosql_doc = {
        "user_id": sql_data["id"],
        "profile": {
            "name": f"{sql_data['first_name']} {sql_data['last_name']}",
            "contact": {
                "email": sql_data["email"],
                "phones": [sql_data["phone"]] 
            }
        },
        # 嵌套关联数据(替代JOIN)
        "orders": [get_orders(sql_data["id"])] 
    }
    return add_version_flag(nosql_doc)  # 添加版本标记兼容新旧数据

关键架构配合:

  1. 版本化数据模型:文档中嵌入_schema_version字段

读写分离适配层

public interface DataAccess {
    // 架构推导产生的抽象接口
    Object read(String id, DataFormat format); 
    void write(String id, Object entity);
}

场景3:分库分表扩容

迁移四步法:

  1. 逻辑分片:应用层路由规则生效(未物理拆分)
  2. 影子写入:同时写入新旧分片(双写)
  3. 历史迁移:通过select ... into outfile + load data 批量转移
  4. 流量切换:停双写 → 数据差异补偿 → 清理旧数据

📌 架构推导要点:分片键选择必须符合业务查询模式(如按user_id分片需避免跨分片join)


四、数据迁移的架构约束矩阵

迁移类型

可用性要求

一致性要求

推荐架构模式

典型工具

零停机迁移

★★★★★

★★★☆☆

双写+增量同步

Debezium, Kafka Connect

强一致性迁移

★★☆☆☆

★★★★★

事务日志冻结+全局锁

Flyway, Liquibase

大数据量迁移

★★★☆☆

★★★★☆

分批次迁移+校验点

Spark, Sqoop

云迁移

★★★★☆

★★★☆☆

在线复制+最终一致性

AWS DMS, Azure DMA

五、验证阶段的三重保障

数据一致性校验

/* 抽样验证脚本示例 */
SELECT 
  (SELECT COUNT(*) FROM old_table) AS old_count,
  (SELECT COUNT(*) FROM new_table) AS new_count,
  CHECKSUM_TABLE(old_table) AS old_checksum,
  CHECKSUM_TABLE(new_table) AS new_checksum;
  1. 业务语义验证
    • 新旧系统并行执行相同操作
    • 对比输出结果(日志/数据库状态)
  1. 流量回放测试
    • 捕获生产环境请求
    • 在新环境重放并监控异常

六、经典反模式与规避策略

反模式

后果

规避方案

直接覆写目标库

数据丢失且不可逆

始终保留源数据快照

未考虑数据时效性

迁移后立即出现数据不一致

设计增量数据捕获管道

忽略字段语义转换

业务逻辑错误

建立字段映射字典+自动化校验

单次全量迁移

系统长时间不可用

采用分阶段迁移(先全量后增量)

七、架构推导决策框架(ADR示例)

决策记录23:采用CDC实现零停机迁移

## 状态:已批准
### 背景
现有订单库需迁移至新集群,要求停机时间≤5分钟

### 方案比较
| 方案        | 停机时间 | 风险 | 实现复杂度 |
|------------|---------|------|-----------|
| 停机导出导入 | 8小时   | 低   | ★★☆☆☆     |
| **CDC同步** | **3分钟** | 中   | ★★★★☆     |
| 双写        | 无停机  | 高   | ★★★★★     |

### 决策
选择CDC方案,原因:
1. 满足SLA要求的5分钟窗口期
2. 通过Kafka持久化日志降低数据丢失风险
3. 配合流量切换脚本实现快速回滚

### 后果
- 需增加Debezium监控告警
- 迁移前需验证binlog格式兼容性

终极建议:
数据迁移本质是业务连续性技术演进风险控制的三角平衡。每次迁移都应产出三份文档:

  1. 迁移操作手册(详细步骤+回滚流程)
  2. 数据血缘图谱(新旧字段映射关系)
  3. 架构决策记录(关键选择的技术依据)

正如Martin Fowler所言:"数据迁移的成功不在于技术实现,而在于对业务含义的深刻理解"。您当前面临的具体迁移场景是什么?我们可以针对性地探讨解决方案。

数据迁移实战方法论

一、数据迁移

1.如何平滑地迁移数据库中的数据

数据迁移并不是简单的将数据从一个数据库拷贝到另一个数据库,可以通过 MySQL 主从同步的方式做到准实时的数据拷贝;也可以通过 mysqldump 工具将源库的数 据导出,再导入到新库。因为这两种方式只能支持单库到单库的迁移,无法支持单库到多库多表的场景。而且即便 是单库到单库的迁移,迁移过程也需要满足以下几个目标:

  • 迁移应该是在线的迁移,也就是在迁移的同时还会有数据的写入;数据应该保证完整性,也就是说在迁移之后需要保证新的库和旧的库的数据是一致的;
  • 迁移的过程需要做到可以回滚,这样一旦迁移的过程中出现问题,可以立刻回滚到源库,不会对系统的可用性造成影响。

如果使用 Binlog 同步的方式,在同步完成后再修改代码,将主库修改为新的数据库,这 样就不满足可回滚的要求,一旦迁移后发现问题,由于已经有增量的数据写入了新库而没有 写入旧库,不可能再将数据库改成旧库。一般来说有两种方案可以做数据库的迁移。

(1)“双写”方案

第一步:将新的库配置为源库的从库,用来同步数据;如果需要将数据同步到多库多表,那么可 以使用一些第三方工具获取 Binlog 的增量日志(比如开源工具 Canal),在获取增量日志 之后就可以按照分库分表的逻辑写入到新的库表中了。

第二步:需要改造业务代码,在数据写入的时候,不仅要写入旧库,也要写入新库。当然,基于性能的考虑,可以异步地写入新库,只要保证旧库写入成功即可。但是需要注意的是,需要将写入新库失败的数据记录在单独的日志中,这样方便后续对这些数 据补写,保证新库和旧库的数据一致性。

第三步:开始校验数据了。由于数据库中数据量很大,做全量的数据校验不太 现实。你可以抽取部分数据,具体数据量依据总体数据量而定,只要保证这些数据是一致的 就可以。

第四步:如果一切顺利,就可以将读流量切换到新库了。由于担心一次切换全量读流量可能 会对系统产生未知的影响,所以这里最好采用灰度的方式来切换,比如开始切换 10% 的流 量,如果没有问题再切换到 50% 的流量,最后再切换到 100%。

第五步由于有双写的存在,所以在切换的过程中出现任何的问题,都可以将读写流量随时切换 到旧库去,保障系统的性能。

第六步:在观察了几天发现数据的迁移没有问题之后,就可以将数据库的双写改造成只写新库, 数据的迁移也就完成了。


 

其中,最容易出问题的步骤就是数据校验的工作,所以建议你在未开始迁移数据之前先 写好数据校验的工具或者脚本,在测试环境上测试充分之后,再开始正式的数据迁移。 如果是将数据从自建机房迁移到云上,你也可以使用这个方案,只是你需要考虑的一个重要 的因素是:自建机房到云上的专线的带宽和延迟,你需要尽量减少跨专线的读操作,所以在 切换读流量的时候,你需要保证自建机房的应用服务器读取本机房的数据库,云上的应用服 务器读取云上的数据库。这样在完成迁移之前,只要将自建机房的应用服务器停掉,并且将 写入流量都切到新库就可以了。

这种方案是一种比较通用的方案,无论是迁移 MySQL 中的数据,还是迁移 Redis 中的数 据,甚至迁移消息队列都可以使用这种方式,在实际的工作中可以直接拿来使用。这种方式的好处是:迁移的过程可以随时回滚,将迁移的风险降到了最低。劣势是:时间周 期比较长,应用有改造的成本。

(2)级联同步方案

这种方案也比较简单,比较适合数据从自建机房向云上迁移的场景。因为迁移上云,最担心云上的环境和自建机房的环境不一致,会导致数据库在云上运行时,因为参数配置或者硬件 环境不同出现问题。所以会在自建机房准备一个备库,在云上环境上准备一个新库,通过级联同步的方式 在自建机房留下一个可回滚的数据库,具体的步骤如下:

  • 先将新库配置为旧库的从库,用作数据同步;
  • 再将一个备库配置为新库的从库,用作数据的备份;
  • 等到三个库的写入一致后,将数据库的读流量切换到新库;
  • 然后暂停应用的写入,将业务的写入流量切换到新库(由于这里需要暂停应用的写入, 所以需要安排在业务的低峰期)。

这种方案的回滚方案也比较简单,可以先将读流量切换到备库,再暂停应用的写入,将写流 量切换到备库,这样所有的流量都切换到了备库,也就是又回到了自建机房的环境,就可以 认为已经回滚了。

上面的级联迁移方案可以应用在,将 MySQL 从自建机房迁移到云上的场景,也可以应用 在将 Redis 从自建机房迁移到云上的场景,如果你有类似的需求可以直接拿来应用。这种方案优势是简单易实施,在业务上基本没有改造的成本;缺点是在切写的时候需要短暂 的停止写入,对于业务来说是有损的,不过如果在业务低峰期来执行切写,可以将对业务的 影响降至最低。


 

2.数据迁移时如何预热缓存

缓存迁移的重点是保持缓存的热度。Redis 的数据迁移可以使用双写的方案或者级联同步的方案,所以在这里不考虑 Redis 缓存的同步了,而是以 Memcached 为例来说明。

为了保证缓存 的可用性,可以部署多个副本组来尽量将请求阻挡在数据库层之上。数据的写入流程是写入 Master、Slave 和所有的副本组,而在读取数据的时候,会先读副本组的数据,如果读取不到再到 Master 和 Slave 里面加载数据,再写入到副本组中。那 么就可以在云上部署一个副本组,这样,云上的应用服务器读取云上的副本组,如果 副本组没有查询到数据,就可以从自建机房部署的主从缓存上加载数据,回种到云上的副本组上。

当云上部署的副本组足够热之后,也就是缓存的命中率达到至少 90%,就可以将云机房上 的缓存服务器的主从都指向这个副本组,这时迁移也就完成了。这种方式足够简单,不过有一个致命的问题是:如果云上的请求穿透云上的副本组,到达自 建机房的主从缓存时,这个过程是需要跨越专线的。这不仅会占用较多专线的带宽,同时专线的延迟相比于缓存的读取时间是比较大的,一般, 即使是本地的不同机房之间的延迟也会达到 2ms~3ms,那么,一次前端请求可能会访问十几次甚至几十次的缓存,一次请求就会平白增加几十毫秒甚至过百毫秒的延迟,会极大地 影响接口的响应时间,因此在实际项目中我们很少使用这种方案。但是,这种方案给了我们思路,让我们可以通过方案的设计在系统运行中自动完成缓存的预 热,所以,我们对副本组的方案做了一些改造,以尽量减少对专线带宽的占用。

改造副本组方案预热缓存 改造后的方案对读写缓存的方式进行改造,步骤是这样的:

  • 在云上部署多组 mc 的副本组,自建机房在接收到写入请求时,会优先写入自建机房的 缓存节点,异步写入云上部署的 mc 节点;
  • 在处理自建机房的读请求时,会指定一定的流量,比如 10%,优先走云上的缓存节点, 这样虽然也会走专线穿透回自建机房的缓存节点,但是流量是可控的;
  • 当云上缓存节点的命中率达到 90% 以上时,就可以在云上部署应用服务器,让云上的应 用服务器完全走云上的缓存节点就可以了。


 

3.数据同步技术选择

  • 监控预警:
    • DataPipeline有可视化的过程监控,提供多样化的图表,辅助运维,故障问题可实时预警。
    • Datax:依赖工具日志定位故障问题


 

  • 数据实时性:
    • DataPipeline:实时
    • Datax:定时
  • 实施与售后服务:
    • DataPipeline:原厂实施和售后服务
    • Datax:阿里开源代码,需自行实施、开发、维护
  • 数据清洗:
    • DataPipeline:围绕数据质量做轻量清洗
    • Datax:需要根据自身清晰规则编写清洗脚本,进行调用(DataX3.0 提供的功能)
  • 自动断点续传
    • DataPipeline:支持
    • Datax:不支持


 

二、架构推导方式

1.自底向上的架构推导

这些需求哪里来的?基本上有这三种产出:

  • 有些是来自产品方一拍脑袋产生的灵感
  • 有些是对数据进行了详细的分析产出的产品策略
  • 有些是当前产品中暴露的一个个问题

产品方的这些详细的需求来了之后,如何应对的呢?首先和产品方一起讨论产品方案的合理性,在产品方案合理的基础上,开始识别用例,开始了一系列软件工程领域方面的措施。

这张图中有几个重点:

  • 软件研发分成了两个阶段:
  • 分析阶段,也是我们常说的问题空间领域建模,关键的一步是业务概念模型的输出,而业务概念模型输出的前置条件是从需求中分解出合理的用例集合。
  • 设计阶段,也是我们常说的解决方案空间建模,以及应用逻辑架构。
  • 图中存在了箭头这个东西,说明了做架构推导的主要的思维路径,也说明做架构不需要拍脑袋,都是根据严密的逻辑推导出来的。

2.自底向上的架构方法

业务概念架构推导

(1)模型的 3 个层次

  • 业务概念模型,问题空间领域模型,信息模型是同样的意思,这个层次上的实体我们称之为概念实体,这部分内容是用在需求和业务分析上的,讨论业务概念模型时完全不需要考虑软件的实现,这个过程是一个分析过程,即使不做软件研发,做其他的研发,类似的分析过程也应该是有的。
  • 系统模型,解决方案空间领域模型,逻辑模型是同样的意思,这个层次上的实体,我们称之为系统实体,或者逻辑实体,就是各种类,这个是用在软件设计和软件研发上的。
  • 存储模型,数据模型,物理模型,在这里也是同样的意思,这个层次上的实体,我们称之为数据实体,或者物理实体,也是用在软件设计上。

这 3 个层次其实是从 3 个角度在看待问题,他们之间是自上而下的转换的关系,这里尤其要注意的两个词是:逻辑的,顺序的推导。这些不同层次的模型是应用逻辑架构的基础


 

(2)模型的推导

①用例集合推导概念模型

  • 根据用例集合推导业务概念模型
  • 根据用例中的动词和量词推导业务概念模型的关联关系
  • 在特定的边界内根据模型的职责归纳子域

这里业务概念模型如果没有分析正确,那么下面要搞清楚是不容易的,这个分析部分是软件逻辑架构设计的基础。这个环节需要理解业务,更需要掌握问题空间建模这一严谨的方法论,这样才能推导出合理的模型,整个过程是非常严谨的,非常符合逻辑的。

②对业务概念模型进行归纳

在模型产出之后,要对模型进行归纳。从而保证职责的高内聚,同时明确出来的两个子域的边界,保证模块和模块之间的低耦合。

  • 通过名词定义来进行归纳思维概念:如果多个模型都在围绕某个名词,那么我们倾向将这个名词提炼出来
  • 通过内聚的度量公式来进行归纳:业务模型图中,模型和模型连线(连线就是模型和模型连接线)数量除以模型的梳理得到的值比较大的,那么我们可以看做是内聚,这些连线比较紧密我们趋向将其放到一个模块中,连线不是那么密切的,我们趋向于将它们放置在不同的模块中。然后我们再观察 连线数 / 模型数 观察内聚度量是高了还是低了,通过这样的方式归纳完成之后,我们再来通过度量公式来度量各模块的内聚和耦合程度。
  • 其他归纳方式:如果划分出了基本模块,发现还有一些模型不确定应该放到哪些模块中,还可以使用创建者原则和信息专家原则来判断应该将该模型归纳如哪个模块。
(3)基础逻辑架构推导(软件设计阶段)

系统模型和应用逻辑架构都是用在软件设计阶段,其目的是用来指导软件的研发。接下来,分别从三个角度来阐述逻辑架构的生成:

  • 业务概念架构
  • 模型(系统模型和数据模型)
  • 流程(系统调用流和数据流)

整个过程存在以下关键点:

在业务概念架构的基础上推演应用逻辑架构。

根据流程和系统模型来完善应用逻辑架构。

①根据业务概念架构推演

业务概念架构图产出之后,基本上逻辑架构的初步模型就具备了。所以可以理解成,第一步就是把业务概念架构直接先搬到应用逻辑架构中来,此处就不用多阐述了。

②根据系统流程进行推演模块

当业务概念架构产出之后,逻辑架构的骨架初成,接下来就是在这个框架上去填充内容。第一步就是根据流程来进行模块划分。这里的方法就是,先根据业务流程,分解出系统时序图,根据时序图开始对模块进行归纳,从而得到粒度更大的模块。这是粒度比较细的根据流程划分模块的案例,在粒度更大的流程,此方法同样适用,看大家是工作在何种粒度上。通过流程来进行推导是我们日常工作必不可少的一部分,尤其当很多场景的流程具有业务共同点时,那么可以考虑提炼出这些业务共同点,以提升研发的效率。

③根据性能 & 稳定性 & 成本等进行提炼模块

3.逻辑架构是分粒度的

(1)逻辑架构颗粒度树


 

对于业务复杂的架构来说,粒度会更多,层次就会更多。这取决于N个研发资源投入在某个模块上的效率最高,而这个 N 在某个阶段的技术限制下应该是一个比较稳定的值。逻辑架构粒度树的 3 条原则:

  • 纵向上,任何一层次的模块的职责,都必须是下一层职责的概括
  • 横向上,同一层次的模块职责属于同一范畴
  • 横向上,同一层次的模块的边界清晰

(2)模块颗粒度树落地情况

模块树上的这些不同粒度的模块,在具体落地成物理架构时,可以是不同的形式,如下:

  • 可能是子包
  • 可能是顶层的包
  • 可能是应用
  • 可能是一组应用的集合,负责某种职责
  • 也可能是某个平台(如营销平台,商品中心等)
  • 更有可能更大层次的平台,比如 B2C
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值