【实战手册】8000w数据迁移实践:MySQL到MongoDB的完整解决方案

🔥 本文将带你深入解析大规模数据迁移的实践方案,从架构设计到代码实现,手把手教你解决数据迁移过程中的各种挑战。

📚博主其他匠心之作,强推专栏

  • 小游戏开发【博主强推 匠心之作 拿来即用无门槛】

沉淀

文章目录

    • 一、场景引入
      • 1. 问题背景
      • 2. 场景分析
        • 为什么需要消息队列?
        • 为什么选择Redis?
        • 技术选型对比
    • 二、技术方案设计
      • 1. 整体架构
      • 2. 核心组件设计
        • 2.1 任务管理模块
          • 2.1.1 MigrationStarter
          • 2.1.2 MigrationTaskManager
        • 2.2 数据读取模块
          • 2.2.1 DataReader
          • 2.2.2 OrderMapper
        • 2.3 Redis队列模块
          • 2.3.1 RedisQueue
        • 2.4 消费者模块
          • 2.4.1 ConsumerManager
          • 2.4.2 ConsumerWorker
          • 2.4.3 MongoWriter
          • 2.4.4 MongoDBMonitor
          • 2.4.5 ConsumerProgressManager
    • 写在最后

一、场景引入

场景:需要将8000w条历史订单数据从原有MySQL数据库迁移到新的MongoDB集群中,你有什么好的解决方案? 大家可以先暂停,自己思考一下。

1. 问题背景

需要将8000w条历史订单数据从原有MySQL数据库迁移到新的MongoDB集群中,主要面临以下挑战:

  • 源库压力:直接读取大量数据会影响线上业务
  • 目标库压力:直接写入大量数据可能导致MongoDB性能下降
  • 数据一致性:确保迁移过程中数据不丢失、不重复
  • 迁移效率:需要在规定时间窗口内完成迁移
  • 异常处理:支持断点续传,避免异常导致全量重试

2. 场景分析

为什么需要消息队列?
  • 源库保护
    • 通过消息队列控制读取速度
    • 避免大量查询影响线上业务
  • 任务解耦
    • 将数据读取和写入解耦
    • 支持独立扩展读写能力
  • 流量控制
    • 控制写入MongoDB的速度
    • 避免瞬时高并发
为什么选择Redis?
  • 性能考虑
    • Redis的list结构天然支持队列操作
    • 单机QPS可达10w级别
  • 可靠性
    • 支持持久化,防止数据丢失
    • 主从架构保证高可用
  • 成本因素
    • 无需额外引入消息队列组件
    • 降低系统复杂度
  • 实现简单
    • 开发成本低
    • 维护成本低
技术选型对比
方案优势劣势是否采用
直接迁移实现简单压力大,风险高
Redis队列实现简单,成本低单机容量有限
Kafka吞吐量大,持久化好部署复杂,成本高
RabbitMQ功能完善过重,维护成本高

二、技术方案设计

1. 整体架构

MySQL(Source) -> Data Reader -> Redis Queue -> Consumer Workers -> MongoDB(Target)↑              ↑               ↑                ↑限流控制     队列监控告警     消费进度监控     写入状态监控

整个迁移过程说起来很简单:从MySQL读数据,写到Redis队列,消费者从Redis读取后写入MongoDB。但魔鬼藏在细节里,让我们一步步看看要注意什么:

MySQL数据读取
首先是MySQL这块,我们不能无脑读取。想象一下,如果不控制读取速度,直接大量读取数据,很可能会影响到线上正常业务。所以这里有两个关键点:

  1. 选择合适的时间。建议在业务低峰期,比如凌晨,这时候可以适当提高读取速率。
  2. 控制读取速度。通过监控MySQL的负载情况,动态调整读取速率。

读取方式
读取数据时要格外注意顺序问题。我们一般用创建时间和ID来排序:

  1. 先按创建时间排序
  2. 如果时间相同,再按ID排序
  3. 每次读取都记录当前的时间点和ID
  4. 下次继续读取时,就从这个位置开始

