Nodejs实现MySQL数据迁移优化版本

Nodejs实现MySQL数据迁移优化版本

概述

主要是在之前写的基础版本上,加了异步和mysql2依赖的使用,同时使用了批量插入优化了迁移性能。下面做一个简单的演示,以下代码就不做详细的解释,注释已经大致讲明白了。

创建测试数据库

先建立目标数据库来测试

const mysql = require('mysql2/promise');

// 创建测试数据库
async function createTestDatabase() {
  const connection = await mysql.createConnection({
    host: 'target_host',    // 替换为你的目标数据库MySQL账号信息
    user: 'target_user',
    password: 'target_password' 
  });

  const databaseName = 'test_db';

  try {
    // 创建目标数据库
    await connection.query(`CREATE DATABASE IF NOT EXISTS ${databaseName}`);
    console.log(`Database "${databaseName}" created successfully!`);

    // 使用创建的数据库
    await connection.query(`USE ${databaseName}`);

    // 创建目标数据库用户表
    await connection.query(`CREATE TABLE IF NOT EXISTS simple_users (
      id INT PRIMARY KEY AUTO_INCREMENT,
      name VARCHAR(50) NOT NULL,
      email VARCHAR(50) NOT NULL
    )`);
    console.log('Table "simple_users" created successfully!');

  } catch (err) {
    console.error('Failed to create test database:', err);
  } finally {
    // 关闭数据库连接
    await connection.end();
  }
}

// 创建测试数据库和插入测试数据
createTestDatabase().catch((err) => {
  console.error('Test database creation failed:', err);
});

再建立源数据库,同时插入十万条测试数据

const mysql = require('mysql2/promise');

// 创建测试数据库
async function createTestDatabase() {
  const connection = await mysql.createConnection({
    host: 'source_host',// 替换为你的源数据库MySQL账号信息
    user: 'source_user',
    password: 'source_password' 
  });

  const databaseName = 'test_db';

  try {
    // 创建源数据库
    await connection.query(`CREATE DATABASE IF NOT EXISTS ${databaseName}`);
    console.log(`Database "${databaseName}" created successfully!`);

    // 使用创建的数据库
    await connection.query(`USE ${databaseName}`);

    // 创建源数据库用户表
    await connection.query(`CREATE TABLE IF NOT EXISTS users (
      id INT PRIMARY KEY AUTO_INCREMENT,
      firstName VARCHAR(50) NOT NULL,
      lastName VARCHAR(50) NOT NULL,
      email VARCHAR(50) NOT NULL
    )`);
    console.log('Table "users" created successfully!');
    // 插入10w条测试数据
    const testData = [];
    for(var i = 0; i < 100000; i++){
      testData.push(["Zhan"+i,"runyuan"+i,"EXAMPLE@qq.com"]);
    }
    await connection.query('INSERT INTO users (firstName, lastName, email) VALUES ?', [testData]);
    console.log(`${testData.length} users inserted successfully!`);
  } catch (err) {
    console.error('Failed to create test database:', err);
  } finally {
    // 关闭数据库连接
    await connection.end();
  }
}

// 创建测试数据库和插入测试数据
createTestDatabase().catch((err) => {
  console.error('Test database creation failed:', err);
});

执行完毕后,在源数据库MySQL中输入select count(*) from test_db.users;

可以看到十万条数据已经插入:

在这里插入图片描述

编写node迁移脚本

随后开始编写脚本进行迁移

// 引入依赖
const mysql = require('mysql2/promise');
const log4js = require('log4js');

// 配置日志,此处格式需要自定义
log4js.configure({
  appenders: {
    errorLog: { type: 'file', filename: 'error.log' }
  },
  categories: {
    default: { appenders: ['errorLog'], level: 'error' }
  }
})
const logger = log4js.getLogger()

