1

ネット上で見つけた変更されたphpmysqlバックアップスクリプトを使用して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/MySQL初心者

ps。ここに私が使用したリンクがありますhttp://www.htmlcenter.com/blog/back-up-your-mysql-database-with-php/

編集:これらの大規模なデータベースをバックアップする場合、mysqldumpは正常に機能しますが、大規模なデータベースは最も変更されたものであり、誰かが作業する必要がある場合、ダンプ中にデータベースをロックすることはできません。そのため、私はこのスクリプトに頼りました。

4

3 に答える 3

2

他のポスターが述べているように、既製のツールを使用したほうがよいでしょうが、とりあえずスクリプトについて話しましょう。

途方もなく大きなテーブルがある場合、それだけのメモリを割り当てる方法はありません。データセットを複数のチャンクに分割する必要があります。次のようなクエリを使用します。

$result = mysql_query('SELECT * FROM '.$table.' LIMIT '.$start.', 10000');

次に、繰り返し$start実行するか、whileループを実行して、まだ結果が得られるかどうかを確認します。このように、10000レコードのチャンクでデータを取得するので、確実にメモリに収まります。

ただし、まだ問題があります–一貫性。チャンクをフェッチしている間に一部の行が変更された場合はどうなりますか?さて、SELECTデータを取得している間、ループをトランザクションでラップし、テーブルをロックすることができます。ロックを回避しようとしていることは知っていますが、テーブルをロックする方がデータベース全体よりも優れています。これでもテーブル間の一貫性は保証されませんが、設定した条件下で実行できるのが最善です。

編集:これがループのコードです。ソリューションを実装しましたwhile。同等のforループがありますが、2つの間の比較はこの投稿の範囲外です。

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 );
}
于 2012-08-22T00:11:24.863 に答える
2

mysql拡張機能には、大規模データベースでのクエリ用に特別に設計された機能mysql_unbuffered_queryがあります。

mysqliの同等のものは、 の 2 番目の引数を利用しますmysqli_query。たとえば、次のようになります。

$res = $mysqli->query('SELECT * FROM large_database', MYSQLI_USE_RESULT);
while ($row = $res->fetch_assoc()) {
    ...
}

ただし、この方法にはいくつかの欠点があります。たとえば、行数がわからない、解放さ$resれるまで他のクエリを実行できないなどです。$res

于 2015-01-16T12:01:51.050 に答える
1

これは、dump が何らかの方法でテーブル サイズに比例する量のメモリを割り当てるという非常に典型的な問題です。mysqldump の「--opt」オプションと同様にスクリプトに実装し、大きなデータベースをダンプする方法を確認する必要があります。たとえば、https: //dba.stackexchange.com/questions/20/how-can-i-optimize-a-大規模データベースの mysqldump

出力を何らかのファイルに記録するmysqldumpを使用しないのはなぜですか?

于 2012-08-21T23:43:56.317 に答える