Redis队列控制
数据读出来了,不能直接往Redis里写。Redis也是有容量限制的,需要合理控制:

  1. 假设一条订单数据1KB(实际可能2-3KB)
  2. 单实例Redis一般4-8G,按8G算
  3. 建议只用30%给这个任务,也就是2.4GB
  4. 差不多能放2400万条数据,超过就要告警
  5. 如果队列积压严重,要停止写入,等消费者消费一些后再继续

数据写入流程
整个写入过程要严格保证可靠性:

  1. 读取数据
  2. 记录任务进度
  3. 写入Redis
  4. 这几步要在一个事务里完成
  5. 如果失败了要记录下来,方便重试

消费者处理
消费这块要特别注意几个问题:

  1. 从Redis读取数据
  2. 写入MongoDB
  3. 确认消费完成
  4. 记录消费进度
  5. 失败要支持重试
  6. 整个过程要保证幂等性,防止重复消费

MongoDB写入控制
最后写MongoDB时也要注意控制速度:

  1. 监控MongoDB的负载情况
  2. 根据负载动态调整写入速度
  3. 避免写入太快导致MongoDB压力过大

通过这样的设计,我们就能实现一个相对可靠的数据迁移方案。当然,实际实现时还需要考虑更多细节,比如异常处理、监控告警等。

2. 核心组件设计

在开始具体的代码实现之前,让我们先理解每个组件的职责和实现思路。

2.1 任务管理模块

首先来看任务管理相关的代码实现。这部分主要包含两个核心类:MigrationStarterMigrationTaskManager

2.1.1 MigrationStarter

这是整个迁移任务的入口类,负责创建任务并提交给任务管理器。它的主要职责是:

  • 生成唯一的任务ID
  • 创建迁移任务实例
  • 提交任务到管理器
/*** 数据迁移启动器* 作为整个迁移任务的入口*/
@Slf4j
@Component
public class MigrationStarter {@Autowiredprivate MigrationTaskManager taskManager; // 迁移任务管理器/*** 启动迁移任务* @param startTime 开始时间* @param endTime 结束时间*/public void startMigration(LocalDateTime startTime, LocalDateTime endTime) {// 1. 创建迁移任务String taskId = UUID.randomUUID().toString(); // 生成任务IDMigrationTask task = new MigrationTask(taskId, startTime, endTime); // 创建迁移任务// 2. 提交任务taskManager.submitTask(task); // 提交任务log.info("迁移任务已提交,taskId={}", taskId); // 日志记录  }
}
2.1.2 MigrationTaskManager

任务管理器负责任务的具体执行和生命周期管理。它实现了:

  • 线程池管理
  • 任务执行流程控制
  • 异常处理和重试机制
/*** 迁移任务管理器* 负责任务的调度和管理*/
@Slf4j
@Component
public class MigrationTaskManager {@Autowiredprivate DataReader dataReader;@Autowiredprivate MongoWriter mongoWriter;@Autowiredprivate RedisQueue redisQueue;// 线程池配置private final ExecutorService executorService = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000),new ThreadFactoryBuilder().setNameFormat("migration-pool-%d").build());/*** 提交迁移任务*/@Transactional(rollbackFor = Exception.class)public void submitTask(MigrationTask task) {executorService.submit(() -> {try {// 1. 初始化任务task.init();// 2. 执行数据读取while (task.hasNext()) {// 读取数据List<Order> orders = dataReader.readNextBatch(task);// 在事务中执行更新进度和写入Redis队列transactionTemplate.execute(status -> {try {// 先更新任务进度(持久化)task.updateProgress(orders);// 原子性写入Redis队列redisQueue.pushBatchAtomic(orders);return true;} catch (Exception e) {status.setRollbackOnly();throw new RuntimeException("处理批次数据失败", e);}});}// 3. 完成任务task.complete();} catch (Exception e) {log.error("任务执行异常", e);task.fail(e);}});}
}
2.2 数据读取模块

数据读取是整个迁移过程的起点,需要特别注意读取性能和源库压力控制。这部分包含两个关键类:

2.2.1 DataReader

数据读取器负责从MySQL中批量读取数据,实现了:

  • 动态批次大小调整
  • 读取速率控制
  • 异常处理机制
