如何通过PHP将数百万行从MySQL导出到CSV而不会耗尽内存?

D4V*_*V1D 4 php mysql csv dql symfony

所以我有这张桌子:

mysql> DESCRIBE table;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(15) unsigned | NO   | PRI | NULL    | auto_increment |
| unid  | char(9)          | NO   | UNI | NULL    |                |
| rs    | varchar(255)     | NO   |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

其中包含数百万行:

mysql> SELECT COUNT(1) FROM table;
+----------+
| COUNT(1) |
+----------+
|  9435361 |
+----------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

我愿意导出.csv文件中的所有行(我正在使用Symfony2.6).此文件旨在存储在服务器上(未下载),稍后由PHP读取.

第一次尝试

我试图提出一个巨大的请求,一次选择所有(根据这篇博文),但这有,尽管使用->iterate(),导致Allowed memory size of 1073741824 bytes exhausted运行~9s后.

    ini_set('memory_limit', '1024M');
    ini_set('max_execution_time', -1);

    $results = $em
        ->getRepository('MyBundle:Entity')
        ->createQueryBuilder('e')
        ->getQuery()
        ->iterate();
    $handle = fopen('/path/to/csv/file/', 'w');
    while (false !== ($row = $results->next())) {
        fputcsv($handle, $row[0]->toArray());
        $em->detach($row[0]);
    }
    fclose($handle);
Run Code Online (Sandbox Code Playgroud)

第二次尝试

我检索了总行数,然后做了一个循环,使得相同数量的查询逐个检索行.但是在向.csv文件写入~260K行之后,PHP内存耗尽并抛出与上面相同的错误:Allowed memory size of 1073741824 bytes exhausted.

    ini_set('memory_limit', '1024M');
    ini_set('max_execution_time', -1);

    $total = (int) $em
        ->getRepository('MyBundle:Entity')
        ->countAll();
    $csv = '/path/to/csv/file';
    $handle = fopen($csv, 'w');
    for($i = 1; $i < $total; $i++)
    {
        $entity = $em->getRepository('MyBundle:Entity')->findOneById($i);
        fputcsv($handle, $entity->toArray());
        $em->detach($entity);
    }
    fclose($handle);
Run Code Online (Sandbox Code Playgroud)

第三次尝试

我曾想过使用该exec()函数来运行导出表的MySQL命令行.但是,我的经理似乎不喜欢这个选项.


所以我自欺欺人地认为使用PHP将~9.5M的行转储到.csv文件中甚至可能吗?还有其他我还没知道的方法吗?

感谢您对此的帮助.

dog*_*ose 10

您可以直接尝试将结果选择到文件中,而不是尝试构建对象树:http://dev.mysql.com/doc/refman/5.7/en/select.html

就像是

SELECT * INTO OUTFILE "c:/temp/mycsv.csv"
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY "\n"
FROM theTable;
Run Code Online (Sandbox Code Playgroud)

这应该将作业留给mysql并绕过任何php内存限制.


正如venca所指出的那样:在这种情况下,运行mysql服务的用户需要对相关目录具有写权限.

  • 运行mysql服务器的用户需要对目录的写权限,要保存csv. (3认同)