我正在导出一个大型数据库,并想知道使其占用内存较少的最佳方法。
我意识到我必须在有时间限制且使用内存不足的循环中执行此操作,一次取出100行并将信息保存到文件然后重定向以从完成时开始新的循环开始上一个周期。
我想知道最好的方法是将数据缓冲到文件而不是耗尽内存,目前脚本将所有数据作为字符串获取,然后在完成从数据库获取所有行时保存到文件中。有时它会耗尽内存,因此需要修复。
我是否对从数据库中提取的数据使用fwrite()而不是放入var或使用临时文件?如果我在合并/重命名到备份文件时使用临时文件?
基本上,脚本将数据库数据导出到文件中的最佳方法是什么,而不会出现“致命错误:PHP允许的内存大小耗尽”错误?
function backup_tables($host, $user, $pass, $db, $tables = '*')
{
set_time_limit(0);
$mysqli = new mysqli($host,$user,$pass, $db);
if ($mysqli->connect_errno)
{
echo "Failed to connect to MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
$return = '';
$return .= "--\n";
$return .= "-- Database: `$db`\n";
$return .= "--\n\n";
$return .= "-- --------------------------------------------------------\n\n";
$numtypes = array(
'tinyint',
'smallint',
'mediumint',
'int',
'bigint',
'float',
'double',
'decimal',
'real'
);
// get all of the tables
if ($tables == '*')
{
$tables = array();
$result = $mysqli->query('SHOW TABLES');
while ($row = $result->fetch_row())
{
$tables[] = $row[0];
}
$result->close();
}
else
{
$tables = is_array($tables) ? $tables : explode(',',$tables);
}
for ($z = 0; $z == 0; $z++)
{
echo $z.'<br>';
// cycle through tables
foreach ($tables as $table)
{
//
$typesarr = array();
$result = $mysqli->query("SHOW COLUMNS FROM `".$table."`");
while ($row = $result->fetch_assoc())
{
$typesarr[] = $row;
}
$result->close();
#echo '<h2>'.$table.'</h2>';
#print("<pre>" . print_r($typesarr, true). "</pre>");
// table structure dump
$return .= "--\n";
$return .= "-- Table structure for table `$table`\n";
$return .= "--\n\n";
$return.= 'DROP TABLE IF EXISTS `'.$table.'`;'."\n\n";
$result = $mysqli->query("SHOW CREATE TABLE `".$table."`");
$row = $result->fetch_array();
$return.= $row[1].";\n\n";
$result->close();
// table data dump
$return .= "--\n";
$return .= "-- Dumping data for table `$table`\n";
$return .= "--\n\n";
$result = $mysqli->query("SELECT * FROM `".$table."`");
$num_fields = $result->field_count;
if ($result->num_rows > 0)
{
// put field names in array and into sql insert for dump
$fields_str = '';
$fields = array();
$finfo = $result->fetch_fields();
foreach ($finfo as $val)
{
$fields_str .= '`'.$val->name.'`, ';
$fields[] = $val->name;
}
$fields_str = '('.rtrim($fields_str, ', ').')';
$return.= 'INSERT INTO `'.$table.'` '.$fields_str.' VALUES'."\n";
// cycle through fields and check if int for later use
for ($i = 0; $i < $num_fields; $i++)
{
// strip brackets from type
$acttype = trim(preg_replace('/\s*\([^)]*\)/', '', $typesarr[$i]['Type']));
$acttype = explode(' ', $acttype);
// build array, is field int or not
if (is_numeric(array_search($acttype[0], $numtypes)))
{
$numflag[$i] = 1;
}
else
{
$numflag[$i] = 0;
}
}
}
$x = 0;
$num_rows = $result->num_rows;
// cycle through table rows
while($row = $result->fetch_row())
{
$x++;
// cycle through rows fields
for($j=0; $j<$num_fields; $j++)
{
if (isset($row[$j]) and $j === 0) { $return .= '('; }
// field data has value or not NULL
if (isset($row[$j]))
{
// field data dump (INT)
if ($numflag[$j]==1)
{
#echo '(INT) '. $fields[$j].' = '.$row[$j].'<br>';
$return.= $mysqli->real_escape_string($row[$j]);
}
else
{
// field data dump values (empty string, NULL and INT)
$return.= "'".$mysqli->real_escape_string($row[$j])."'";
#echo $fields[$j]." = '".$mysqli->real_escape_string($row[$j])."'<br>";
}
}
else
{
// field data dump (NULL)
if (is_null($row[$j]))
{
$row[$j] = 'NULL';
#echo '(NULL) '. $fields[$j].' = '.$row[$j].'<br>';
$return.= $row[$j];
}
else
{
// field data dump (empty string)
$return.= "''";
}
}
if ($j<($num_fields-1)) { $return.= ', '; }
}
if ($x<$num_rows) { $return.= "),\n"; } else { $return .= ");\n"; }
#echo '<br>';
}
#echo 'Rows: '.$rows.'<br>';
#echo 'Iterations: '.$x.'<br>';
$return.="\n-- --------------------------------------------------------\n\n";
}
}
$result->close();
//save file
$handle = fopen('/db-backup-'.time().'.sql','a');
fwrite($handle,$return);
fclose($handle);
}
欢迎举例
答案 0 :(得分:0)
默认情况下,mysqldump
的最新版本启用了--opt
,其中包括--quick
,后者表示一次一个地提取行,而不会在内存中缓冲整个表。因此mysqldump
的内存占用量已经非常低了。
使用mysqldump
时,您确定确实存在任何问题吗?如果是这样,他们如何表现出来? mysqldump
是内存不足,还是mysql服务器,还是其他什么东西?
答案 1 :(得分:0)
将MYSQLI_USE_RESULT
作为resultmode
参数添加到mysqli::query
调用,以便一次迭代一行结果,而不是将它们全部转移到PHP中。另请参阅mysqli::use-result
的文档。
将每一行直接写入输出文件,避免使用$result
变量。结合上述情况,这可能导致从服务器获取每一行并写入文件,因此PHP不必一次存储多行。