当前位置: 首页 > 知识库问答 >
问题:

Foreach()和each()内存不足,分块不起作用

何升
2023-03-14

我正在编写一个工匠控制台命令,该命令循环遍历表中的所有记录,并在该表上重新生成一个字段。

该字段是一个散列,并作为特定字符串md5()生成。

最初我的代码是这样的:

// Get all recipes
$recipes = Recipe::all();

$hashProgress = $this->output->createProgressBar(count($recipes));

// Loop over each recipe and generate a new hash for it
foreach ($recipes as $recipe)
{
    $hashString = '';

    $hashString .= $recipe->field1;
    $hashString .= $recipe->field2;
    $hashString .= $recipe->field3;
    $hashString .= $recipe->field4;
    $hashString .= $recipe->field5;
    $hashString .= $recipe->field6;
    $hashString .= $recipe->field7;

    $extras1Total = $recipe->extras1->sum('amount');
    $hashString .= $recipe->extras1->reduce(function ($str, $item) use ($extras1Total) {
        return $str . $item->name . ($extras1Total == 0 ? $item->amount : ($item->amount / $extras1Total * 100));
    }, '');

    $extras2Total = $recipe->extras2->sum('amount');
    $hashString .= $recipe->extras2->reduce(function ($str, $item) use ($extras2Total) {
        return $str . $item->name . ($extras2Total == 0 ? $item->amount : ($item->amount / $extras2Total * 100));
    }, '');

    $extras3Total = $recipe->extras3->sum('amount');
    $hashString .= $recipe->extras3->reduce(function ($str, $item) use ($extras3Total) {
        return $str . $item->name . ($extras3Total == 0 ? $item->amount : ($item->amount / $extras3Total * 100));
    }, '');

    $extras4Total = $recipe->extras4->sum('amount');
    $hashString .= $recipe->extras4->reduce(function ($str, $item) use ($extras4Total) {
        return $str . $item->name . ($extras4Total == 0 ? $item->amount : ($item->amount / $extras4Total * 100));
    }, '');

    $recipe->update([
        'hash' => md5($hashString),
    ]);

    $hashProgress->advance();
}

$hashProgress->finish();
$this->info(' Recipe hashes regenerated.');

当达到28,000条记录中的大约10,000条时,它会因内存耗尽错误而死亡:

PHP致命错误:已耗尽268435456字节的允许内存大小(尝试分配4096字节)

我想chunking这可能会有帮助:

// Get all recipes
$recipes = Recipe::all();

$hashProgress = $this->output->createProgressBar(count($recipes));

// Loop over each recipe and generate a new hash for it
foreach ($recipes->chunk(1000) as $chunk)
{
    foreach ($chunk as $recipe)
    {
        $hashString = '';

        $hashString .= $recipe->field1;
        $hashString .= $recipe->field2;
        $hashString .= $recipe->field3;
        $hashString .= $recipe->field4;
        $hashString .= $recipe->field5;
        $hashString .= $recipe->field6;
        $hashString .= $recipe->field7;

        $extras1Total = $recipe->extras1->sum('amount');
        $hashString .= $recipe->extras1->reduce(function ($str, $item) use ($extras1Total) {
            return $str . $item->name . ($extras1Total == 0 ? $item->amount : ($item->amount / $extras1Total * 100));
        }, '');

        $extras2Total = $recipe->extras2->sum('amount');
        $hashString .= $recipe->extras2->reduce(function ($str, $item) use ($extras2Total) {
            return $str . $item->name . ($extras2Total == 0 ? $item->amount : ($item->amount / $extras2Total * 100));
        }, '');

        $extras3Total = $recipe->extras3->sum('amount');
        $hashString .= $recipe->extras3->reduce(function ($str, $item) use ($extras3Total) {
            return $str . $item->name . ($extras3Total == 0 ? $item->amount : ($item->amount / $extras3Total * 100));
        }, '');

        $extras4Total = $recipe->extras4->sum('amount');
        $hashString .= $recipe->extras4->reduce(function ($str, $item) use ($extras4Total) {
            return $str . $item->name . ($extras4Total == 0 ? $item->amount : ($item->amount / $extras4Total * 100));
        }, '');

        $recipe->update([
            'hash' => md5($hashString),
        ]);

        $hashProgress->advance();
    }
}

