[英]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
,我確定這是這里的瓶頸。 根據可用內存,您可能需要稍微調整塊大小以避免內存耗盡。
另外,在生成哈希值時,我會嘗試使用較少的變量,而不要留下$extras1Total
, extras2Total
,...變量的痕跡。 所有這些都可以用將被一遍又一遍重寫的$total
代替。 不過,這是微優化。
PS在數據庫寫入壓力很大的情況下(總計28k很少見),您可能要考慮一次(或幾次)執行最終更新,而不是對每個記錄進行一次。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.