返回

mysqldump 数据迁移:快速导入不同表名 (4种方法)

mysql

使用 mysqldump 将数据导入到不同表中

直接进入正题, 碰到的问题是怎么把 MySQL 一个表的数据导出来,再灌到另一个表里。 两个表的结构都一样,就想把数据挪过去。 已经用 mysqldump 导出了数据, 文件里却有对原表名的引用, 不能直接用. 想找个简单有效的法子, 完成数据迁移。

一、 问题原因分析

mysqldump 默认导出内容包括建表语句和数据插入语句。--no-create-info 参数虽然能去掉建表语句, 但是插入语句里,还是包含了原表名。直接导入到新表,肯定会出错。核心在于导出的 INSERT 语句像这样:

INSERT INTO `users` (...) VALUES (...);

直接导入会把数据插入 users 表,而不是我们想要的 users_new 表。 我们需要的形式是:

INSERT INTO `users_new` (...) VALUES (...);

二、 解决方案

有好几个方法可以解决这个问题。下面一个一个说,每个方法都解释原理,配上代码和使用提示。

1. 使用 --table 参数 和 sed 替换

这是最简单直接的方法,导出的时候不做任何处理,导出后,直接替换文本中的表名。

原理:

sed 是一个流编辑器, 可以用来替换文本。我们可以用它来替换 INSERT 语句中的旧表名为新表名。

操作步骤:

  1. 导出数据:
  ```bash
  mysqldump -u root -p --no-create-info --skip-add-drop-table --skip-add-locks --skip-disable-keys --quick icicle_db users > users_existing.sql
  ```

 增加下列选项, 加快大表操作:

   * `--skip-add-drop-table`: 省略 `DROP TABLE` 语句 (避免误操作)。
   * `--skip-add-locks`: 导出过程不加锁.
   * `--skip-disable-keys`: 省略禁用/开启索引的操作
   * `--quick`: 快速导出大表数据(不一次性加载所有数据到内存).
  1. 使用 sed 替换表名:
  ```bash
  sed 's/INSERT INTO `users`/INSERT INTO `users_new`/' users_existing.sql > users_new.sql
  ```
    * `s/`: sed 的替换命令的起始。
    * `INSERT INTO \`users\``:  要被替换掉的原表名,  包含 "INSERT INTO"。
    * `INSERT INTO \`users_new\``:  新的表名,也要加上 `INSERT INTO`.
    * `/`: 替换命令的分隔符。
    * `users_existing.sql`:  输入文件.
    * `users_new.sql`:  输出文件.
  1. 导入新表:
  ```bash
  mysql -u root -p icicle_db < users_new.sql
  ```

安全建议:

  • mysqldump--skip-add-locks 选项在生产环境中要小心使用, 确保导出过程不会对业务造成影响。
  • 正式操作前,先在小数据量表上做测试。

2. 导出时直接使用 sed

这个方法可以把上面导出和替换两个命令合在一起, 一步搞定。

原理:

通过管道把 mysqldump 的输出直接送到 sed 命令处理。

操作步骤:

  1. 一条命令完成导出和表名替换:

    mysqldump -u root -p --no-create-info --skip-add-drop-table --skip-add-locks --skip-disable-keys --quick icicle_db users | sed 's/INSERT INTO `users`/INSERT INTO `users_new`/' > users_new.sql
    

    注意: 这条命令跟上一条命令, 只在于把输出到一个中间文件,改成直接管道处理.

  2. 导入到新表

    mysql -u root -p icicle_db < users_new.sql

3. 使用 mysql 客户端 --execute

可以利用mysql客户端直接插入。

原理:

我们可以将所有的数据行作为值构建为一个庞大的插入语句。这适用于中小表, 特别简单.

