使用 mysqldump 格式化每行插入一个?
Posted
技术标签:
【中文标题】使用 mysqldump 格式化每行插入一个?【英文标题】:Using mysqldump to format one insert per line? 【发布时间】:2013-03-22 22:42:16 【问题描述】:这已被问过几次,但我找不到解决问题的方法。基本上,当使用 mysql Workbench 管理工具的内置工具 mysqldump 时,当我使用扩展插入转储数据库时,我会得到大量长行数据。我理解它为什么这样做,因为它通过将数据作为一个命令插入(尤其是在 InnoDB 上)来加快插入速度,但是格式化使得实际上很难查看转储文件中的数据,或者使用差异工具比较两个文件如果您将它们存储在版本控制等中。在我的情况下,我将它们存储在版本控制中,因为我们使用转储文件来跟踪我们的集成测试数据库。
现在我知道我可以关闭扩展插入,所以我会得到每行一个插入,这是可行的,但任何时候你使用转储文件进行恢复都会变慢。
我的核心问题是,在我转储文件时我们使用的 OLD 工具(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 工具中的此功能不再可用?
【问题讨论】:
这是mysqldump
中的一个已知缺点。是first reported in 2004。在 2011 年,Tim Riker 和 Lon Binder 都建议使用 1 行补丁来修复它。令人难以置信的是,mysqldump
开发人员/维护人员尚未实现此仍然。由于最初的错误报告已关闭(错误且无济于事),因此现在正在跟踪该问题here。
How to deal with enormous line lengths created by mysqldump的可能重复
【参考方案1】:
尝试使用以下选项: --skip-extended-insert
它对我有用。
【讨论】:
--skip-extended-insert
在问题中被排除,因为它会使恢复速度变慢。
它使恢复速度变慢,但它更容易且更安全。其他答案可能会失败,因为复杂且容易出错的替换方法可能会损坏数据,或者在执行 sed 或执行非常长的 mysql 查询时出现内存不足问题。
+1 我正在寻找答案“使用 mysqldump 格式化每行插入一个?”。这个答案对我来说已经足够好了。
可能很慢,但这取决于用途...--skip-extended-insert
的一个好处是它提供了一种非常快速的方法来将记录集限制为给定数量的记录。当您准备指数大小的测试数据集时,您可能需要它:1k、10k、100k、1M 记录等。如果这是您的用例,请巧妙地使用| head -<integer>
。【参考方案2】:
使用默认的 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 文件建立一个有效的源代码控制存储库;从那里将它们签入存储库。这也将有助于避免每次转储时都必须通过网络推送整个数据库。【讨论】:
好方法,但是...问题是备份的数据可能在行内包含“VALUES(”或“),(”。所以防止意外问题的正确方法是使用另一个工具(不是mysqldump ) 或修复mysqldump:***.com/a/20046484/751932 你是正确的@Speakus,如果表中存在这两个字符串中的任何一个,它将导致 sed 解析失败。关于需要修复的 mysqldump 也是正确的,在此之前找到另一个工具可能是个好主意。但是,具体问题是 mysqldump 可以做到这一点,简短的回答是肯定的,在 sed 的帮助下。 请注意,--extended-insert
并不限制自己为每个表只生成一个批量插入,如解决方案中的--opt
中所述。您可以参考 MySQL 文档以获取更多信息。 dev.mysql.com/doc/refman/5.7/en/…
别忘了--opt
是默认开启的;)
请注意,从今天(2020 年)开始,default 转储(mysqldump 和 mariadb-dump)执行扩展插入,这与当时的情况相反这个答案。【参考方案3】:
正如其他人所说,使用 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" 来替换 in-line。
以下是这段代码的作用:
-
--skip-extended-insert 将为您拥有的每一行创建一个 INSERT INTO。
现在我们使用 sed 来清理数据。请注意,使用 sed 进行常规搜索/替换适用于单行,因此我们无法检测到“\n”字符,因为 sed 一次只工作一行。这就是为什么我们放 ":a;N;$!ba;"这基本上告诉 sed 搜索多行并缓冲下一行。
希望对你有帮助
【讨论】:
在 Windows 上,如果git
使用安装程序和 unix 命令安装,sed
可以工作。
实际上我并没有做任何事情。输入和输出之间没有差异。
需要一些东西来让“grep”给出有用的结果。用 sed 's/(INSERT INTO [A-Za-z0-9$_]*
VALUES )/\1\n/g' test2.sql > test3.sql 补充上述内容,以避免来自表名等的误报。【参考方案4】:
如何使用 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' ...
比较
CSV 文件最容易用于差异或查看内部,或者对于可以使用Excel
、Access
或命令行(comm
等)等常用工具的非 SQL 技术用户。 .)
【讨论】:
这很有用,请记住,如果您从远程主机运行它,它会在数据库服务器上创建文件。 @AlexCiminian: 是的,只有系统管理员(或 dba - 或对 dbserver 具有正确权限的用户)才能获取和提供文件【参考方案5】:恐怕这是不可能的。在旧的 MySQL Administrator 中,我编写了用于转储 db 对象的代码,它完全独立于 mysqldump 工具,因此提供了许多附加选项(例如这种格式或进度反馈)。在 MySQL Workbench 中,决定改用 mysqldump 工具,除了在某些方面倒退和产生版本问题外,它还具有始终与服务器保持同步的优势。
所以简短的回答是:目前无法使用 mysqldump 进行格式化。
【讨论】:
这太糟糕了。当然这是一个可以添加到 mysqldump 本身的有用功能? 完全同意!同时我们必须重新发明***并编写一个重新格式化的程序^^ ...【参考方案6】:试试这个:
mysqldump -c -t --add-drop-table=FALSE --skip-extended-insert -uroot -p<Password> databaseName tableName >c:\path\nameDumpFile.sql
【讨论】:
【参考方案7】:我发现这个工具对于处理扩展插入非常有帮助:http://blog.lavoie.sl/2014/06/split-mysqldump-extended-inserts.html
它解析 mysqldump 输出并在每条记录后插入换行符,但仍使用更快的扩展插入。与 sed 脚本不同,如果正则表达式恰好在字符串中匹配,则不存在在错误位置换行的风险。
【讨论】:
【参考方案8】:我喜欢 Ace.Di 的 sed 解决方案,直到出现以下错误: sed: 无法重新分配内存
因此我不得不编写一个小的 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++;
【讨论】:
【参考方案9】:添加
set autocommit=0;
到您的 sql 脚本文件的第一行,然后通过以下方式导入:
mysql -u<user> -p<password> --default-character-set=utf8 db_name < <path>\xxx.sql
,它会快 10 倍。
【讨论】:
以上是关于使用 mysqldump 格式化每行插入一个?的主要内容,如果未能解决你的问题,请参考以下文章