> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Redis

> 在 Laravel 應用程式中使用 Redis 進行快取、Session、Queue 與 Pub/Sub 的設定與操作方法

Redis 是記憶體式的 key-value store，也是 Laravel 眾多功能的後端。
本文從設定到操作、Pub/Sub 為止，解說如何在 Laravel 中使用 Redis。

<CardGroup cols={3}>
  <Card title="快取" icon="database" href="/zh-TW/cache">
    將 Redis 作為快取 driver 使用
  </Card>

  <Card title="Queue" icon="list" href="/zh-TW/queues">
    將 Redis 作為 queue driver 使用
  </Card>

  <Card title="Broadcasting" icon="radio" href="/zh-TW/broadcasting">
    以 Pub/Sub 進行即時通訊
  </Card>
</CardGroup>

## 什麼是 Redis

[Redis](https://redis.io) 是開源的高速 key-value store。
支援字串、hash、list、set、sorted set 等多樣資料結構，也被稱為資料結構伺服器。

Laravel 中會將 Redis 用於下列用途：

```mermaid theme={null}
flowchart LR
    A["Laravel<br>應用程式"] --> B["Redis"]

    subgraph uses ["主要用途"]
        C["快取<br>Cache"]
        D["Session"]
        E["Queue"]
        F["Broadcasting"]
    end

    B --> C
    B --> D
    B --> E
    B --> F
```

## 選擇 Client

Laravel 支援 **PhpRedis**（PHP extension）與 **Predis**（PHP 套件）兩種 client。

| 項目           | PhpRedis                | Predis                  |
| ------------ | ----------------------- | ----------------------- |
| 實作           | 以 C 語言撰寫的 PHP extension | 純 PHP 套件                |
| 安裝           | 需要 PECL extension       | 以 `composer require` 完成 |
| 效能           | 高速                      | 稍慢於 PhpRedis            |
| Laravel Sail | 預設已安裝                   | 需另外安裝                   |
| 推薦環境         | 正式環境                    | 開發環境／不易安裝的環境            |

<Info>
  Laravel 13 的預設 client 為 PhpRedis。正式環境建議使用 PhpRedis。
  使用 Laravel Sail 時已經安裝好 PhpRedis。
</Info>

## 設定

### config/database.php

Redis 的設定於 `config/database.php` 的 `redis` 陣列管理。

```php theme={null}
'redis' => [

    'client' => env('REDIS_CLIENT', 'phpredis'),

    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
    ],

    'default' => [
        'url' => env('REDIS_URL'),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'username' => env('REDIS_USERNAME'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_DB', '0'),
    ],

    'cache' => [
        'url' => env('REDIS_URL'),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'username' => env('REDIS_USERNAME'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_CACHE_DB', '1'),
    ],

],
```

### 環境變數

在 `.env` 中設定連線目標。

```ini theme={null}
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=null
REDIS_DB=0
REDIS_CACHE_DB=1
```

也可以用 URL 格式指定。

```php theme={null}
'default' => [
    'url' => 'tcp://127.0.0.1:6379?database=0',
],

'cache' => [
    'url' => 'tls://user:password@127.0.0.1:6380?database=1',
],
```

### TLS / SSL 連線

使用 TLS 加密時，可指定 `scheme` 選項。

```php theme={null}
'default' => [
    'scheme' => 'tls',
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
],
```

### Cluster

要以叢集使用多台 Redis 伺服器時，使用 `clusters` 鍵。

```php theme={null}
'redis' => [

    'client' => env('REDIS_CLIENT', 'phpredis'),

    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
    ],

    'clusters' => [
        'default' => [
            [
                'url' => env('REDIS_URL'),
                'host' => env('REDIS_HOST', '127.0.0.1'),
                'username' => env('REDIS_USERNAME'),
                'password' => env('REDIS_PASSWORD'),
                'port' => env('REDIS_PORT', '6379'),
                'database' => env('REDIS_DB', '0'),
            ],
        ],
    ],

],
```

預設 `options.cluster` 為 `redis`，會使用原生 Redis clustering。
自動處理 failover，是正式環境的建議設定。

若要用 Predis 使用 client-side sharding，可移除 `options.cluster`。
但 client-side sharding 不處理 failover，因此只適合快取等暫時性資料。

### Predis 的設定

要使用 Predis，安裝 `predis/predis` 套件並將 `REDIS_CLIENT` 改為 `predis`。

```shell theme={null}
composer require predis/predis
```

```ini theme={null}
REDIS_CLIENT=predis
```

可加入 Predis 特有的[連線參數](https://github.com/nrk/predis/wiki/Connection-Parameters)。

```php theme={null}
'default' => [
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
    'read_write_timeout' => 60,
],
```

#### Predis 的重試設定

Predis 3.4.0 以後可使用內建的重試與 backoff 設定。以 `max_retries` 選項設定重試次數，以 `retry` 選項設定 backoff 策略。`retry` 選項需以 `NoBackoff`、`EqualBackoff`、`ExponentialBackoff` 其中之一的類別名稱作為 key 的陣列指定。

```php theme={null}
use Predis\Retry\Strategy\ExponentialBackoff;

'default' => [
    'url' => env('REDIS_URL'),
    // ...
    'retry' => [
        ExponentialBackoff::class => [
            env('REDIS_BACKOFF_BASE', 100),
            env('REDIS_BACKOFF_CAP', 1000),
            true, // 啟用 jitter
        ],
    ],
    'max_retries' => env('REDIS_MAX_RETRIES', 3),
],
```

在 Redis cluster 使用 Predis 時，可於 cluster 設定的 `parameters` 選項設定重試。

```php theme={null}
use Predis\Retry\Strategy\NoBackoff;

'clusters' => [
    'default' => [
        // ...
    ],
],

'options' => [
    'cluster' => env('REDIS_CLUSTER', 'redis'),
    'parameters' => [
        'retry' => [
            NoBackoff::class => [],
        ],
        'max_retries' => env('REDIS_MAX_RETRIES', 3),
    ],
],
```

### PhpRedis 的設定

PhpRedis 透過 PECL 安裝（Laravel Sail 已內建）。
PhpRedis 支援以下額外選項：
`name`、`persistent`、`persistent_id`、`prefix`、`read_timeout`、`retry_interval`、
`max_retries`、`backoff_algorithm`、`backoff_base`、`backoff_cap`、`timeout`、`context`

```php theme={null}
'default' => [
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
    'read_timeout' => 60,
    'context' => [
        // 'auth' => ['username', 'secret'],
        // 'stream' => ['verify_peer' => false],
    ],
],
```

#### 重試與 Backoff 設定

設定連線失敗時的重試行為。
支援的 backoff 演算法：`default`、`decorrelated_jitter`、`equal_jitter`、`exponential`、`uniform`、`constant`

```php theme={null}
'default' => [
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
    'max_retries' => env('REDIS_MAX_RETRIES', 3),
    'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'),
    'backoff_base' => env('REDIS_BACKOFF_BASE', 100),
    'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000),
],
```

#### Unix socket 連線

連線同一台伺服器上的 Redis 時，使用 Unix socket 可降低 TCP overhead。

```ini theme={null}
REDIS_HOST=/run/redis/redis.sock
REDIS_PORT=0
```

#### 序列化與壓縮

PhpRedis 可設定所儲存資料的序列化與壓縮演算法。

```php theme={null}
'redis' => [

    'client' => env('REDIS_CLIENT', 'phpredis'),

    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        'serializer' => Redis::SERIALIZER_MSGPACK,
        'compression' => Redis::COMPRESSION_LZ4,
    ],

],
```

支援的序列化器：

| 常數                           | 說明          |
| ---------------------------- | ----------- |
| `Redis::SERIALIZER_NONE`     | 不序列化（預設）    |
| `Redis::SERIALIZER_PHP`      | PHP 序列化     |
| `Redis::SERIALIZER_JSON`     | JSON        |
| `Redis::SERIALIZER_IGBINARY` | igbinary    |
| `Redis::SERIALIZER_MSGPACK`  | MessagePack |

支援的壓縮演算法：

| 常數                        | 說明        |
| ------------------------- | --------- |
| `Redis::COMPRESSION_NONE` | 無壓縮（預設）   |
| `Redis::COMPRESSION_LZF`  | LZF       |
| `Redis::COMPRESSION_ZSTD` | Zstandard |
| `Redis::COMPRESSION_LZ4`  | LZ4       |

## 與 Redis 的互動

### Redis facade

可透過 `Redis` facade 執行所有 Redis 指令。
facade 會以 magic method 將指令轉發到 Redis 伺服器。

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Redis;
use Illuminate\View\View;

class UserController extends Controller
{
    public function show(string $id): View
    {
        return view('user.profile', [
            'user' => Redis::get('user:profile:'.$id)
        ]);
    }
}
```

接收引數的指令，直接以方法引數傳入即可。

```php theme={null}
use Illuminate\Support\Facades\Redis;

Redis::set('name', 'Taylor');

$values = Redis::lrange('names', 5, 10);
```

也可以用 `command` 方法明確指定指令名稱與引數。

```php theme={null}
$values = Redis::command('lrange', ['name', 5, 10]);
```

### 使用多個連線

可在 `config/database.php` 定義多個 Redis 連線，並以 `connection()` 方法切換。

```php theme={null}
// 取得具名連線
$redis = Redis::connection('connection-name');

// 取得預設連線
$redis = Redis::connection();
```

### 交易（Transaction）

`transaction()` 方法會包裝 Redis 的 `MULTI`/`EXEC` 指令。
closure 內的所有指令會被 atomic 地執行。

```php theme={null}
use Redis;
use Illuminate\Support\Facades;

Facades\Redis::transaction(function (Redis $redis) {
    $redis->incr('user_visits', 1);
    $redis->incr('total_visits', 1);
});
```

<Warning>
  交易中無法從 Redis 取值。
  因為整個 closure 會執行完再以 `EXEC` 一次執行。
</Warning>

### Lua 腳本

以 `eval()` 方法可以 atomic 地執行 Lua 腳本。
比交易更靈活，可在腳本中參照及更新 Redis 的值。

```php theme={null}
$value = Redis::eval(<<<'LUA'
    local counter = redis.call("incr", KEYS[1])

    if counter > 5 then
        redis.call("incr", KEYS[2])
    end

    return counter
LUA, 2, 'first-counter', 'second-counter');
```

引數順序：Lua 腳本 → key 數量 → key 名稱... → 追加引數...

`KEYS[1]`、`KEYS[2]` 為 key 名稱，`ARGV[1]` 起為追加引數。

### Pipeline

可一次傳送大量指令，減少網路來回。

```php theme={null}
use Redis;
use Illuminate\Support\Facades;

Facades\Redis::pipeline(function (Redis $pipe) {
    for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", $i);
    }
});
```

```mermaid theme={null}
sequenceDiagram
    participant App as Laravel<br>應用
    participant Redis

    Note over App,Redis: 一般（每個指令都往返）
    App->>Redis: SET key:0 0
    Redis-->>App: OK
    App->>Redis: SET key:1 1
    Redis-->>App: OK

    Note over App,Redis: Pipeline（合併傳送）
    App->>Redis: SET key:0 0 / SET key:1 1 / ...
    Redis-->>App: OK / OK / ...
```

<Tip>
  Pipeline 只是把指令合併傳送，並非 atomic。
  若需要 atomic 操作，請使用交易或 Lua 腳本。
</Tip>

## Pub / Sub

可透過 Redis 的 `publish` / `subscribe` 指令，經由 channel 收發訊息。

### Subscriber

`subscribe()` 是長時間運作的 process，應在 Artisan 指令內呼叫。

```php theme={null}
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;

class RedisSubscribe extends Command
{
    protected $signature = 'redis:subscribe';

    protected $description = 'Subscribe to a Redis channel';

    public function handle(): void
    {
        Redis::subscribe(['test-channel'], function (string $message) {
            echo $message;
        });
    }
}
```

### Publisher

從其他請求或 process 送出訊息。

```php theme={null}
use Illuminate\Support\Facades\Redis;

Route::get('/publish', function () {
    Redis::publish('test-channel', json_encode([
        'name' => 'Adam Wathan'
    ]));
});
```

### Wildcard 訂閱

`psubscribe()` 可進行樣式比對的訂閱。

```php theme={null}
// 訂閱所有 channel
Redis::psubscribe(['*'], function (string $message, string $channel) {
    echo $message;
});

// 訂閱 users.* 樣式的 channel
Redis::psubscribe(['users.*'], function (string $message, string $channel) {
    echo $message;
});
```

## 總結

<AccordionGroup>
  <Accordion title="Client 選擇">
    * **正式環境**：PhpRedis（PECL extension、高效能）
    * **開發環境／難以安裝時**：Predis（Composer 套件）
    * **Laravel Sail**：預設已安裝 PhpRedis
  </Accordion>

  <Accordion title="連線設定要點">
    * 在 `config/database.php` 定義多個 Redis 連線，依用途分開使用（如 `default` / `cache`）
    * 叢集環境使用 `clusters` 鍵
    * 正式環境考量 TLS 連線與認證資訊設定
  </Accordion>

  <Accordion title="操作的分工">
    | 操作                          | 用途                         |
    | --------------------------- | -------------------------- |
    | facade 方法                   | 一般的 Redis 指令               |
    | `transaction()`             | 多指令的 atomic 執行（無法讀取值）      |
    | `eval()`                    | Lua 腳本（伴隨讀取的 atomic 操作）    |
    | `pipeline()`                | 大量指令的高速傳送（非 atomic）        |
    | `subscribe()` / `publish()` | Channel messaging（Pub/Sub） |
  </Accordion>
</AccordionGroup>


## Related topics

- [Laravel Sail](/zh-TW/sail.md)
- [快取](/zh-TW/cache.md)
- [速率限制的自訂](/zh-TW/advanced/rate-limiting.md)
- [Queue 與 Job](/zh-TW/queues.md)
- [Laravel Pulse](/zh-TW/pulse.md)
