Laravel中chunk方法分塊處理數據

在一些情況下,我們需要對大批量的數據進行操作,如果這個時候我們使用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 查詢構造器

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章