/*** 数据读取器*/
@Slf4j
@Component
public class DataReader {private final RateLimiter rateLimiter;@Autowiredprivate MySQLMonitor mysqlMonitor;private int currentBatchSize = 1000; // 初始批次大小private static final int MIN_BATCH_SIZE = 100;private static final int MAX_BATCH_SIZE = 5000;public DataReader() {// 初始速率设置this.rateLimiter = RateLimiter.create(100); // 每秒100个请求}/*** 动态调整读取速率*/private void adjustReadingRate() {if (!mysqlMonitor.checkMySQLStatus()) {// 降低速率和批次大小rateLimiter.setRate(rateLimiter.getRate() * 0.8);currentBatchSize = Math.</

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75828.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

运行小程序需要选择什么配置的服务器

主要是看有多少人浏览&#xff0c;如果是每天有几十个人浏览&#xff0c;通常2核或者4核就可以满足需求&#xff0c;内存的话建议4g或者8g&#xff0c;足够的内存可以使服务器同时处理多个请求&#xff0c;避免因内存不足导致的卡顿或程序崩溃。 硬盘存储方面&#xff0c;50GB…

基于SpringBoo的地方美食分享网站

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

Solidity私有函数和私有变量区别,私有变量可以被访问吗

web3面试题 私有函数和私有变量区别&#xff0c;私有变量可以被访问吗 ChatGPT said: 在 Web3 开发&#xff0c;尤其是使用 Solidity 编写智能合约时&#xff0c;关于私有函数和私有变量的区别是常见的面试题。下面是详细解析&#xff1a; ✅ 私有函数&#xff08;Private Fu…

mongodb 安装配置

1.下载 官网下载地址&#xff1a;MongoDB Community Download | MongoDB 2.使用解压包 解压包安装&#xff1a;https://pan.baidu.com/s/1Er56twK9UfxoExuCPlJjhg 提取码: 26aj 3.配置环境&#xff1a; &#xff08;1&#xff09;mongodb安装包位置&#xff1a; &#xf…

多模态大语言模型arxiv论文略读(十九)

MLLMs-Augmented Visual-Language Representation Learning ➡️ 论文标题&#xff1a;MLLMs-Augmented Visual-Language Representation Learning ➡️ 论文作者&#xff1a;Yanqing Liu, Kai Wang, Wenqi Shao, Ping Luo, Yu Qiao, Mike Zheng Shou, Kaipeng Zhang, Yang Yo…

[LeetCode 45] 跳跃游戏2 (Ⅱ)

题面&#xff1a; LeetCode 45 跳跃游戏2 数据范围&#xff1a; 1 ≤ n u m s . l e n g t h ≤ 1 0 4 1 \le nums.length \le 10^4 1≤nums.length≤104 0 ≤ n u m s [ i ] ≤ 1000 0 \le nums[i] \le 1000 0≤nums[i]≤1000 题目保证可以到达 n u m s [ n − 1 ] nums[…

前端面试宝典---闭包

闭包介绍 使用闭包&#xff1a; 在函数内声明一个变量&#xff0c;避免外部访问在该函数内再声明一个函数访问上述变量&#xff08;闭包&#xff09;返回函数内部的函数使用完毕建议闭包函数null;译放内存 function createCounter() {let count 0;return function () {coun…

GPT4O画图玩法案例,不降智,非dalle

网址如下&#xff1a; 玩法1&#xff1a;吉卜力&#xff08;最火爆&#xff09; 提示词&#xff1a;请将附件图片转化为「吉卜力」风格&#xff0c;尺寸不变 玩法2&#xff1a;真人绘制 提示词&#xff1a;创作一张图片&#xff0c;比例4:3&#xff0c;一个20岁的中国女孩…

4.12~4.14【Q】cv homework6

我正在写GAMES101作业6&#xff0c;在这段代码中&#xff0c;我十分想知道inline Intersection Triangle::getIntersection(Ray ray) 是由哪个函数&#xff0c;哪段代码调用的&#xff1f;什么是Inline&#xff1f;详细解释&#xff0c;越细节越好 我正在写GAMES101作业6&…

MATLAB双目标定

前言&#xff1a; 现在有许多双目摄像头在出厂时以及标定好&#xff0c;用户拿到手后可以直接使用&#xff0c;但也有些双目摄像头在出厂时并没有标定。因而这个时候就需要自己进行标定。本文主要介绍基于matlab工具箱的自动标定方式来对双目相机进行标定。 1、MATLAB工具箱标…

visual studio 常用的快捷键(已经熟悉的就不记录了)

以下是 Visual Studio 中最常用的快捷键分类整理&#xff0c;涵盖代码编辑、调试、导航等核心场景&#xff1a; 一、生成与编译 ​生成解决方案 Ctrl Shift B 一键编译整个解决方案&#xff0c;检查编译错误&#xff08;最核心的生成操作&#xff09;​编译当前文件 Ctrl F…

Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.

今天写项目的时候碰到一个报错&#xff0c;在网上查找到了解决方法&#xff0c;这里备份一下。防止下次再次遇到 原文章链接&#xff1a;Sass import rules are deprecated and will be removed in Dart Sass 3.0.0. 报错内容如下&#xff1a; Deprecation Warning: Sass i…

【QT】QWidget 概述与核心属性(API)

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Qt 目录 一&#xff1a;&#x1f525; 控件概述 &#x1f98b; 控件体系的发展阶段 二&#xff1a;&#x1f525; QWidget 核心属性 &#x1f98b; 核心属性概览&#x1f98b; 用件可用&#xff08…

Redis 在处理并发请求时,如何保证高效性和数据一致性

1. 单线程模型&#xff08;核心命令处理&#xff09; 单线程优势&#xff1a;Redis 的核心命令处理是单线程的&#xff08;基于内存操作&#xff0c;避免多线程竞争&#xff09;&#xff0c;所有命令按顺序执行&#xff0c;天然避免了多线程的锁竞争和上下文切换开销。非阻塞 …

flutter-Text等组件出现双层黄色下划线的问题

文章目录 1. 现象2. 原因3. 解决方法 1. 现象 这天我正在写Flutter项目的页面功能&#xff0c;突然发现我的 Text 文字出现了奇怪的样式&#xff0c;具体如下&#xff1a; 文字下面出现了双层黄色下划线文字的空格变得很大&#xff0c;文字的间距也变得很大 我百思不得其解&a…

cursor+高德MCP:制作一份旅游攻略

高德开放平台 | 高德地图API (amap.com) 1.注册成为开发者 2.进入控制台选择应用管理----->我的应用 3.新建应用 4.点击添加Key 5.在高德开发平台找到MCP的文档 6.按照快速接入的步骤&#xff0c;进行操作 一定要按照最新版的cursor, 如果之前已经安装旧的版本卸载掉重新安…

使用 IP 代理改 IP 后注意事项如何防封号

在使用一键换IP软件辅助网络营销账号切换时&#xff0c;需注意以下关键事项以确保账号安全并降低封号风险。 一、IP有效性及质量验证 确保更换的IP地址有效且质量高&#xff0c;低质量或失效的IP可能导致账号存活时间缩短。优先选择动态住宅IP&#xff08;如“兔子IP代理”提…

qt designer 创建窗体选择哪种屏幕大小

1. 新建窗体时选择QVGA还是VGA 下面这个图展示了区别 这里我还是选择默认&#xff0c;因为没有特殊需求&#xff0c;只是在PC端使用

数据可视化 —— 折线图应用(大全)

一、导入需要的库 # Matplotlib 是 Python 最常用的绘图库&#xff0c;pyplot 提供了类似 MATLAB 的绘图接口 import matplotlib.pyplot as plt import numpy as np import pandas as pd 二、常用的库函数 plt.plot(x轴,y轴)&#xff1a;plot()是画折线图的函数。 plt.xlabe…

ubuntu 20.04 安装源码编译 ros humble过程

公司要兼容ros1还需要ros2 这个时候不得不使用ubuntu20.04 安装 humble 但实际上在20.04上安装humble是需要在源码编译的。 根据这个帖子 https://blog.csdn.net/m0_62353836/article/details/129730981 重写一份,以应对无法下载的问题 系统配置 #检查是否为UTF-8编码,是则跳…