步骤:

  1. 先查询到需要的数据,并且格式化好,准备作为 values 使用.

    SELECT GROUP_CONCAT(
        CONCAT('(',
               id, ',',
               QUOTE(name), ',', -- 处理字符串
               QUOTE(email),
               ')'
              ) SEPARATOR ','
        )
    FROM icicle_db.users;
    
    
  • GROUP_CONCAT: 将查询出的每一行组合成一个字符串, 方便我们组合成 VALUES 后面的多个括号组合。
  • CONCAT: 将每个字段的值, 组成带括号的形式 (val1,val2)
  • QUOTE: 处理字符串中的特殊字符。避免 SQL 注入.
  • SEPARATOR ',' 每个values,之间,使用 , 分隔
  1. 将上面得到的结果(作为值列表),跟 INSERT INTO 新表名 拼起来, 组成完整的 insert 语句

  2. 使用mysql执行这个完整的插入语句:

 mysql -u root -p -e "INSERT INTO icicle_db.users_new (id, name, email) VALUES <将第一步执行得到的结果贴到这里> "
  • -e: 执行后面的字符串中的SQL语句。

把第一步的结果复制进去就可以.

进阶
第一步产生的values, 默认有最大长度控制。 使用下列方式扩大, 适应超长的单次插入

```sql
    -- 先扩大 GROUP_CONCAT 长度
SET group_concat_max_len = 1024 * 1024 * 10; -- 设置为10MB 或更大

    -- 然后再运行前面的查询, 生成INSERT语句需要的
SELECT GROUP_CONCAT(
    -- .... 其它保持不变 ...
    )
    );
```

缺点

  • 如果数据量过大, 会产生很长的单个语句. 可能会遇到数据库限制
  • 需要先导出一次.

4. 使用 LOAD DATA INFILE (进阶技巧)

如果对性能有很高的要求,或者数据量特别大,这个方法是首选. 但是操作稍复杂。

原理:

LOAD DATA INFILE 是 MySQL 导入数据的最快方式之一。

步骤:

  1. 从原表导出数据到 CSV 文件。

    SELECT *
    INTO OUTFILE '/tmp/users.csv'
    FIELDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"'
    LINES TERMINATED BY '\n'
    FROM icicle_db.users;
    
    
  • INTO OUTFILE: 指定导出的文件路径。
  • FIELDS TERMINATED BY ',': 字段之间用逗号分隔。
  • OPTIONALLY ENCLOSED BY '"': 字符串字段可以选择用双引号包围。
  • LINES TERMINATED BY '\n': 行之间用换行符分隔。
  • 导出路径通常位于 mysql secure-file-priv 路径, 可以查询: show variables like '%secure%';
  1. 将 CSV 文件导入到新表。

    LOAD DATA INFILE '/tmp/users.csv'
    INTO TABLE icicle_db.users_new
    FIELDS TERMINATED BY ','
    OPTIONALLY ENCLOSED BY '"'
    LINES TERMINATED BY '\n';
    
    
    • 文件路徑一定要正确。

安全建议:

  • 导出的文件 (/tmp/users.csv) 要注意权限, 避免信息泄露。
  • 用完后尽快删除临时文件.

进阶:

导出时候,直接指定表名。 这样不需要中间临时文件. mysql 命令行结合 sed.

    mysql -u root -p -N -s -e "SELECT * FROM icicle_db.users" | sed 's/\t/,/g' | mysql -u root -p -D icicle_db -e "LOAD DATA LOCAL INFILE STDIN INTO TABLE users_new FIELDS TERMINATED BY ','"

  • -N: 去掉列名.
  • -s: 简单输出, 非table格式.
  • sed 's/\t/,/g': 替换Tab为逗号。
  • LOAD DATA LOCAL INFILE STDIN :直接从管道输入.
    * 此种用法有客户端版本和配置限制,需要 local_infile=1
    在 my.cnf, 客户端也允许该功能。
可以添加下列设置来调整性能

  ```sql
   SET unique_checks=0;
  SET foreign_key_checks=0;

      -- 插入数据 ...

   SET unique_checks=1;
   SET foreign_key_checks=1;
  ```

总结

以上就是几种迁移数据的方法,各有优缺点,根据情况选一种就行:

  • 简单替换:适合中小表,数据量不大的。
  • 管道处理:跟上面差不多,减少一步文件操作.
  • 客户端组装INSERT:适合需要处理某些复杂条件.
  • LOAD DATA INFILE:大数据量下首选,性能最好。