61

这已被问过几次,但我找不到解决问题的方法。基本上,当使用 MySQL Workbench 管理工具的内置工具 mysqldump 时,当我使用扩展插入转储数据库时,我会得到大量长行数据。我理解它为什么这样做,因为它通过将数据作为一个命令插入(尤其是在 InnoDB 上)来加快插入速度,但是格式化使得实际上很难查看转储文件中的数据,或者使用差异工具比较两个文件如果您将它们存储在版本控制等中。在我的情况下,我将它们存储在版本控制中,因为我们使用转储文件来跟踪我们的集成测试数据库。

现在我知道我可以关闭扩展插入,所以我会得到每行一个插入,这是可行的,但是任何时候你使用转储文件进行恢复都会变慢。

我的核心问题是,在我转储文件时我们使用的旧工具(MySQL 管理员)中,它基本上做同样的事情,但它格式化 INSERT 语句以每行插入一个,同时仍然进行批量插入。所以代替这个:

INSERT INTO `coupon_gv_customer` (`customer_id`,`amount`) VALUES (887,'0.0000'),191607,'1.0300');

你得到这个:

INSERT INTO `coupon_gv_customer` (`customer_id`,`amount`) VALUES 
 (887,'0.0000'),
 (191607,'1.0300');

无论我尝试什么选项,似乎都没有任何方法能够得到这样的转储,这确实是两全其美。是的,它需要更多的空间,但在需要人工阅读文件的情况下,它会更有用。

我是否遗漏了一些东西,并且有一种方法可以使用 MySQLDump 做到这一点,或者我们都倒退了,旧的(现已弃用)MySQL Administrator 工具中的这个功能不再可用?

4

9 回答 9

42

尝试使用以下选项: --skip-extended-insert

它对我有用。

于 2013-08-02T07:06:16.493 回答
38

使用默认的 mysqldump 格式,转储的每条记录都会在转储文件(即 sql 文件)中生成一个单独的 INSERT 命令,每个命令都在自己的行中。这非常适合源代码控制(例如,svn、git 等),因为它使 diff 和 delta 分辨率更精细,并最终导致更有效的源代码控制过程。但是,对于非常大的表,执行所有这些 INSERT 查询可能会使从 sql 文件恢复的速度非常慢。

使用 --extended-insert 选项通过将所有记录包装到转储 sql 文件的单行上的单个 INSERT 命令中来修复多个 INSERT 问题。但是,源代码控制过程变得非常低效。整个表的内容在 sql 文件中的单行中表示,如果该表中的任何一个字符发生了变化,源代码控制会将整行(即整个表)标记为版本之间的差异。而且,对于大型表,这抵消了使用正式源代码控制系统的许多好处。

所以理想情况下,为了高效的数据库恢复,在 sql 文件中,我们希望每个表都由单个 INSERT 表示。对于有效的源代码控制过程,在 sql 文件中,我们希望该 INSERT 命令中的每条记录位于其自己的行上。

我对此的解决方案是以下备份脚本:

#!/bin/bash

cd my_git_directory/

ARGS="--host=myhostname --user=myusername --password=mypassword --opt --skip-dump-date"
/usr/bin/mysqldump $ARGS --database mydatabase | sed 's$VALUES ($VALUES\n($g' | sed 's$),($),\n($g' > mydatabase.sql

git fetch origin master
git merge origin/master
git add mydatabase.sql
git commit -m "Daily backup."
git push origin master

结果是一个 sql 文件 INSERT 命令格式,如下所示:

INSERT INTO `mytable` VALUES
(r1c1value, r1c2value, r1c3value),
(r2c1value, r2c2value, r2c3value),
(r3c1value, r3c2value, r3c3value);

一些注意事项:

  • 命令行上的密码...我知道,不安全,不同的讨论。
  • --opt:除其他外,打开 --extended-insert 选项(即,每个表一个 INSERT)。
  • --skip-dump-date:mysqldump 通常在创建时将日期/时间戳记在 sql 文件中。当版本之间的唯一差异是该日期/时间戳时,这在源代码控制中可能会变得很烦人。操作系统和源代码控制系统将为文件和版本添加日期/时间戳。它在 sql 文件中并不真正需要。
  • git 命令不是基本问题(格式化 sql 文件)的核心,但显示了我如何将我的 sql 文件恢复到源代码控制中,类似的事情可以用 svn 完成。当将此 sql 文件格式与您选择的源代码控制相结合时,您会发现当您的用户更新他们的工作副本时,他们只需在 Internet 上移动增量(即更改的记录),他们可以利用 diff 实用程序轻松查看数据库中的哪些记录已更改。
  • 如果您要转储驻留在远程服务器上的数据库,如果可能,请在该服务器上运行此脚本以避免每次转储时通过网络推送数据库的全部内容。
  • 如果可能,在您运行此脚本的同一台服务器上为您的 sql 文件建立一个工作源代码控制存储库;从那里将它们签入存储库。这也将有助于避免每次转储时都必须通过网络推送整个数据库。
