所以,我已经接触到越来越多的现实生活应用: 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)
现在,这会给您一些问题:
- 很难调试。如果第3页中发出警告/通知,除非您将其记录或查看PHP日志,否则您将无法看到这些输出。
- 浏览器不允许您重定向太多。因此,我们要么使用命令行卷曲或代码代码另一个PHP脚本,该脚本将在启用后续重新启用的情况下curl
process.php
。
这就是我当前处理的方式,但是有时认为我需要更多地编码以使其正常工作有点令人沮丧。有人知道如何更优雅地处理这些?
php发电机是此类情况的理想选择。
发电机允许您编写使用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
}
注意:
- 我选择特定字段,由于内存是一个问题,因此您应该小心所有内容,
SELECT *
可以不必要地使您无法记忆。 - 类似
fetchAll()
之类的东西是行不通的,因为它会立即获得所有行。 - 在
foreach
中迭代时不要将数据存储在内存中,这会破坏发电机的目的。
在循环中;从数据库中获取一块数据,请使用UNSET()函数免费内存,然后获取下一个数据。