在一些情況下,我們需要對大批量的數據進行操作,如果這個時候我們使用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]);
}
這段代碼看起來並沒有什麼不對,但是當數據量很大的時候,情況就不那麼樂觀了,一方面是運行時間,再者要考慮數據存儲時內存消耗完。
在Laravel中,應用chunk將數據分塊的方法,可以很好的處理這種問題,下面是應用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]);
}
});
這段代碼是執行一個100條的數據進行更新,當執行完成後繼續後面的另一百條數據……
也就是說他每次操作的是一個數據塊而不是整個數據庫。
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時,如果是自更新,那麼你會漏掉一些數據,接着看代碼:
User::where('approved', 0)->chunk(100, function ($users) {
foreach ($users as $user) {
$user->update(['approved' => 1]);
}
});
如果要運行上面的代碼,並不會有報錯,但是where條件是篩選approved爲0的user然後將approved的值跟新爲1。
在這個過程中,檔第一數據庫的數據被修改後,下一個數據塊的數據將是在被
修改後的數據
中選出來的,這個時候數據變了,而page也加了1
。所以執行結束後,只對數據中一半的數據進行了更新操作。
如果沒有明白的話,我們來看一下chunk的底層實現。還以上面的代碼爲例,假如一共有400條數據,數據被按照100條進行分塊處理。
page = 1: 最開始的時候page爲1,選取1-100條數據進行處理;
page = 2: 這時候前一百數據的approved值全部爲1,那麼在次篩選的時候數據將從第101條開始,而這個時候的page=2,那麼處理的數據將是第200-300之前的數據之後依舊。
chunk的分塊機制源碼:
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;
}
如果要在分塊結果時更新數據庫記錄,則塊結果可能會和預計的返回結果不一致。 因此,在分塊更新記錄時,最好使用 chunkById
方法。 此方法將根據記錄的主鍵自動對結果進行分頁:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
foreach ($users as $user) {
DB::table('users')
->where('id', $user->id)
->update(['active' => true]);
}
});
提示在塊的回調裏面更新或刪除記錄時,對主鍵或外鍵的
任何更改都可能影響塊查詢
。 這可能會導致記錄沒有包含在分塊結果中。
Process big DB table with chunk() method
Laravel中chunk方法分塊處理數據
Laravel 查詢構造器