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


How to export millions of rows from MySQL to CSV via PHP without exhausting memory?

所以我有这个表:

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)

包含数百万行:

mysql> SELECT COUNT(1) FROM table;
+----------+
| COUNT(1) |
+----------+
|  9435361 |
+----------+
1 row in set (0.00 sec)

我愿意导出.csv文件中的所有行(我使用的是Symfony2.6(。该文件将存储在服务器上(而不是下载(,稍后由PHP读取。

第一次尝试

我试图提出一个巨大的请求,要求一次选择所有内容(根据这篇博客文章(,但尽管使用了->iterate(),但在运行了大约9秒后,还是出现了Allowed memory size of 1073741824 bytes exhausted

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

第二次尝试

我检索了行的总数,然后做了一个循环,进行相同数量的查询来逐个检索行。但是,在向.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);

第三次尝试

我已经考虑过使用exec()函数来运行MySQL命令行,该命令行将导出表。然而,我的经理似乎不喜欢这个选择。


那么,我是在自欺欺人地认为使用PHP将大约9.5M行转储到.csv文件中是可能的吗?还有其他我还不知道的方式吗?

谢谢你在这个问题上的帮助。

与其尝试构建对象树,不如直接尝试将结果选择到文件中: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;

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


正如venca所指出的:在这种情况下,运行mysql服务的用户需要对有问题的目录具有写入权限。