# 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 等工具分析瓶頸來源。