# Laravel 效能優化筆記 提升 Laravel 應用程式效能是一個持續的過程,需根據應用特性選擇合適的優化策略,並持續監控與調整,以確保高負載下仍能維持良好效能。 --- ## 一、批次處理:`chunk()` 與 `chunkById()` ### chunk() ```php Model::chunk($count, function ($records) { // 處理每批資料 }); ```` **優點:** * 節省記憶體,每次只載入少量資料。 * 適合長時間處理的大型資料集。 **缺點:** * 查詢次數增加,速度可能較慢。 * 程式邏輯較複雜,需自行處理狀態保存。 ```php use App\Models\Post; Post::chunk(100, function ($posts) { foreach ($posts as $post) { echo $post->title . "\n"; } }); // SQL: select * from posts order by id asc limit 100 offset 0 / 100 / 200 ``` ### chunkById() ```php use App\Models\Order; Order::where('status', '>', 0)->chunkById(500, function ($orders) { foreach ($orders as $order) { // 處理每筆訂單 } }, 'order_id'); // 避免預設 order by id 導致索引失效 ``` **適用時機:** * 資料需讀取後更新再寫回資料庫。 * 可避免使用 offset 導致的效率低落與資料遺漏問題。 **注意事項:** * 使用 chunkById 時若未指定欄位,預設使用主鍵排序,可能導致索引失效。 * 使用 chunkById 時 **請勿使用排序(orderBy)**,以免影響邏輯正確性。 --- ## 二、使用 `cursor()` 減少記憶體使用 ```php foreach (User::where('active', true)->cursor() as $user) { echo $user->name . "\n"; } ``` **優點:** * 每次僅讀取一筆資料,顯著降低記憶體消耗。 **缺點:** * 不能直接更新資料(資料僅於迭代期間存於記憶體)。 * 每次迭代都會進行資料庫讀取,速度可能較慢。 --- ## 三、只查需要的欄位:使用 `select()` 替代 `all()` ```php User::select('id', 'name')->get(); ``` **優點:** * 減少資料傳輸與記憶體使用。 * 提高查詢效率,尤其適用於大表。 --- ## 四、避免使用 `toArray()` 若需保留模型功能 ```php $collection = User::all(); $array = $collection->toArray(); // 失去模型功能 ``` **建議:** 除非要進行 JSON 序列化或與外部介面整合,否則保留 Collection 結構可使用更多模型相關操作與方法。 --- ## 五、使用 `pluck()` 只取得必要欄位值 ```php User::where('age', 30)->pluck('age', 'name'); // 回傳格式: ['Alice' => 30, 'Carol' => 30] ``` **優點:** * 僅回傳特定欄位,大幅減少資料量與查詢負擔。 * 寫法簡潔、意圖明確。 --- ## 六、使用 `with()` 避免 N+1 查詢問題 ```php // 不推薦:N+1 問題 $users = User::all(); foreach ($users as $user) { $user->posts; // 每次觸發額外查詢 } // 推薦:使用 with() 預加載 $users = User::with('posts')->get(); foreach ($users as $user) { foreach ($user->posts as $post) { echo "User: {$user->name}, Post: {$post->title}\n"; } } ``` --- ## 七、使用 `paginate()` 分頁查詢 ```php $users = User::paginate(10); foreach ($users as $user) { echo $user->name . "\n"; } echo $users->links(); // 顯示分頁導航 ``` **進階用法:** ```php $users = User::paginate(10, ['id', 'name'], 'page', 2); ``` **與 `chunk()` 差異:** * `paginate()` 用於前端分頁顯示。 * `chunk()` 用於後端批次處理大量資料,不提供分頁導覽。 --- ## 八、提升記憶體效能與資料讀取速度 ### 1. 使用緩存(Cache) * 使用 Redis 或 Memcached 儲存頻繁查詢的資料。 * Laravel Cache 支援資料快取、設定快取等多種應用。 ### 2. 使用資料庫索引(Index) * 對常查詢欄位加索引可顯著提升查詢速度。 * 注意:過多索引會增加寫入成本與記憶體佔用。 ### 3. 資料分區(Partitioning) * **水平分區**:依時間、區域、用戶等維度分表。 * **垂直分區**:將常用欄位與次要欄位拆分至不同表。 ### 4. 延遲與預加載的取捨 * **Lazy Loading**:初始不載入關聯資料,節省記憶體。 * **Eager Loading**:事先載入資料,減少資料庫查詢次數。 ```php // Lazy $users = User::all(); foreach ($users as $user) { $user->posts; } // Eager $users = User::with('posts')->get(); ``` ### 5. 查詢優化與分析 * 使用 `EXPLAIN` 分析 SQL 查詢計畫。 * 使用適當的索引、避免過多 join 與子查詢。 ### 6. 後台任務處理(Queue / Jobs) * 大量資料轉換或寫入建議放至 Laravel 任務隊列中執行。 * 可降低即時應用的記憶體與 CPU 壓力。 --- **備註:** 效能優化無銀彈,應根據應用場景進行監控與漸進調整。推薦搭配如 Laravel Telescope、Debugbar、New Relic 等工具分析瓶頸來源。