// 数据迁移配置
const migrationConfig = {
  batchSize: 1000, // 每批处理的数据量
  sourceDB: {
    host: 'source_host', // 替换为你的源数据库MySQL账号信息
    user: 'source_user',
    password: 'source_password',
    database: 'test_db',
    connectionLimit: 10,  // 设置连接数限制
    queueLimit: 100,  // 设置队列限制
    acquireTimeout: 30000,  // 设置获取连接的超时时间为30秒
    waitForConnections: true,  // 等待连接可用
    acquireRetryCount: 3,  // 获取连接的重试次数
    acquireRetryWait: 1000,  // 获取连接的重试间隔时间为1秒
    supportBigNumbers: true,
    bigNumberStrings: true
  },
  targetDB: {
    host: 'target_host', // 替换为你的目标数据库MySQL账号信息
    user: 'target_user',
    password: 'target_password',
    database: 'test_db',
    connectionLimit: 20,  // 设置连接数限制
    queueLimit: 200,  // 设置队列限制
    acquireTimeout: 5000,  // 设置获取连接的超时时间为5秒
    waitForConnections: true,  // 等待连接可用
    acquireRetryCount: 5,  // 获取连接的重试次数
    acquireRetryWait: 500,  // 获取连接的重试间隔时间为0.5秒
    supportBigNumbers: true,
    bigNumberStrings: true
  }
};

// 创建源数据库连接池
const sourcePool = mysql.createPool(migrationConfig.sourceDB);

// 创建目标数据库连接池
const targetPool = mysql.createPool(migrationConfig.targetDB);

// 从源数据库连接池获取连接
async function getConnectionFromSourcePool() {
  try {
    return await sourcePool.getConnection();
  } catch (err) {
    console.error('Failed to get connection from source pool:', err);
    throw err;
  }
}

// 从目标数据库连接池获取连接
async function getConnectionFromTargetPool() {
  try {
    return await targetPool.getConnection();
  } catch (err) {
    console.error('Failed to get connection from target pool:', err);
    throw err;
  }
}

// 从源数据库中选择数据并迁移到目标数据库
async function migrateData() {
  let sourceConnection, targetConnection;

  try {
    sourceConnection = await getConnectionFromSourcePool();
    targetConnection = await getConnectionFromTargetPool();

    let offset = 0;
    while (true) {
      const [rows] = await sourceConnection.query(`SELECT * FROM users LIMIT ${offset}, ${migrationConfig.batchSize}`);

      if (rows.length === 0) {
        // 没有更多数据,数据迁移完成
        break;
      }

      const users = rows.map(row => [row.id, `${row.firstName} ${row.lastName}`, row.email.toLowerCase()]); // 对批量插入的数据进行数据转换

      try {
        await targetConnection.query('INSERT INTO simple_users (id, name, email) VALUES ?', [users]); // 批量插入
        users.forEach(user => {
          console.log(`User with ID ${user[0]} inserted successfully!`);
        });
      } catch (err) {
        // 处理迁移过程中的错误
        logger.error(`Error inserting users: ${users.map(user => user[0]).join(', ')}`, err);
        console.error('Failed to insert users:', err);
        // 可以选择继续处理下一批数据或者中断迁移
        continue;
      }

      offset += migrationConfig.batchSize;
    }

    console.log('Data migration completed successfully!');
  } catch (err) {
    console.error('Data migration failed:', err);
    throw err;
  } finally {
    // 释放连接回连接池
    if (sourceConnection) {
      sourceConnection.release();
    }
    if (targetConnection) {
      targetConnection.release();
    }
  }
}

// 执行数据迁移
migrateData().catch((err) => {
  console.error('Data migration failed:', err);
});

执行完毕后,十万条数据转化迁移成功,我源数据库和目标数据库用是都是双核2G的服务器,十万条数据迁移花了30s左右。

在这里插入图片描述

最后到目标数据库MySQL中输入select *count*(*) from test_db.simple_users;

可以看到十万条数据已经迁移:

在这里插入图片描述

源数据库数据:

在这里插入图片描述

目标数据库数据

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值