【實戰手冊】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.pswp.cn/web/75828.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75828.shtml
英文地址,請注明出處:http://en.pswp.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編碼,是則跳…