---
# System prepended metadata

title: Laravel 效能優化筆記
tags: [laravel]

---

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