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 不起作用