【问题标题】:mysql fetch row memory errormysql获取行内存错误
【发布时间】:2012-08-21 23:34:37
【问题描述】:

我正在使用我在网上找到的修改后的 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/MySQL 新手

ps。这是我使用的链接http://www.htmlcenter.com/blog/back-up-your-mysql-database-with-php/

编辑:mysqldump 在备份这些大型数据库时工作正常,但大型数据库是修改最多的数据库,当有人需要处理它们时,我无法在转储时锁定我的 dbs。这就是我使用这个脚本的原因。

【问题讨论】:

  • 如果您的数据库太大以至于无法满足其内存需求,我认为它无法满足您的要求。这确实是mysqldump 的工作……特别是考虑到这只是转储表,而不是任何触发器或您最终可能需要定义的过程。
  • 我最初使用 mysqldump 进行转储,但问题是我会在一夜之间备份,它会在转储期间锁定数据库并防止海外人员修改它们。目前使用 Myisam dbs,这个脚本可以让我在不将 dbs 锁定到远程 PC 的情况下进行备份。我正在购买新服务器,但那是几个月后的事了,所以这是我备份数据库的临时解决方案,因为我将实施一些不同的东西。哦,由于使用了我的服务器需要的临时表,复制也被破坏了。
  • 我将编辑我的帖子以进一步说明我的情况。不过感谢您的回复。

标签: php mysql memory


【解决方案1】:

正如其他海报所提到的,您最好使用现成的工具,但现在让我们谈谈您的脚本。

如果你有一个大得离谱的表,那么就没有办法分配那么多内存。您必须将数据集拆分为多个块。使用如下查询:

$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 );
}

【讨论】:

  • 感谢您的回复!但问题是,如何迭代查询的其余部分?
  • 我会再次调用查询,它会吐出接下来的 10k 行吗?比如 while($result = mysql_query(.......); 完全相同的行?还是另一个查询命令?
  • 哦,这就是迭代 $start 的意思。不知道你可以用限制做到这一点。每天学习新东西。感谢您的帮助!
【解决方案2】:

mysql 扩展有 mysql_unbuffered_query 函数专门为大型数据库的查询而设计。

mysqli 中的等价物利用mysqli_query 的第二个参数,例如:

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

这种方法有一些缺点,例如在$res 被释放之前,您无法知道$res 中的行数,并且您无法执行其他查询。

【讨论】:

    【解决方案3】:

    这是一个非常典型的问题,转储以某种方式分配与表大小成正比的内存量。您应该在脚本中实现类似于 mysqldump 中的“--opt”选项,并查看如何转储大型数据库,例如此处https://dba.stackexchange.com/questions/20/how-can-i-optimize-a-mysqldump-of-a-large-database

    为什么不直接使用 mysqldump 将输出记录到某个文件?

    【讨论】:

    • 在上面的评论中回答了这个问题,但是 mysqldump 会锁定我的数据库并阻止人们在转储时使用它们。这就是我在遇到锁定问题之前最初备份它们的方式。
    • 我可能会把它放在我原来的帖子里。不过感谢您的回复。我也会读到那个链接。
    • 你可以在没有锁的情况下做一个 mysqldumps :)(只需阅读 mysqldump 手册)
    • 我已经读过,如果我没有锁,并且它被修改,那么整个备份就无法使用?加上转储这些大型数据库需要很长时间,所以我真的没有时间测试。
    猜你喜欢
    • 1970-01-01
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 2017-07-19
    • 2011-10-10
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    相关资源
    最近更新 更多