如何将大型 (14 GB) MySQL 转储文件导入新的 MySQL 数据库?
11 回答
我四处搜索,只有这个解决方案对我有帮助:
mysql -u root -p
set global net_buffer_length=1000000; --Set network buffer length to a large byte number
set global max_allowed_packet=1000000000; --Set maximum allowed packet size to a large byte number
SET foreign_key_checks = 0; --Disable foreign key checking to avoid delays,errors and unwanted behaviour
source file.sql --Import your sql dump file
SET foreign_key_checks = 1; --Remember to enable foreign key checks when procedure is complete!
答案在这里。
您是否尝试过直接使用mysql
命令行客户端?
mysql -u username -p -h hostname databasename < dump.sql
如果你不能这样做,你可以通过谷歌搜索找到任何数量的实用程序,它们可以帮助你将大型转储导入 MySQL,比如BigDump
在最近的一个项目中,我们面临着处理和操纵大量数据的挑战。我们的客户向我们提供了 50 个 CSV 文件,大小从 30 MB 到 350 MB 不等,总共包含大约 2000 万行数据和 15 列数据。我们的最终目标是将数据导入和操作到 MySQL 关系数据库中,以用于驱动我们也开发的前端 PHP 脚本。现在,处理这么大或更大的数据集并不是最简单的任务,在处理它时,我们想花点时间分享一些在处理这样的大型数据集时应该考虑和知道的事情。
- 分析您的数据集预导入
这第一步我怎么强调都不过分!确保在导入之前花时间分析正在使用的数据。了解所有数据代表什么,哪些列与您需要进行的操作和操作类型相关,从长远来看,最终将为您节省时间。
- LOAD DATA INFILE 是你的朋友
如果您继续尝试通过 PHPMyAdmin 之类的工具进行常规 CSV 插入,那么导入像我们使用过的(以及更大的)这样的大型数据文件可能会很困难。它不仅在许多情况下会失败,因为由于上传大小限制和服务器超时,您的服务器将无法处理与某些数据文件一样大的文件上传,而且即使成功,该过程也可能需要数小时取决于我们的硬件。创建 SQL 函数 LOAD DATA INFILE 是为了处理这些大型数据集,并将显着减少处理导入过程所需的时间。值得注意的是,这可以通过 PHPMyAdmin 执行,但您可能仍然遇到文件上传问题。
LOAD DATA INFILE '/mylargefile.csv' INTO TABLE temp_data FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n'
- MISAM 与 InnoDB
无论是大型数据库还是小型数据库,花一点时间来考虑您将为您的项目使用哪个数据库引擎总是好的。您将要阅读的两个主要引擎是 MISAM 和 InnoDB,它们各有优缺点。简而言之,要考虑的事情(一般而言)如下:
米萨姆
- 降低内存使用率
- 允许全文搜索
- 表级锁定 - 在写入时锁定整个表
- 非常适合阅读密集型应用程序
InnoDB
- 项目清单
- 使用更多内存
- 不支持全文搜索
- 更快的性能
- 行级别锁定——在写入时锁定单行
- 非常适合读/写密集型应用程序
- 仔细规划您的设计
MySQL 分析您的数据库设计/结构将成为影响其性能的重要因素。花点时间规划不同的字段并分析数据以找出最佳的字段类型、默认值和字段长度。您希望容纳适量的数据,并在数据不保证时尽量避免使用 varchar 列和过大的数据类型。作为完成数据库后的附加步骤,您希望查看 MySQL 建议的所有不同字段的字段类型。您可以通过执行以下 SQL 命令来执行此操作:
ANALYZE TABLE my_big_table
结果将是对每列信息的描述以及对它应该是什么类型的数据类型以及适当长度的建议。现在您不一定需要遵循这些建议,因为它们仅基于现有数据,但它可能有助于您走上正确的轨道并让您思考
- 索引或不索引
对于如此大的数据集,根据您需要对前端数据执行的操作为您的数据创建适当的索引非常重要,但是如果您计划事先操作数据,请避免在其上放置太多索引数据。它不仅会使您的 SQL 表变大,而且还会减慢某些操作,例如列加法、减法和附加索引。对于我们的数据集,我们需要获取刚刚导入的信息并将其分解为几个不同的表以创建关系结构,并获取某些列并将信息拆分为其他列。我们在最少的列上放置了一个索引,我们知道这些列可以帮助我们进行操作。总而言之,我们取了 1 个包含 2000 万行数据的大表,并将其信息拆分为 6 个不同的表,其中包含主要数据片段以及基于现有内容新创建的数据。我们通过编写小的 PHP 脚本来解析和移动数据来完成所有这些工作。
- 寻找平衡
从编程的角度来看,使用大型数据库的很大一部分是速度和效率。将所有数据放入数据库中很棒,但是如果您编写的用于访问数据的脚本很慢,那还有什么意义呢?在处理大型数据集时,花时间了解脚本正在执行的所有查询并创建索引以尽可能帮助这些查询非常重要。一种分析查询的方法是执行以下 SQL 命令:
EXPLAIN SELECT some_field FROM my_big_table WHERE another_field='MyCustomField';
通过将 EXPLAIN 添加到查询的开头,MySQL 将吐出描述它尝试使用、确实使用了哪些索引以及如何使用它们的信息。我将这一点标记为“寻找平衡”,因为尽管索引可以帮助您的脚本执行得更快,但它们也可以轻松地使其运行得更慢。您需要确保索引需要的内容并且仅索引需要的内容。每个索引都会消耗磁盘空间并增加表的开销。每次对表进行编辑时,都必须为该特定行重建索引,并且这些行上的索引越多,所需的时间就越长。这一切都归结为制作智能索引、高效的 SQL 查询以及最重要的基准测试,以了解每个查询在做什么以及完成它需要多长时间。
- 索引打开,索引关闭
当我们处理数据库和前端脚本时,客户和我们都开始注意到需要更改的小事情,并且需要我们对数据库进行更改。其中一些更改涉及添加/删除列和更改列类型。由于我们已经在数据上设置了许多索引,因此进行任何这些更改都需要服务器做一些认真的工作来保持索引到位并处理任何修改。在我们的小型 VPS 服务器上,一些更改需要 6 个小时以上才能完成……当然对我们能够进行快速开发没有帮助。解决方案?关闭索引!有时最好关闭索引,进行更改,然后重新打开索引……尤其是如果您要进行许多不同的更改。关闭索引后,更改需要几秒钟到几分钟而不是几小时。当我们对我们的更改感到满意时,我们只需重新打开索引。这当然需要相当长的时间来重新索引所有内容,但它至少能够一次重新索引所有内容,从而减少一项一项进行这些更改所需的总时间。这是如何做到的:
- 禁用索引:
ALTER TABLE my_big_table DISABLE KEY
- 启用索引:
ALTER TABLE my_big_table ENABLE KEY
- 调整 MySQL
在使您的数据库和脚本快速运行时,不要忽视您的服务器。您的硬件需要与数据库和脚本一样多的关注和调整。尤其重要的是查看您的 MySQL 配置文件,以了解可以进行哪些更改以更好地提高其性能。
- 不要害怕问
开始使用 SQL 可能具有挑战性,而使用非常大的数据集只会让它变得更加困难。当涉及到大型数据集时,不要害怕去找知道自己在做什么的专业人士。最终,您将获得卓越的产品、更快的开发和更快的前端性能。当涉及到大型数据库时,有时需要专业人员经验丰富的眼睛才能发现所有可能降低数据库性能的小警告。
我在我看到的一些回复中发布了我的发现,这些回复没有提到我遇到了什么,显然这甚至会打败 BigDump,所以检查一下:
我试图通过 Linux 命令行加载 500 兆转储,并不断收到“Mysql 服务器已消失”错误。my.conf 中的设置没有帮助。原来解决它的是......我正在做一个大的扩展插入,如:
insert into table (fields) values (a record, a record, a record, 500 meg of data);
我需要将文件格式化为单独的插入,如下所示:
insert into table (fields) values (a record);
insert into table (fields) values (a record);
insert into table (fields) values (a record);
Etc.
为了生成转储,我使用了这样的东西,它就像一个魅力:
SELECT
id,
status,
email
FROM contacts
INTO OUTFILE '/tmp/contacts.sql'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES STARTING BY "INSERT INTO contacts (id,status,email) values ("
TERMINATED BY ');\n'
我制作了一个 PHP 脚本,旨在导入由 phpmyadmin 或 mysql dump (from cpanel) 生成的大型数据库转储。它叫做 PETMI,你可以在这里[project page] [gitlab page]下载它。
它通过拆分一个。sql 文件分成称为拆分的较小文件,并一次处理每个拆分。无法处理的拆分可以由用户在 phpmyadmin 中手动处理。这可以像在 sql 转储中一样轻松编程,每个命令都在一个新行上。sql 转储中的某些内容在 phpmyadmin 导入中有效,但在 mysqli_query 中无效,因此这些行已从拆分中删除。
它已经用 1GB 的数据库进行了测试。它必须上传到现有网站。PETMI 是开源的,示例代码可以在 Gitlab 上看到。
一位版主让我提供一些示例代码。我在打电话,所以请原谅格式。
这是创建拆分的代码。
//gets the config page
if (isset($_POST['register']) && $_POST['register'])
{
echo " <img src=\"loading.gif\">";
$folder = "split/";
include ("config.php");
$fh = fopen("importme.sql", 'a') or die("can't open file");
$stringData = "-- --------------------------------------------------------";
fwrite($fh, $stringData);
fclose($fh);
$file2 = fopen("importme.sql","r");
//echo "<br><textarea class=\"mediumtext\" style=\"width: 500px; height: 200px;\">";
$danumber = "1";
while(! feof($file2)){
//echo fgets($file2)."<!-- <br /><hr color=\"red\" size=\"15\"> -->";
$oneline = fgets($file2); //this is fgets($file2) but formatted nicely
//echo "<br>$oneline";
$findme1 = '-- --------------------------------------------------------';
$pos1 = strpos($oneline, $findme1);
$findme2 = '-- Table structure for';
$pos2 = strpos($oneline, $findme2);
$findme3 = '-- Dumping data for';
$pos3 = strpos($oneline, $findme3);
$findme4 = '-- Indexes for dumped tables';
$pos4 = strpos($oneline, $findme4);
$findme5 = '-- AUTO_INCREMENT for dumped tables';
$pos5 = strpos($oneline, $findme5);
if ($pos1 === false && $pos2 === false && $pos3 === false && $pos4 === false && $pos5 === false) {
// setcookie("filenumber",$i);
// if ($danumber2 == ""){$danumber2 = "0";} else { $danumber2 = $danumber2 +1;}
$ourFileName = "split/sql-split-$danumber.sql";
// echo "writing danumber is $danumber";
$ourFileHandle = fopen($ourFileName, 'a') or die("can't edit file. chmod directory to 777");
$stringData = $oneline;
$stringData = preg_replace("/\/[*][!\d\sA-Za-z@_='+:,]*[*][\/][;]/", "", $stringData);
$stringData = preg_replace("/\/[*][!]*[\d A-Za-z`]*[*]\/[;]/", "", $stringData);
$stringData = preg_replace("/DROP TABLE IF EXISTS `[a-zA-Z]*`;/", "", $stringData);
$stringData = preg_replace("/LOCK TABLES `[a-zA-Z` ;]*/", "", $stringData);
$stringData = preg_replace("/UNLOCK TABLES;/", "", $stringData);
fwrite($ourFileHandle, $stringData);
fclose($ourFileHandle);
} else {
//write new file;
if ($danumber == ""){$danumber = "1";} else { $danumber = $danumber +1;}
$ourFileName = "split/sql-split-$danumber.sql";
//echo "$ourFileName has been written with the contents above.\n";
$ourFileName = "split/sql-split-$danumber.sql";
$ourFileHandle = fopen($ourFileName, 'a') or die("can't edit file. chmod directory to 777");
$stringData = "$oneline";
fwrite($ourFileHandle, $stringData);
fclose($ourFileHandle);
}
}
//echo "</textarea>";
fclose($file2);
这是导入拆分的代码
<?php
ob_start();
// allows you to use cookies
include ("config.php");
//gets the config page
if (isset($_POST['register']))
{
echo "<div id**strong text**=\"sel1\"><img src=\"loading.gif\"></div>";
// the above line checks to see if the html form has been submitted
$dbname = $accesshost;
$dbhost = $username;
$dbuser = $password;
$dbpasswd = $database;
$table_prefix = $dbprefix;
//the above lines set variables with the user submitted information
//none were left blank! We continue...
//echo "$importme";
echo "<hr>";
$importme = "$_GET[file]";
$importme = file_get_contents($importme);
//echo "<b>$importme</b><br><br>";
$sql = $importme;
$findme1 = '-- Indexes for dumped tables';
$pos1 = strpos($importme, $findme1);
$findme2 = '-- AUTO_INCREMENT for dumped tables';
$pos2 = strpos($importme, $findme2);
$dbhost = '';
@set_time_limit(0);
if($pos1 !== false){
$splitted = explode("-- Indexes for table", $importme);
// print_r($splitted);
for($i=0;$i<count($splitted);$i++){
$sql = $splitted[$i];
$sql = preg_replace("/[`][a-z`\s]*[-]{2}/", "", $sql);
// echo "<b>$sql</b><hr>";
if($table_prefix !== 'phpbb_') $sql = preg_replace('/phpbb_/', $table_prefix, $sql);
$res = mysql_query($sql);
}
if(!$res) { echo '<b>error in query </b>', mysql_error(), '<br /><br>Try importing the split .sql file in phpmyadmin under the SQL tab.'; /* $i = $i +1; */ } else {
echo ("<meta http-equiv=\"Refresh\" content=\"0; URL=restore.php?page=done&file=$filename\"/>Thank You! You will be redirected");
}
} elseif($pos2 !== false){
$splitted = explode("-- AUTO_INCREMENT for table", $importme);
// print_r($splitted);
for($i=0;$i<count($splitted);$i++){
$sql = $splitted[$i];
$sql = preg_replace("/[`][a-z`\s]*[-]{2}/", "", $sql);
// echo "<b>$sql</b><hr>";
if($table_prefix !== 'phpbb_') $sql = preg_replace('/phpbb_/', $table_prefix, $sql);
$res = mysql_query($sql);
}
if(!$res) { echo '<b>error in query </b>', mysql_error(), '<br /><br>Try importing the split .sql file in phpmyadmin under the SQL tab.'; /* $i = $i +1; */ } else {
echo ("<meta http-equiv=\"Refresh\" content=\"0; URL=restore.php?page=done&file=$filename\"/>Thank You! You will be redirected");
}
} else {
if($table_prefix !== 'phpbb_') $sql = preg_replace('/phpbb_/', $table_prefix, $sql);
$res = mysql_query($sql);
if(!$res) { echo '<b>error in query </b>', mysql_error(), '<br /><br>Try importing the split .sql file in phpmyadmin under the SQL tab.'; /* $i = $i +1; */ } else {
echo ("<meta http-equiv=\"Refresh\" content=\"0; URL=restore.php?page=done&file=$filename\"/>Thank You! You will be redirected");
}
}
//echo 'done (', count($sql), ' queries).';
}
我在下面发现 SSH 命令对于导出/导入巨大的 MySql 数据库非常强大,至少我已经使用了多年。永远不要依赖通过 cPanel WHM、CWP、OVIPanel 等控制面板生成的备份,它们可能会给您带来麻烦,尤其是当您在控制面板之间切换时,请始终信任 SSH。
[出口]
$ mysqldump -u root -p example_database| gzip > example_database.sql.gz
[进口]
$ gunzip < example_database.sql.gz | mysql -u root -p example_database
简单的解决方案是运行此查询:
mysql -h yourhostname -u username -p databasename < yoursqlfile.sql
如果你想用进度条导入,试试这个:
pv yoursqlfile.sql | mysql -uxxx -pxxxx databasename
我长期以来一直试图为这个问题找到一个好的解决方案。最后我想我有一个解决方案。据我了解max_allowed_packet
没有上限。所以去设置 my.cnf 说max_allowed_packet=300M
现在这样做mysql> source sql.file
不会做任何事情,因为转储文件,insert
语句被分成 1mb 大小。所以我的 45gb 文件插入计数是 ~:45bg/1mb。
为了解决这个问题,我用 php 解析 sql 文件并将插入语句设置为我想要的大小。就我而言,我已将数据包大小设置为 100mb。所以我减少了插入字符串。在另一台机器上,我的数据包大小为 300M,插入 200M,它可以工作。
由于所有表的总大小约为 1.2tb,因此我按数据库逐表导出。所以我每张表有一个 sql 文件。如果您的不同,则必须相应地调整代码。
<?php
global $destFile, $tableName;
function writeOutFile(&$arr)
{
echo " [count: " . count($arr) .']';
if(empty($arr))return;
global $destFile, $tableName;
$data='';
//~ print_r($arr);
foreach($arr as $i=>$v)
{
$v = str_replace(";\n", '', $v);
//~ $v = str_replace("),(", "),\n(", $v);
$line = ($i==0? $v: str_replace("INSERT INTO `$tableName` VALUES",',', $v));
$data .= $line;
}
$data .= ";\n";
file_put_contents($destFile, $data, FILE_APPEND);
}
$file = '/path/to/sql.file';
$tableName = 'tablename';
$destFile = 'localfile.name';
file_put_contents($destFile, null);
$loop=0;
$arr=[];
$fp = fopen($file, 'r');
while(!feof($fp))
{
$line = fgets($fp);
if(strpos($line, "INSERT INTO `")!==false)$arr[]=$line;
else
{writeOutFile($arr); file_put_contents($destFile, $line, FILE_APPEND);$arr=[];continue;}
$loop++;
if(count($arr)==95){writeOutFile($arr);$arr=[];}
echo "\nLine: $loop, ". count($arr);
}
?>
这对您的工作方式取决于您的硬件。但所有事情都保持不变,这个过程以指数方式加速了我的导入。我没有任何基准可以分享,这是我的工作经验。
使用source命令导入大型数据库
mysql -u username -p
> source sqldbfile.sql
这可以导入任何大型数据库
根据mysql文档,这些都不起作用!人们注意了!所以我们将 test.sql 上传到 test_db 中,在 shell 中输入:
mysql --user=user_name --password=yourpassword test_db < d:/test.sql
导航到 C:\wamp64\alias\phpmyadmin.conf 并从以下位置更改:
php_admin_value upload_max_filesize 128M
php_admin_value post_max_size 128M
至
php_admin_value upload_max_filesize 2048M
php_admin_value post_max_size 2048M
或者更多 :)