PHP:在不改变 memory_limit 和 max_execution_time 的情况下读取和导出大数据

Posted

技术标签:

【中文标题】PHP:在不改变 memory_limit 和 max_execution_time 的情况下读取和导出大数据【英文标题】:PHP : Read and Export big data without changing memory_limit & max_execution_time 【发布时间】:2018-10-22 09:50:05 【问题描述】:

我有很多数据可以导出到 csv 文件中。我的函数循环到每个字段并执行一个函数以从 sql 表中获取数据。 现在我有一个非常大的数据库,我想在不更改 memory_limit 配置的情况下导出一些数据,因为我不想阻止其他用户。

如何执行我的功能?

例如: 我有 100000 人,每个人都有很多版本的一些数据。他们每天都会保存这样的信息:

Person Table
+-----------+-------------+-------------+
| id_person | name_person | city_person |
+-----------+-------------+-------------+
| 1         | Jack        | Paris       |
+-----------+-------------+-------------+
| 2         | John        | London      |
+-----------+-------------+-------------+
| ...       | ...         | ...         |
+-----------+-------------+-------------+
| 99999     | Rose        | Madrid      |
+-----------+-------------+-------------+
| 100000    | Jackie      | Rome        |
+-----------+-------------+-------------+

Field Table
+----------+------------+-------------------+
| id_field | name_field | label_field       |
+----------+------------+-------------------+
| 1        | Location   | Visited location  |
+----------+------------+-------------------+
| 2        | Article    | Count of articles |
+----------+------------+-------------------+
| ...      | ...        | ...               |
+----------+------------+-------------------+
| 289      | Distance   | Distance          |
+----------+------------+-------------------+
| 299      | Pause      | Time of pause     |
+----------+------------+-------------------+

Field Value Table
+----------+----------+-----------+----------------+------------+
| id_value | id_field | id_person | value          | Date       |
+----------+----------+-----------+----------------+------------+
| 1        | 1        | 148       | Hanover Street | 2015-05-10 |
+----------+----------+-----------+----------------+------------+
| 2        | 66       | 57962     | 20             | 2015-05-10 |
+----------+----------+-----------+----------------+------------+
| ...      | ...      | ...       | ...            |            |
+----------+----------+-----------+----------------+------------+
| 3475992  | 105      | 847       | 17,5           | 2018-02-01 |
+----------+----------+-----------+----------------+------------+
| 3475993  | 15       | 66359     | 44             | 2018-02-01 |
+----------+----------+-----------+----------------+------------+

每个字段都有特定的功能来获取数据。

如何在不更改限制内存的情况下将所有数据导出到 csv 文件中?

谢谢

【问题讨论】:

请向我们展示您是如何组装和写出这些数据的,并说明您在什么时候遇到了这个问题。 我对字段表中的每个字段都有一个功能。例如:我有函数 GetArticle($persons) 这个函数在字段值表中搜索 $person 数组中每个人的文章字段值的最后一个版本。当 $persons 列表很大并且字段列表也很大时。我有超时和内存限制错误 对不起,我在完成评论之前按了回车! 【参考方案1】:

使用无缓冲查询、隐式刷新、将数据直接发送到输出缓冲区(用于下载)、使用 CLI(用于文件导出)。关闭/增加时间限制(如果需要),仅针对此脚本,而不是全局。

http://php.net/manual/en/mysqlinfo.concepts.buffering.php

http://php.net/manual/en/wrappers.php.php

How to flush output after each `echo` call?(@Roger 的回答)

http://php.net/manual/en/function.set-time-limit.php

我写的代码太多了,还有太多的未知数。比如你使用什么数据库(MySQL、MsSQL 等)、什么数据库类、PDO 或 MySqli?您是导出到服务器上的文件还是下载。您是否希望数据为 CSV、SQL 等格式。

不缓冲查询会花费更多网络成本,花费更长的时间,但更好地管理内存并更好地处理更大的表。 隐式刷新使输出缓冲区保持较小(管理内存)。 发送数据到php://output更好的内存管理,效率更高。 时间限制应该很明显。

我的函数循环到每个字段并执行一个函数从 sql 表中获取数据。

使用联接而不是重复调用数据库,在表上使用适当的索引。

可以使用ini_set('memory_limit' ...)set_time_limit,因为它们只影响当前的PHP 进程,而不是全局的。如果可以的话,显然最好避免它们,但有时这是不可能的。

最快的导出方式是mysqldump:

https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html

但它有局限性(例如)

https://dba.stackexchange.com/questions/4654/is-it-possible-to-mysqldump-a-subset-of-a-database-required-to-reproduce-a-query

你不能使用JOIN导出,复杂的查询会变得非常困难,因为我认为你只能使用基本的--where调用,没有聚合..等等。

【讨论】:

你能给我更多的解释吗?我是缓冲区输出的初学者谢谢 没有更多关于你在做什么的信息。 我已经在其他评论中解释了这一点:我对字段表中的每个字段都有一个功能。例如:我有函数 GetArticle($persons) 这个函数在字段值表中搜索 $person 数组中每个人的文章字段值的最后一个版本。当 $persons 列表很大并且字段列表也很大时。我有超时和内存限制错误我不能使用连接,因为每个字段都有特定的行为和特定的显示格式(列表、文本、数字、电话、货币...) I can't use joins because each field have a specific behavior - 你可以使用连接,这些specific behavior 是用PHP 完成的?使用连接后没有什么能阻止你这样做。查询比较复杂,是的,放PHP代码会比较简单(一般来说)。 PS 仍然没有告诉我任何有用的信息。 最后,PHP.net 上有大量的例子,那里解释得更好,然后我可以在这里做。【参考方案2】:

有两种方法可以读取和导出大数据

通过批处理 - 将批量数据拆分成块并使用 sleep 然后继续下一个要处理的块。 通过队列项目进入数据库

示例代码

$con = 'mysql:host=localhost;dbname=example';
$username = 'example';
$password = 'example';
$pdo = new PDO($con, $username, $password);

$i = !empty($_GET['pass']) ? (int) $_GET['pass'] : 0;

$string = "SELECT * FROM users LIMIT $i,10";
$query = $pdo->prepare("$string");
$query->execute();
// This would not fill memory anymore.
$results = $query->fetchAll();
// Nothing to do, we have finished.
if (!count($results)) 
  return;

foreach ($results as $result) 
  // Perform lengthy operation.
  sleep(1);


$i++;
// Send request back to process next execution.
$redirect = $_SERVER['DOCUMENT_URI'] . '?pass=' . $i;
header("Location: $redirect");
exit();

【讨论】:

你能给我一个例子吗? @prozbk 我已经用示例编辑了答案。请参考。 谢谢我会看到,并确认结果【参考方案3】:

通过命令尝试此导出

mysqldump -p -u username database_name > dbname.csv

【讨论】:

他正在尝试在 PHP 中执行此操作,导出 MySQL 转储不会创建 CSV 我不想导出 sql 文件。我想导出一个 csv 文件 @VinothRaja 我不想导出我拥有的从多个表中获取我想要的数据的所有数据库。当我循环进入列表时,我有超时和内存错误! mysqldump 不是解决方案

以上是关于PHP:在不改变 memory_limit 和 max_execution_time 的情况下读取和导出大数据的主要内容,如果未能解决你的问题,请参考以下文章

PHP:设置 memory_limits > 1024M 不起作用

php的memory_limit、upload_max_filesize和post_max_filesize的关系

一次php内存溢出的解决及思考

如何在上传图像时修复WordPress上的HTTP错误?

如何修改PHP的memory_limit限制

PHP修改memory_limit的三种办法