我想要backup my database with PHP。
我测试了链接的脚本,但它永远不会结束,我试图在查询之前添加repair $table
,但它没有帮助。
所以我想出如果我只是跳过两个表(你可以在代码中看到)然后它工作正常:
<?
error_reporting(E_ALL);
ini_set('error_reporting',1);
require('../basedatos.php');
echo 'included<br>';
/* backup the db OR just a table */
function backup_tables($host,$user,$pass,$name,$tables = '*')
{
echo '1<br>';
//get all of the tables
if($tables == '*')
{
$tables = array();
$result = mysql_query('SHOW TABLES') or die(msyql_error());
while($row = mysql_fetch_row($result))
{
$tables[] = $row[0];
}
}
else
{
$tables = is_array($tables) ? $tables : explode(',',$tables);
}
echo '2<br>';
//cycle through
foreach($tables as $table)
{
if($table == 'etiquetas' || $table == 'links') continue;
$repair = mysql_query("REPAIR table $table") or die(mysql_error());
echo '3- '.$table.'<br>';
$result = mysql_query('SELECT * FROM '.$table) or die(msyql_error());
$num_fields = mysql_num_fields($result);
$return.= 'DROP TABLE '.$table.';';
$row2 = mysql_fetch_row(mysql_query('SHOW CREATE TABLE '.$table)) or die(msyql_error());
$return.= "\n\n".$row2[1].";\n\n";
for ($i = 0; $i < $num_fields; $i++)
{
while($row = mysql_fetch_row($result))
{
$return.= '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])) { $return.= '"'.$row[$j].'"' ; } else { $return.= '""'; }
if ($j<($num_fields-1)) { $return.= ','; }
}
$return.= ");\n";
}
}
$return.="\n\n\n";
}
echo '4<br>';
//save file
$handle = fopen('db-backup-'.time().'-'.(md5(implode(',',$tables))).'.sql','w+');
fwrite($handle,$return);
fclose($handle);
}
backup_tables('localhost','username','password','*');
?>
有没有办法找到给我一个问题的行,以便我可以编辑/删除它们?
-PS-
此外,如果我不跳过它们,我不会得到任何错误(脚本只是永远不会结束,这就是为什么我添加了一些丑陋的日志..,任何想法为什么?
-编辑-
此外,如果我尝试通过,例如,sqlBuddy导出数据库,我也会收到错误:
正如许多人所说,这个脚本(以及简单的“通过PHP转储MySQL”)远非最佳,但仍然比没有备份更好。
由于您只能使用PHP访问数据库,因此您应该使用它来查找出错的地方。
以下是对脚本的修改,它只会将一个表转储到文件中。它是一个调试脚本,而不是用于生产的导出工具(但是,用它做你想做的事),这就是它在保存表的每一行后输出调试的原因。
正如Amit Kriplani所建议的那样,每次迭代都会将数据附加到目标文件中,但我不认为PHP内存是你的问题,如果你的内存不足你应该得到一个PHP错误,或者至少应该抛出一个HTTP 500由服务器而不是永远运行脚本。
function progress_export( $file, $table, $idField, $offset = 0, $limit = 0 )
{
debug("Starting export of table $table to file $file");
// empty the output file
file_put_contents( $file, '' );
$return = '';
debug("Dumping schema");
$return.= 'DROP TABLE '.$table.';';
$row2 = mysql_fetch_row(mysql_query("SHOW CREATE TABLE $table"));
$return.= "\n\n".$row2[1].";\n\n";
file_put_contents( $file, $return, FILE_APPEND );
debug("Schema saved to $file");
$return = '';
debug( "Querying database for records" );
$query = "SELECT * FROM $table ORDER BY $idField";
// make offset/limit optional if we need them for further debug
if ( $offset && $limit )
{
$query .= " LIMIT $offset, $limit";
}
$result = mysql_query($query);
$i = 0;
while( $data = mysql_fetch_assoc( $result ) )
{
// Let's be verbose but at least, we will see when something goes wrong
debug( "Exporting row #".$data[$idField].", rows offset is $i...");
$return = "INSERT INTO $table (`". implode('`, `', array_keys( $data ) )."`) VALUES (";
$coma = '';
foreach( $data as $column )
{
$return .= $coma. "'". mysql_real_escape_string( $column )."'";
$coma = ', ';
}
$return .=");\n";
file_put_contents( $file, $return, FILE_APPEND );
debug( "Row #".$data[$idField]." saved");
$i++;
// Be sure to send data to browser
@ob_flush();
}
debug( "Completed export of $table to file $file" );
}
function debug( $message )
{
echo '['.date( "H:i:s" )."] $message <br/>";
}
// Update those settings :
$host = 'localhost';
$user = 'user';
$pass = 'pwd';
$base = 'database';
// Primary key to be sure how record are sorted
$idField = "id";
$table = "etiquetas";
// add some writable directory
$file = "$table.sql";
$link = mysql_connect($host,$user,$pass);
mysql_select_db($base,$link);
// Launching the script
progress_export( $file, $table, $idField );
编辑脚本末尾的设置,并针对两个表中的一个运行它。
您应该在脚本仍处理时看到输出,并获取有关正在处理的行的一些引用,如下所示:
[23:30:13]开始将表ezcontentobject导出到文件ezcontentobject.sql
[23:30:13]倾倒架构
[23:30:13]架构保存到ezcontentobject.sql
[23:30:13]查询记录数据库
[23:30:13]导出第4行,行偏移为0 ...
[23:30:13]第4行已保存
[23:30:13]导出第10行,行偏移量为1 ...
[23:30:13]第10行已保存
[23:30:13]导出第11行,行偏移量为2 ...
[23:30:13]第11行保存
[23:30:13]导出第12行,行偏移量为3 ...
[23:30:13]第12行保存
[23:30:13]导出第13行,行偏移为4 ...
[23:30:13]第13行已保存
等等
那么你将有一个表的备份(小心,我没有测试生成的SQL)!
我猜它不会完成:
然后问题是在查询时。
然后,您应该尝试使用偏移量和限制参数来限制查询,继续进行二分法以找出它挂起的位置
生成限制为1000个第一结果的查询的示例。
// Launching the script
progress_export( $file, $table, $idField, 0, 1000 );
在确定显示的最后一行ID之前,您应该尝试:
例如50作为偏移量,一些大数字作为限制:
// Launching the script
progress_export( $file, $table, $idField, 50, 600000 );
这是为了检查它自己的行是否导致问题,或者它是否是临界行数/数据量...
如果您无法在分配的资源上播放,那么解决方案将是将导出拆分为块。您可以使用接近此脚本的脚本完成此操作,输出一些HTML / javascript,重定向到它自己,使用offset和limit作为参数,而导出未完成(如果我们最终需要,我将编辑答案) )
一些线索:
我对VPS没有任何经验,但是你对CPU使用有什么限制吗?
如果您一次使用过多的资源,那么您的流程是否会排队?
那些没有问题的转储表怎么样?是否存在与两者一样大的表格导致问题?
我不知道为什么这个“阻止”...但脚本只适用于非常基本的数据库。
例如,它如何处理外键约束?这只是一个建议,可能你是故意丢弃它,但为什么不使用mysql_dump
?
从你的shell:
mysql_dump -h host -u user -p my_database > db-backup.sql
编辑:根据Riggs Folly的建议,phpMyAdmin有备份设施,通常可以在托管上使用。
实际上,即使您的主机上没有,您仍然可以将其安装在您的http服务器上并将其配置为访问远程数据库服务器:
http://www.mittalpatel.co.in/access_mysql_database_hosted_remote_server_using_phpmyadmin
不要将输出保存到变量,而是使用带有FILE_APPEND标志的file_put_contents将查询写入文件。如果您认为需要花费大量时间,可以使用查看器检查文件,或者在webroot目录中创建文件并在浏览器中打开它以查看发生了什么......
如果您使用某些自定义脚本和诸如“sqlbuddy”之类的工具存在相同的备份问题 - 结论是问题在于您的表和/或DB更普遍。
我会尝试复制有问题的表并备份那些而不是原始表,以查看附加内容:
CREATE TABLE etiquetas_copy AS SELECT * FROM etiquetas
如果你不能备份副本,我的猜测是行数肯定是问题。一些提供程序任意(有时默默地)杀死使用过多资源的脚本或数据库请求。
您可以尝试按照评论中的建议一次备份1000行。像这样的东西:
$result = mysql_query('SELECT * FROM '.$table." LIMIT $n,$n+1000") or die(msyql_error());
你必须包装它,并且几行是一个“转”的循环,而获取的行数是1000,以便在处理这1000行后读取下一批。
您是否尝试仅在有问题的表上运行脚本?这些表是否具有二进制字段(如BLOB?)。
也许你可以在PHP中处理之前尝试转义字段的输出:select HEX(field1),HEX(field2),HEX(field3)FROM links
然后像这样编写INSERT语句:INSERT INTO links(field1,field2,field3)VALUES(UNHEX(),UNHEX(),UNHEX());
此外,您应该使用preg_replace而不是ereg_replace。它快100倍。因此,如果您在大型数据上执行此操作,则可能会降低脚本速度。
最后,您应该真正研究错误日志记录配置,因为应该发生错误。它是一个php错误,内存限制错误,最大执行时间错误等。
很喜欢你的项目。
你选择的剧本是浪费时间。肯定有更好的,经典和维护一个完全适合的工作是Mysqldumper。无论如何,我不想只在这里给出工具建议,但也读你的问题,你想知道为什么会发生这种情况。
如果你真的想知道,这是麻烦部门的一个提示:你很可能在这里达到内存限制。但是您没有看到,因为这可能是您服务器的硬限制。在这种情况下,脚本刚刚被杀死,而PHP没有提供任何更多错误消息。
而不是仅向STDOUT / STDERR报告错误,您可以登录到文件。我真的建议你为了解决问题。这包括拖动操作系统本身的日志,如/var/log/messages
,但也配置PHP写入日志文件(对于硬限制,你不会看到任何东西,因为PHP刚刚被杀死,但我仍然建议你让自己舒服,并了解如何启用PHP错误日志记录以及如何获取它)。
而对于你拥有的大量数据,如果你将每个表中的所有数据放入内存中的一个字符串中,那么这个数据太大了。简单地采用脚本将附加到文件而不是字符串。这将保持较低的内存要求(但会增加磁盘I / O)。这只是一个常见的权衡(RAM内存与磁盘存储)。你通常首先喜欢RAM,因为它更快,但你没有太多。原始脚本确实只对每个表反映这一点,它不支持字符串长度的字符串缓冲写入。
顺便说一句,即使你运行它的服务器有 - 比方说 - 你的脚本有64 GB的RAM - 如果字符串超过2千兆字节,因为PHP has a string size limit它会失败。