在一些情況下厢绝,我們需要對大批量的數(shù)據(jù)進行操作,如果這個時候我們使用foreach
的話二蓝,很可能會遇到操作超時的情況翘盖。
在Laravel
框架中我們可以很方便的使用chunk
方法來解決。
來看一個簡單的例子:
$users = User::all();
foreach ($users as $user) {
$some_value = ($user->some_field > 0) ? 1 : 0;
// 一些其他的邏輯
$user->update(['some_other_field' => $some_value]);
}
這段代碼看起來并沒有什么不對瓶摆,但是當數(shù)據(jù)量很大的時候凉逛,情況就不那么樂觀了,一方面是運行時間群井,再者要考慮數(shù)據(jù)存儲時內(nèi)存消耗完状飞。
在Laravel中,應用chunk將數(shù)據(jù)分塊的方法书斜,可以很好的處理這種問題诬辈,下面是應用chunk的代碼:
User::chunk(100, function ($users) {
foreach ($users as $user) {
$some_value = ($user->some_field > 0) ? 1 : 0;
// might be more logic here
$user->update(['some_other_field' => $some_value]);
}
});
這段代碼是執(zhí)行一個100條的數(shù)據(jù)進行更新,當執(zhí)行完成后繼續(xù)后面的另一百條數(shù)據(jù)……
也就是說他每次操作的是一個數(shù)據(jù)塊而不是整個數(shù)據(jù)庫荐吉。
User::chunk(100, function ($users) {
foreach ($users as $user) {
$some_value = ($user->some_field > 0) ? 1 : 0;
// might be more logic here
$user->update(['some_other_field' => $some_value]);
}
});
需要注意的是:當使用帶篩選的條件的chunk時焙糟,如果是自更新,那么你會漏掉一些數(shù)據(jù)样屠,接著看代碼:
User::where('approved', 0)->chunk(100, function ($users) {
foreach ($users as $user) {
$user->update(['approved' => 1]);
}
});
如果要運行上面的代碼穿撮,并不會有報錯,但是where
條件是篩選approved
為0
的user
然后將approved
的值跟新為1
痪欲。
在這個過程中混巧,檔第一數(shù)據(jù)庫的數(shù)據(jù)被修改后,下一個數(shù)據(jù)塊的數(shù)據(jù)將是在被修改后的數(shù)據(jù)中選出來的勤揩,這個時候數(shù)據(jù)變了咧党,而page也加了1。所以執(zhí)行結(jié)束后陨亡,只對數(shù)據(jù)中一半的數(shù)據(jù)進行了更新操作傍衡。
如果沒有明白的話,我們來看一下chunk的底層實現(xiàn)负蠕。還以上面的代碼為例蛙埂,假如一共有400條數(shù)據(jù),數(shù)據(jù)被按照100條進行分塊處理遮糖。
page = 1: 最開始的時候page為1绣的,選取1-100條數(shù)據(jù)進行處理;
page = 2: 這時候前一百數(shù)據(jù)的approved
值全部為1,那么在次篩選的時候數(shù)據(jù)將從第101條開始,而這個時候的page=2,那么處理的數(shù)據(jù)將是第200-300之前的數(shù)據(jù)
之后依舊屡江。
public function chunk($count, callable $callback)
{
$results = $this->forPage($page = 1, $count)->get();
while (count($results) > 0) {
// On each chunk result set, we will pass them to the callback and then let the
// developer take care of everything within the callback, which allows us to
// keep the memory low for spinning through large result sets for working.
if (call_user_func($callback, $results) === false) {
return false;
}
$page++;
$results = $this->forPage($page, $count)->get();
}
return true;
}