繁体   English   中英

Foreach()和each()内存不足,分块无法正常工作

[英]Foreach() and each() running out of memory, chunking not working

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

该字段是一个hash并作为特定字符串的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字节)

我认为分chunk这可能会有所帮助:

// 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.');

但是我仍然遇到内存耗尽错误。

我如何遍历所有这些记录并实现我所追求的目标而又不增加内存限制?

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

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

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

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

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

另外,在生成哈希值时,我会尝试使用较少的变量,而不要留下$extras1Totalextras2Total ,...变量的痕迹。 所有这些都可以用将被一遍又一遍重写的$total代替。 不过,这是微优化。

PS在数据库写入压力很大的情况下(总计28k很少见),您可能要考虑一次(或几次)执行最终更新,而不是对每个记录进行一次。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM