Featured image of post Laravel 大量寫入的好幫手 chunk

Laravel 大量寫入的好幫手 chunk

What is chunk

可以將 chunk 理解成…
我將一大組資料分「塊」處理,
而不是逐筆處理

Why chunk

假設每筆就要執行一段SQL,
這樣我 5000筆資料,就要執行 5000次
如果我將這 5000 筆,每 500 分成一塊,我只要執行 10 次。
如此一來可以有效縮減執行時間也降低了DB的負擔。

How to chunk

Laravel chunk 分成三種

  1. Query Builder chunk
  2. Eloquent chunk
  3. Collection chunk

假設情境

當我有好幾萬的會員,每年要贈送 100點的點數給他們,
我們建立了一個排程,每年1月1日贈送,
隨著會員數量的增加,執行時間會愈來愈久,
我們必須著手優化這段排程 …

Original (No chunk)

當我們沒有chunk通常都是逐筆寫入
這邊用 each 作為範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12

User::query()
    ->get()
    ->each(function($user) {

        Point::insert([
            'amount' => 100,
            'user_id' => $user->id,
            'created_at' => now()->toDateTimeString(),
            'updated_at' => now()->toDateTimeString(),
        ]);
    });

Query Builder chunk

可參考 文件

使用方式和 Eloquent chunk 相同
所以我會在下方 Eloquent chunk 共同解釋

Eloquent chunk

可參考 官方文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18

User::query()
    ->chunk(500, function($users) {
        
        $data = [];
        foreach ($users as $user) {

            $data[] = [
                'amount' => 100,
                'user_id' => $user->id,
                'created_at' => now()->toDateTimeString(),
                'updated_at' => now()->toDateTimeString(),
            ];                       
            
        }

        Point::insert($data);
    });

運作的方式是用 SQL ,
使用 limit 500 offset 0limit 500 offset 500 ….. 直到分完全部
在 query 當下就分塊。

Collection chunk

可參考 官方文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$chunks = User::query()
    ->get()
    ->map(function($user) {

        return [
            'amount' => 100,    
            'user_id' => $user->id,
            'created_at' => now()->toDateTimeString(),
            'updated_at' => now()->toDateTimeString(),
        ];                       
    })
    ->chunk(500)
    ->toArray();

foreach ($chunks as $chunk) {
    Point::insert($chunk);
}

運作的方式是先都 query出來,再用 php 整理的,

如圖所示

比較

老實說使用哪一個其實感覺不出來,
因為執行效率差不多,
Eloquent 吃DB資源,Collection 吃記憶體資源,

Collection chunk 遇過直接網頁執行,記憶體吃掛了的問題,

最後我還是選擇了 Eloquent chunk
雖然 執行的SQL多了一些 query,但不會吃你記憶體,

也是看大家取捨拉 ~

這是我執行3千筆的資訊

資料量運用花費時間記憶體
3000筆Original (No chunk)6.6s142MB
3000筆Eloquent chunk1.52s33MB
3000筆Collection chunk1.49s141MB

題外話

這裡的寫入使用了 Point::insert 為何不使用 Point::create ?
是為了批次寫入及效率的問題考量,
之後再來聊聊 Laravel insert & Laravel create 差異吧

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy