2016-12-25 50 views
1

处理所以,我一直在接触到越来越多的现实生活中的应用,其中: SELECT * FROM table太重已经,导致到 Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 47 bytes) in优雅处理大的MySQL行由PHP

什么我目前做的是将它们分MySQL的行成batches,设置变量$perPage,然后$_GET['page']变量传递到页面,一些链接:process.php?page=1

我认为这是“好”。但有时候,我们希望完全自动化。所以我设置$nextPage = (int) $_GET['page'] + 1,然后重定向页面,然后处理这些MySQL的行 header("Location: process.php?p="$nextPage)

现在,经过迭代,这将给你一些问题:

  1. 其难以调试。如果在第3页发出警告/通知,您将无法看到这些输出,除非您记录它们或查看您的php日志。
  2. 浏览器不允许你重定向太多。所以,我们要么使用命令行curl,要么编写另一个php脚本,它将跟踪process.php并启用follow-redirect。

这就是我目前如何处理这个问题,但有时候认为我需要更多的代码才能使其工作有点令人沮丧。 有谁知道如何处理这些更优雅?

回答

1

PHP Generators对于这种情况非常理想。

甲发生器允许编写使用的foreach超过 迭代的一组数据,而无需建立在存储器阵列,其可以 导致您超出存储器限制,或需要大量代码 的处理时间来生成。相反,您可以编写一个发生器 函数,该函数与正常函数相同,只不过返回一次而不是返回 ,发生器可以产生尽可能多的次数,以便提供要迭代的值。

您不应该将所有数据存储到内存,而是通过块处理记录。

使用yield(来自C)将允许您实现这一点。

忘掉重定向的东西,这个声音好像应该是CLI脚本。

下面是关于如何实现它的一个例子:

function getRecords() 
{ 
    $sql = 'SELECT field1, field2 FROM table'; 
    $stmt = $this->conn->prepare($sql); 
    $stmt->execute(); 
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { 
     yield $row; 
    } 
} 

foreach ($this->getRecords() as $record) { 
    //process then release, do not store 
} 

注意:

  1. 我选择特定字段,因为内存是你应该小心一切顾虑,SELECT *可以不必要地使你用完了内存。
  2. fetchAll()之类的东西是行不通的,因为它会一次获得所有行。
  3. 在迭代foreach时,不要将数据存储在内存中,这会破坏生成器的用途。
+0

您是专业人士。显然,这就是所需要的! –

0

在循环中;从数据库中获取一段数据做你想做的事,用unset()函数释放内存,然后获得下一条数据。