在mysql表中找到无法导出的“有问题”行

问题描述 投票:3回答:6

我想要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 debugging repair
6个回答
2
投票

正如许多人所说,这个脚本(以及简单的“通过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之前,您应该尝试:

  1. 再次运行脚本,看它是否挂在同一行。这是为了看看我们是否面临“随机”问题(它从来都不是随机的)。
  2. 向函数调用添加偏移量(请参阅可选参数),并第三次运行脚本,以查看它是否仍挂在同一行上。

例如50作为偏移量,一些大数字作为限制:

// Launching the script
progress_export( $file, $table, $idField, 50, 600000 );

这是为了检查它自己的行是否导致问题,或者它是否是临界行数/数据量...

  • 如果每次都返回相同的最后一行,请检查并给我们反馈。
  • 如果添加偏移量以可预测的方式更改最后处理的行,我们可能会在某处遇到资源问题。

如果您无法在分配的资源上播放,那么解决方案将是将导出拆分为块。您可以使用接近此脚本的脚本完成此操作,输出一些HTML / javascript,重定向到它自己,使用offset和limit作为参数,而导出未完成(如果我们最终需要,我将编辑答案) )

  • 如果行几乎每次都改变,那将会更复杂......

一些线索:

我对VPS没有任何经验,但是你对CPU使用有什么限制吗?

如果您一次使用过多的资源,那么您的流程是否会排队?

那些没有问题的转储表怎么样?是否存在与两者一样大的表格导致问题?


1
投票

我不知道为什么这个“阻止”...但脚本只适用于非常基本的数据库。

例如,它如何处理外键约束?这只是一个建议,可能你是故意丢弃它,但为什么不使用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


1
投票

不要将输出保存到变量,而是使用带有FILE_APPEND标志的file_put_contents将查询写入文件。如果您认为需要花费大量时间,可以使用查看器检查文件,或者在webroot目录中创建文件并在浏览器中打开它以查看发生了什么......


1
投票

如果您使用某些自定义脚本和诸如“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行后读取下一批。


1
投票

您是否尝试仅在有问题的表上运行脚本?这些表是否具有二进制字段(如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错误,内存限制错误,最大执行时间错误等。

很喜欢你的项目。


1
投票

你选择的剧本是浪费时间。肯定有更好的,经典和维护一个完全适合的工作是Mysqldumper。无论如何,我不想只在这里给出工具建议,但也读你的问题,你想知道为什么会发生这种情况。

如果你真的想知道,这是麻烦部门的一个提示:你很可能在这里达到内存限制。但是您没有看到,因为这可能是您服务器的硬限制。在这种情况下,脚本刚刚被杀死,而PHP没有提供任何更多错误消息。

而不是仅向STDOUT / STDERR报告错误,您可以登录到文件。我真的建议你为了解决问题。这包括拖动操作系统本身的日志,如/var/log/messages,但也配置PHP写入日志文件(对于硬限制,你不会看到任何东西,因为PHP刚刚被杀死,但我仍然建议你让自己舒服,并了解如何启用PHP错误日志记录以及如何获取它)。

而对于你拥有的大量数据,如果你将每个表中的所有数据放入内存中的一个字符串中,那么这个数据太大了。简单地采用脚本将附加到文件而不是字符串。这将保持较低的内存要求(但会增加磁盘I / O)。这只是一个常见的权衡(RAM内存与磁盘存储)。你通常首先喜欢RAM,因为它更快,但你没有太多。原始脚本确实只对每个表反映这一点,它不支持字符串长度的字符串缓冲写入。

顺便说一句,即使你运行它的服务器有 - 比方说 - 你的脚本有64 GB的RAM - 如果字符串超过2千兆字节,因为PHP has a string size limit它会失败。

© www.soinside.com 2019 - 2024. All rights reserved.