简体   繁体   English

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

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

I am writing an artisan console command that loops through all the records in a table and regenerates a field on that table. 我正在编写一个工匠控制台命令,该命令循环遍历表中的所有记录并重新生成该表上的字段。

The field is a hash and is generated as an md5() of a particular string. 该字段是一个hash并作为特定字符串的md5()生成。

Initially my code looked like this: 最初,我的代码如下所示:

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

After getting to around 10,000 of 28,000 records it would die with a memory exhausted error: 在达到28,000条记录中的10,000条之后,它将死于内存耗尽错误:

PHP Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 4096 bytes) PHP致命错误:耗尽的内存容量为268435456字节(尝试分配4096字节)

I thought chunk ing this might help: 我认为分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.');

But I am still getting a memory exhaustion error. 但是我仍然遇到内存耗尽错误。

How can I loop through all these records and achieve what I am after without increasing the memory limit? 我如何遍历所有这些记录并实现我所追求的目标而又不增加内存限制?

The way you're "chunking" is actually consuming more memory than the initial code. 您“分块”的方式实际上比初始代码消耗更多的内存。

What you're doing is getting all the records at once , storing them in $recipes and then chunking the results by calling the chunk() on the resulted collection . 您要做的是一次获取所有记录,将它们存储在$recipes ,然后通过对结果集合调用chunk()结果进行分块。

Instead, you need to call the method with the same name, chunk() , on the underlying Recipe model's query builder and generate hashes chunk by chunk: 相反,您需要在基础Recipe模型的查询生成器上使用相同名称的方法chunk()进行调用,并逐块生成哈希:

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

This way, you eliminate having a huge $recipes variable which I'm sure is the bottleneck here. 这样,您就消除了拥有巨大的$recipes变量的$recipes ,我确定这是这里的瓶颈。 Depending on the available memory, you might need to tweak the chunk size a bit to avoid memory exhaustion. 根据可用内存,您可能需要稍微调整块大小以避免内存耗尽。

Also, I'd try to use fewer variables when generating the hash instead of leaving a trail of $extras1Total , extras2Total , ... variables. 另外,在生成哈希值时,我会尝试使用较少的变量,而不要留下$extras1Totalextras2Total ,...变量的痕迹。 All of them can be replaced with a $total that will be rewritten over and over. 所有这些都可以用将被一遍又一遍重写的$total代替。 This is micro-optimization though. 不过,这是微优化。

PS In case of significant database write stress (which is rare with 28k total), you might want considering to do the final updates in one (or few) go(es) instead of doing it per record. PS在数据库写入压力很大的情况下(总计28k很少见),您可能要考虑一次(或几次)执行最终更新,而不是对每个记录进行一次。

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

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