$hashProgress->finish();
$this->info(' Recipe hashes regenerated.');

但我仍然得到一个内存耗尽错误。

我如何在不增加内存限制的情况下循环浏览所有这些记录并实现我所追求的目标?

共有1个答案

方波娃
2023-03-14

“分块”的方式实际上比初始代码消耗更多的内存。

您要做的是一次获取所有记录,将它们存储在$recipes中,然后通过调用结果集合上的chunk()对结果进行分块。

相反,您需要在基础配方模型的查询生成器上调用同名的方法chunk(),并逐块生成哈希:

Recipe::chunk(1000, function ($recipies) {
    // Hash generation logic here
});

这样,您就消除了一个巨大的$recipes变量,我确信这是这里的瓶颈。根据可用内存的不同,您可能需要稍微调整块大小以避免内存耗尽。

另外,在生成散列时,我会尝试使用更少的变量,而不是留下$extras1Totalextras2Total。。。变量。所有这些都可以替换为将被反复重写的$total。但这是微观优化。

P. S.在数据库写压力很大的情况下(总28k很少),您可能需要考虑在一个(或几个)go(es)中执行最终更新,而不是每个记录执行。

 类似资料:
  • 我正在努力解决古老的字谜问题。多亏了许多教程,我能够迭代一组字符串,递归地找到所有的排列,然后将它们与英语单词列表进行比较。我发现的问题是,在大约三个单词之后(通常是关于“变形”之类的东西),我会得到一个OutOfMemory错误。我试着把我的批分成小的集合,因为它似乎是消耗我所有内存的递归部分。但即使只是“变形”也把它锁起来了... 编辑:根据出色的反馈,我已经将生成器从排列更改为工作查找: 它

  • 我正在处理大的CSV文件,我需要做一个笛卡尔积(合并操作)。我试着面对Pandas的问题(您可以在这里检查Panda的代码和数据格式示例以查找相同的问题),但由于内存错误而没有成功。现在,我尝试使用Dask,它应该可以管理巨大的数据集,即使它的大小大于可用的RAM。 首先,我阅读了CSV: 然后,我进行了磁盘存储操作,以防止内存错误: 我做了一个回购,尝试与我正在使用的完全相同的CSV文件。我尝试

  • 问题内容: 我今天遇到一个奇怪的问题。对于其他人来说,这可能是一个简单的答案,但这让我感到困惑。为什么下面的代码会导致内存错误? 我得到了这两个错误之一…第一个是在节点的解释器中运行此代码时,第二个是通过nodeunit运行它时: 严重错误:CALL_AND_RETRY_2分配失败-内存不足 严重错误:JS分配失败-内存不足 问题答案: 当我尝试访问阵列时会发生这种情况。但是获取长度却没有。

  • 问题内容: 我正在研究真正了解JVM中内存分配的工作方式。我正在编写一个内存不足的应用程序:堆空间异常。 我知道我可以传入VM参数(例如Xms和Xmx)来增加JVM为正在运行的进程分配的堆空间。这是解决此问题的一种可能的解决方案,或者我可以检查代码是否存在内存泄漏并在那里解决问题。 我的问题是: 1)JVM如何实际为其分配内存?这与OS如何将可用内存传递给JVM有什么关系?或更一般而言,任何进程的

  • 我想真正了解内存分配在JVM中是如何工作的。我正在编写一个内存不足的应用程序:堆空间异常。 我知道我可以传入VM参数,如Xms和Xmx,以增加JVM为正在运行的进程分配的堆空间。这是一个可能的解决方案,或者我可以检查我的代码内存泄漏并修复那里的问题。 我的问题是: 1)JVM实际上如何为自己分配内存?这与OS如何将可用内存传递给JVM有什么关系?或者更一般地说,为任何进程分配内存实际上是如何工作的

  • 问题内容: 我正在使用Java 8(内部版本1.8.0_25),Netbeans 8.0.2,并将Java 8的某些功能合并到现有应用程序中。排序和.forEach无法正常工作,因此我创建了一些测试代码以确保我了解lambda等,并诊断出问题。以下是新代码以及与我的系统中的数据进行交互的代码的组合: 注册是一个POJO,反映事件的数据,其中包括一个RegistrationItem(s)列表,这是另