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