优雅地处理要由PHP处理的大型MySQL行

arv*_*vil 1 php mysql

因此,我接触到越来越多的现实生活应用程序,其中: SELECT * FROM table已经太重了,导致 Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 47 bytes) in

我目前正在做的是将这些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或编写另一个process.php将启用cURL 并启用跟随重定向的php脚本。

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

med*_*eda 5

PHP生成器非常适合这种情况。

生成器允许您编写使用foreach遍历一组数据的代码,而无需在内存中构建数组,这可能会导致您超出内存限制,或需要大量的处理时间才能生成。取而代之的是,您可以编写一个生成器函数,该函数与普通函数相同,不同之处在于,生成器可以返回所需的多次次数,而不是返回一次,以提供要迭代的值。

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

使用yield(from 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
}
Run Code Online (Sandbox Code Playgroud)

注意:

  1. 我选择特定的字段,因为考虑到内存问题,您在处理所有事情时都应格外小心,否则可能SELECT *会不必要地导致内存不足。
  2. 诸如此类的东西fetchAll()行不通,因为它将一次获得所有行。
  3. 迭代时不要将数据存储在内存中,foreach这会破坏生成器的目的。