如何在不耗尽内存的情况下通过 PHP 将数百万行从 MySQL 导出到 CSV?
How to export millions of rows from MySQL to CSV via PHP without exhausting memory?
所以我有这个 table:
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 读取。
第一次尝试
我试图一次性向 select 提出一个巨大的请求 (as per this blog post),但是尽管使用了 ->iterate()
,这导致了 Allowed memory size of 1073741824 bytes exhausted
运行 约 9 秒。
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);
第二次尝试
我检索了总行数,然后循环进行相同数量的查询以逐行检索。但是在将 ~260K 行写入 .csv
文件后,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()
函数用于 运行 将导出 table 的 MySQL 命令行。但是,我的经理似乎不喜欢这个选项。
那么我是不是在自欺欺人地认为使用 PHP 将 ~9.5M 行转储到 .csv
文件中甚至是可能的?还有其他我还不知道的方法吗?
感谢您对此的帮助。
与其尝试构建对象树,不如直接尝试将结果 select 写入文件: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 服务需要对相关目录的写入权限。
所以我有这个 table:
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 读取。
第一次尝试
我试图一次性向 select 提出一个巨大的请求 (as per this blog post),但是尽管使用了 ->iterate()
,这导致了 Allowed memory size of 1073741824 bytes exhausted
运行 约 9 秒。
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);
第二次尝试
我检索了总行数,然后循环进行相同数量的查询以逐行检索。但是在将 ~260K 行写入 .csv
文件后,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()
函数用于 运行 将导出 table 的 MySQL 命令行。但是,我的经理似乎不喜欢这个选项。
那么我是不是在自欺欺人地认为使用 PHP 将 ~9.5M 行转储到 .csv
文件中甚至是可能的?还有其他我还不知道的方法吗?
感谢您对此的帮助。
与其尝试构建对象树,不如直接尝试将结果 select 写入文件: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 服务需要对相关目录的写入权限。