于 2013-11-13T18:23:51.983 回答
12

正如其他人所说,使用 sed 替换 "),(" 是不安全的,因为这可能会显示为数据库中的内容。但是有一种方法可以做到这一点:如果您的数据库名称是 my_database,则运行以下命令:

$ mysqldump -u my_db_user -p -h 127.0.0.1 --skip-extended-insert my_database > my_database.sql
$ sed ':a;N;$!ba;s/)\;\nINSERT INTO `[A-Za-z0-9$_]*` VALUES /),\n/g' my_database.sql > my_database2.sql

您还可以使用“sed -i”来替换内联。

这是这段代码的作用:

  1. --skip-extended-insert 将为您拥有的每一行创建一个 INSERT INTO。
  2. 现在我们使用 sed 来清理数据。请注意,使用 sed 进行常规搜索/替换适用于单行,因此我们无法检测到“\n”字符,因为 sed 一次只工作一行。这就是为什么我们放 ":a;N;$!ba;" 这基本上告诉 sed 搜索多行并缓冲下一行。

希望这可以帮助

于 2014-11-07T19:50:27.227 回答
10

使用这样的选项将转储存储到带有 mysqldump 的 CSV 文件中--tab怎么样?

mysqldump --tab=/path/to/serverlocaldir --single-transaction <database> table_a

这会产生两个文件:

  • table_a.sql仅包含表创建语句;和
  • table_a.txt包含制表符分隔的数据。

恢复

您可以通过以下方式恢复您的表格LOAD DATA

LOAD DATA INFILE '/path/to/serverlocaldir/table_a.txt' 
  INTO TABLE table_a FIELDS TERMINATED BY '\t' ...

LOAD DATA 通常比使用 INSERT 语句快 20 倍。

如果您必须将数据恢复到另一个表中(例如,出于审查或测试目的),您可以创建一个“镜像”表:

CREATE TABLE table_for_test LIKE table_a;

然后将 CSV 加载到新表中:

LOAD DATA INFILE '/path/to/serverlocaldir/table_a.txt' 
  INTO TABLE table_for_test FIELDS TERMINATED BY '\t' ...

比较

ExcelCSV 文件最适合用于差异或查看内部,或者对于可以使用常用工具(如、Access或命令行(diffcomm等)的非 SQL 技术用户)

于 2013-04-02T05:18:04.030 回答
6

恐怕这不可能。在旧的 MySQL Administrator 中,我编写了用于转储 db 对象的代码,它完全独立于 mysqldump 工具,因此提供了许多附加选项(例如这种格式或进度反馈)。在 MySQL Workbench 中,决定使用 mysqldump 工具来代替,除了在某些方面倒退和产生版本问题外,它还具有始终与服务器保持同步的优势。

所以简短的回答是:目前无法使用 mysqldump 进行格式化。

于 2013-04-02T06:50:40.480 回答
1

试试这个:

mysqldump -c -t --add-drop-table=FALSE --skip-extended-insert -uroot -p<Password> databaseName tableName >c:\path\nameDumpFile.sql
于 2019-07-26T23:15:28.387 回答
0

我发现这个工具对处理扩展插入非常有帮助:http: //blog.lavoie.sl/2014/06/split-mysqldump-extended-inserts.html

它解析 mysqldump 输出并在每条记录后插入换行符,但仍使用更快的扩展插入。与 sed 脚本不同,如果正则表达式恰好在字符串中匹配,则不应该有任何在错误位置换行的风险。

于 2015-03-06T05:38:13.870 回答
0

我喜欢 Ace.Di 的 sed 解决方案,直到我收到此错误:sed: Couldn't re-allocate memory

因此我不得不写一个小的 PHP 脚本

mysqldump -u my_db_user -p -h 127.0.0.1 --skip-extended-insert my_database | php mysqlconcatinserts.php > db.sql

PHP 脚本还为每 10.000 行生成一个新的 INSERT,再次避免内存问题。

mysqlconcatinserts.php:

#!/usr/bin/php
<?php
/* assuming a mysqldump using --skip-extended-insert */
$last = '';
$count = 0;
$maxinserts = 10000;
while($l = fgets(STDIN)){
  if ( preg_match('/^(INSERT INTO .* VALUES) (.*);/',$l,$s) )
  {
    if ( $last != $s[1] || $count > $maxinserts )
    {
      if ( $count > $maxinserts ) // Limit the inserts
        echo ";\n";
      echo "$s[1] ";
      $comma = ''; 
      $last = $s[1];
      $count = 0;
    }
    echo "$comma$s[2]";
    $comma = ",\n";
  } elseif ( $last != '' ) {
    $last = '';
    echo ";\n";
  }
  $count++;
} 
于 2016-02-02T14:54:48.090 回答
-1

添加

set autocommit=0;

到您的 sql 脚本文件的第一行,然后通过以下方式导入:

mysql -u<user> -p<password> --default-character-set=utf8 db_name < <path>\xxx.sql

,它会快 10 倍。

于 2020-05-22T08:45:32.720 回答