我正在使用我在网上找到的一个修改过的php-mysql备份脚本来备份我的sql数据库,但我一直遇到mysql_fetch_row的问题,我想知道是否有办法解决它。
我用记忆错误注释了这行。
<?php
ini_set('memory_limit','4000M');
$ho = "host";
$us = "username";
$pa = "password";
$da='dbname';
backup_tables($ho,$us,$pa,$da);
/* backup the db OR just a table */
function backup_tables($host,$user,$pass,$name,$tables = '*')
{
$link = mysql_connect($host,$user,$pass);
mysql_select_db($name,$link);
//get all of the tables
if($tables == '*')
{
$tables = array();
$result = mysql_query('SHOW TABLES');
while($row = mysql_fetch_row($result))
{
$tables[] = $row[0];
}
}
else
{
$tables = is_array($tables) ? $tables : explode(',',$tables);
}
$filename = 'db-backup-'.time().'-'.(md5(implode(',',$tables))).'.sql';
$handle = fopen($filename,'w');
//cycle through
foreach($tables as $table)
{
$result = mysql_query('SELECT * FROM '.$table);
$num_fields = mysql_num_fields($result);
fwrite( $handle, 'DROP TABLE '.$table.';' );
$row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
fwrite( $handle, "'n'n".$row2[1].";'n'n" );
for ($i = 0; $i < $num_fields; $i++)
{
//this is the line with the error******
while($row = mysql_fetch_row($result))
{
fwrite( $handle, 'INSERT INTO '.$table.' VALUES(' );
for($j=0; $j<$num_fields; $j++)
{
$row[$j] = addslashes($row[$j]);
$row[$j] = ereg_replace("'n","''n",$row[$j]);
if (isset($row[$j])) { fwrite( $handle, '"'.$row[$j].'"' ) ; }
else { fwrite( $handle, '""' ); }
if ($j<($num_fields-1)) { fwrite( $handle, ',' ); }
}
fwrite( $handle, ");'n" );
}
}
fwrite( $handle, "'n'n'n" );
}
//save file
fclose($handle);
}
?>
这就是错误:
Fatal error: Out of memory (allocated 1338507264) (tried to allocate 35 bytes) in /home/user/backupSQL/backupmodified.php on line 47
我知道有更好的方法来做备份,但这满足了我对系统的所有要求,而且我只遇到了巨大数据库的内存问题。
谢谢你的帮助。
-PHP/MMySQL新手
ps。这是我使用的链接http://www.htmlcenter.com/blog/back-up-your-mysql-database-with-php/
edit:mysqldump在备份这些大型数据库时运行良好,但大型数据库是修改最多的数据库,当有人需要处理它们时,我不能在转储时锁定我的数据库。这就是我使用这个剧本的原因。
正如其他海报所提到的,您最好使用现成的工具,但现在让我们谈谈您的脚本。
如果你有一个大得离谱的表,那么就没有办法分配那么多内存。您必须将数据集拆分为多个块。使用类似的查询
$result = mysql_query('SELECT * FROM '.$table.' LIMIT '.$start.', 10000');
然后只需使用$start
进行迭代或执行while
循环来检查是否仍能得到结果。像这样,你会得到10000条记录的数据块,这样它肯定会适合你的记忆。
不过,仍然存在一个问题——一致性。如果在获取数据块时某些行发生了更改,该怎么办?好吧,您可以将SELECT
循环封装在事务中,并在获取数据时锁定表。我知道你试图避免锁定,但锁定一个表比锁定整个数据库要好。这仍然不能确保表间的一致性,但这是在您设置的条件下可以做到的最好的。
编辑:这是循环的代码。我实现了while
解决方案。有一个等效的for
循环,但两者之间的比较不在本文的范围内。
foreach($tables as $table)
{
fwrite( $handle, 'DROP TABLE '.$table.';' );
$row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table));
fwrite( $handle, "'n'n".$row2[1].";'n'n" );
$start = 0;
do {
$result = mysql_query( 'SELECT * FROM '.$table.' LIMIT '.$start.', 10000' );
$start += 10000;
$num_rows = mysql_num_rows( $result );
while( $row = mysql_fetch_row( $result ) ) {
$line = 'INSERT INTO '.$table.' VALUES(';
foreach( $row as $value ) {
$value = addslashes( $value );
$value = ereg_replace( "'n","''n", $value );
$line .= '"'.$value.'",';
}
$line = substr( $line, 0, -1 ); // cut the final ','
$line .= ');'n';
fwrite( $handle, $line );
}
} while( $num_rows !== 0 );
}
mysql扩展具有专门为大型数据库查询设计的mysql_unbuffered_query
功能。
mysqli中的等价项利用了mysqli_query
的第二个参数,例如:
$res = $mysqli->query('SELECT * FROM large_database', MYSQLI_USE_RESULT);
while ($row = $res->fetch_assoc()) {
...
}
不过,这种方法也有一些缺点,例如,您无法知道$res
中的行数,并且在释放$res
之前无法执行其他查询。
转储以某种方式分配与表大小成比例的内存量是一个非常典型的问题。您应该在脚本中实现类似于mysqldump中的"--opt"选项,并在此处查看如何转储大型数据库https://dba.stackexchange.com/questions/20/how-can-i-optimize-a-mysqldump-of-a-large-database
为什么不使用mysqldump将输出记录到某个文件中呢?