跳到內容

Redis

簡介

Redis 是一個開放原始碼、進階的鍵值儲存。它通常被稱為資料結構伺服器,因為鍵可以包含 字串雜湊列表集合排序集合

在使用 Laravel 的 Redis 之前,我們建議您透過 PECL 安裝並使用 PhpRedis PHP 擴充套件。相較於「使用者空間」PHP 套件,此擴充套件安裝更複雜,但對於大量使用 Redis 的應用程式,可能會產生更好的效能。如果您使用 Laravel Sail,則此擴充套件已安裝在您應用程式的 Docker 容器中。

如果您無法安裝 PhpRedis 擴充套件,您可以透過 Composer 安裝 predis/predis 套件。Predis 是一個完全以 PHP 編寫的 Redis 客戶端,不需要任何額外的擴充套件

1composer require predis/predis:^2.0

設定

您可以透過 config/database.php 設定檔設定應用程式的 Redis 設定。在此檔案中,您會看到一個 redis 陣列,其中包含您的應用程式使用的 Redis 伺服器

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'phpredis'),
4 
5 'options' => [
6 'cluster' => env('REDIS_CLUSTER', 'redis'),
7 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
8 ],
9 
10 'default' => [
11 'url' => env('REDIS_URL'),
12 'host' => env('REDIS_HOST', '127.0.0.1'),
13 'username' => env('REDIS_USERNAME'),
14 'password' => env('REDIS_PASSWORD'),
15 'port' => env('REDIS_PORT', '6379'),
16 'database' => env('REDIS_DB', '0'),
17 ],
18 
19 'cache' => [
20 'url' => env('REDIS_URL'),
21 'host' => env('REDIS_HOST', '127.0.0.1'),
22 'username' => env('REDIS_USERNAME'),
23 'password' => env('REDIS_PASSWORD'),
24 'port' => env('REDIS_PORT', '6379'),
25 'database' => env('REDIS_CACHE_DB', '1'),
26 ],
27 
28],

您在設定檔中定義的每個 Redis 伺服器都必須有一個名稱、主機和埠號,除非您定義單一 URL 來表示 Redis 連線

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'phpredis'),
4 
5 'options' => [
6 'cluster' => env('REDIS_CLUSTER', 'redis'),
7 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
8 ],
9 
10 'default' => [
11 'url' => 'tcp://127.0.0.1:6379?database=0',
12 ],
13 
14 'cache' => [
15 'url' => 'tls://user:[email protected]:6380?database=1',
16 ],
17 
18],

設定連線方案

預設情況下,Redis 客戶端在連線到您的 Redis 伺服器時將使用 tcp 方案;但是,您可以透過在 Redis 伺服器的設定陣列中指定 scheme 設定選項來使用 TLS / SSL 加密

1'default' => [
2 'scheme' => 'tls',
3 'url' => env('REDIS_URL'),
4 'host' => env('REDIS_HOST', '127.0.0.1'),
5 'username' => env('REDIS_USERNAME'),
6 'password' => env('REDIS_PASSWORD'),
7 'port' => env('REDIS_PORT', '6379'),
8 'database' => env('REDIS_DB', '0'),
9],

叢集

如果您的應用程式使用 Redis 伺服器叢集,您應該在 Redis 設定的 clusters 鍵中定義這些叢集。此設定鍵預設不存在,因此您需要在應用程式的 config/database.php 設定檔中建立它

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'phpredis'),
4 
5 'options' => [
6 'cluster' => env('REDIS_CLUSTER', 'redis'),
7 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
8 ],
9 
10 'clusters' => [
11 'default' => [
12 [
13 'url' => env('REDIS_URL'),
14 'host' => env('REDIS_HOST', '127.0.0.1'),
15 'username' => env('REDIS_USERNAME'),
16 'password' => env('REDIS_PASSWORD'),
17 'port' => env('REDIS_PORT', '6379'),
18 'database' => env('REDIS_DB', '0'),
19 ],
20 ],
21 ],
22 
23 // ...
24],

預設情況下,Laravel 將使用原生 Redis 叢集,因為 options.cluster 設定值設定為 redis。Redis 叢集是一個很好的預設選項,因為它可以優雅地處理故障轉移。

Laravel 在使用 Predis 時也支援用戶端分片。但是,用戶端分片不處理故障轉移;因此,它主要適用於可從另一個主要資料儲存區取得的暫時快取資料。

如果您想使用用戶端分片而不是原生 Redis 叢集,您可以移除應用程式 config/database.php 設定檔中的 options.cluster 設定值

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'phpredis'),
4 
5 'clusters' => [
6 // ...
7 ],
8 
9 // ...
10],

Predis

如果您希望您的應用程式透過 Predis 套件與 Redis 互動,您應確保 REDIS_CLIENT 環境變數的值為 predis

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'predis'),
4 
5 // ...
6],

除了預設設定選項外,Predis 還支援其他 連線參數,可以為您的每個 Redis 伺服器定義這些參數。若要使用這些額外的設定選項,請將它們新增到應用程式 config/database.php 設定檔中的 Redis 伺服器設定中

1'default' => [
2 'url' => env('REDIS_URL'),
3 'host' => env('REDIS_HOST', '127.0.0.1'),
4 'username' => env('REDIS_USERNAME'),
5 'password' => env('REDIS_PASSWORD'),
6 'port' => env('REDIS_PORT', '6379'),
7 'database' => env('REDIS_DB', '0'),
8 'read_write_timeout' => 60,
9],

PhpRedis

預設情況下,Laravel 將使用 PhpRedis 擴充套件與 Redis 通訊。Laravel 將用來與 Redis 通訊的客戶端由 redis.client 設定選項的值決定,該值通常反映 REDIS_CLIENT 環境變數的值

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'phpredis'),
4 
5 // ...
6],

除了預設設定選項外,PhpRedis 還支援以下額外的連線參數:namepersistentpersistent_idprefixread_timeoutretry_intervalmax_retriesbackoff_algorithmbackoff_basebackoff_captimeoutcontext。您可以將任何這些選項新增到 config/database.php 設定檔中的 Redis 伺服器設定中

1'default' => [
2 'url' => env('REDIS_URL'),
3 'host' => env('REDIS_HOST', '127.0.0.1'),
4 'username' => env('REDIS_USERNAME'),
5 'password' => env('REDIS_PASSWORD'),
6 'port' => env('REDIS_PORT', '6379'),
7 'database' => env('REDIS_DB', '0'),
8 'read_timeout' => 60,
9 'context' => [
10 // 'auth' => ['username', 'secret'],
11 // 'stream' => ['verify_peer' => false],
12 ],
13],

PhpRedis 序列化和壓縮

PhpRedis 擴充套件也可以設定為使用各種序列化器和壓縮演算法。這些演算法可以透過 Redis 設定的 options 陣列進行設定

1'redis' => [
2 
3 'client' => env('REDIS_CLIENT', 'phpredis'),
4 
5 'options' => [
6 'cluster' => env('REDIS_CLUSTER', 'redis'),
7 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
8 'serializer' => Redis::SERIALIZER_MSGPACK,
9 'compression' => Redis::COMPRESSION_LZ4,
10 ],
11 
12 // ...
13],

目前支援的序列化器包括:Redis::SERIALIZER_NONE(預設)、Redis::SERIALIZER_PHPRedis::SERIALIZER_JSONRedis::SERIALIZER_IGBINARYRedis::SERIALIZER_MSGPACK

支援的壓縮演算法包括:Redis::COMPRESSION_NONE(預設)、Redis::COMPRESSION_LZFRedis::COMPRESSION_ZSTDRedis::COMPRESSION_LZ4

與 Redis 互動

您可以透過在 Redis facade 上呼叫各種方法來與 Redis 互動。Redis facade 支援動態方法,這表示您可以在 facade 上呼叫任何 Redis 命令,並且該命令將直接傳遞到 Redis。在此範例中,我們將透過在 Redis facade 上呼叫 get 方法來呼叫 Redis GET 命令

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Support\Facades\Redis;
7use Illuminate\View\View;
8 
9class UserController extends Controller
10{
11 /**
12 * Show the profile for the given user.
13 */
14 public function show(string $id): View
15 {
16 return view('user.profile', [
17 'user' => Redis::get('user:profile:'.$id)
18 ]);
19 }
20}

如上所述,您可以在 Redis facade 上呼叫任何 Redis 命令。Laravel 使用魔術方法將命令傳遞到 Redis 伺服器。如果 Redis 命令需要引數,您應該將這些引數傳遞給 facade 的對應方法

1use Illuminate\Support\Facades\Redis;
2 
3Redis::set('name', 'Taylor');
4 
5$values = Redis::lrange('names', 5, 10);

或者,您可以使用 Redis facade 的 command 方法將命令傳遞到伺服器,該方法接受命令名稱作為其第一個引數,並接受值陣列作為其第二個引數

1$values = Redis::command('lrange', ['name', 5, 10]);

使用多個 Redis 連線

應用程式的 config/database.php 設定檔允許您定義多個 Redis 連線 / 伺服器。您可以使用 Redis facade 的 connection 方法取得特定 Redis 連線的連線

1$redis = Redis::connection('connection-name');

若要取得預設 Redis 連線的實例,您可以呼叫不帶任何額外引數的 connection 方法

1$redis = Redis::connection();

交易

Redis facade 的 transaction 方法為 Redis 的原生 MULTIEXEC 命令提供了一個方便的包裝器。transaction 方法接受一個閉包作為其唯一引數。此閉包將接收一個 Redis 連線實例,並且可以向該實例發出它想要的任何命令。閉包內發出的所有 Redis 命令都將在單個原子交易中執行

1use Redis;
2use Illuminate\Support\Facades;
3 
4Facades\Redis::transaction(function (Redis $redis) {
5 $redis->incr('user_visits', 1);
6 $redis->incr('total_visits', 1);
7});

在定義 Redis 交易時,您可能無法從 Redis 連線檢索任何值。請記住,您的交易是作為單個原子操作執行的,並且該操作在您的整個閉包完成執行其命令之前不會執行。

Lua 腳本

eval 方法提供了另一種在單個原子操作中執行多個 Redis 命令的方法。但是,eval 方法的好處是能夠在該操作期間與 Redis 鍵值互動和檢查它們。Redis 腳本以 Lua 程式語言 編寫。

eval 方法乍看之下可能有點嚇人,但我們將探索一個基本範例來打破僵局。eval 方法需要多個引數。首先,您應該將 Lua 腳本(作為字串)傳遞給該方法。其次,您應該傳遞腳本與之互動的鍵的數量(作為整數)。第三,您應該傳遞這些鍵的名稱。最後,您可以傳遞您需要在腳本中存取的任何其他額外引數。

在此範例中,我們將遞增一個計數器,檢查其新值,如果第一個計數器的值大於五,則遞增第二個計數器。最後,我們將傳回第一個計數器的值

1$value = Redis::eval(<<<'LUA'
2 local counter = redis.call("incr", KEYS[1])
3 
4 if counter > 5 then
5 redis.call("incr", KEYS[2])
6 end
7 
8 return counter
9LUA, 2, 'first-counter', 'second-counter');

有關 Redis 腳本的更多資訊,請參閱 Redis 文件

管線化命令

有時您可能需要執行數十個 Redis 命令。您可以改用 pipeline 方法,而不是為每個命令向 Redis 伺服器發出網路行程。pipeline 方法接受一個引數:接收 Redis 實例的閉包。您可以向此 Redis 實例發出所有命令,它們將同時傳送到 Redis 伺服器,以減少到伺服器的網路行程。命令仍將按照發出的順序執行

1use Redis;
2use Illuminate\Support\Facades;
3 
4Facades\Redis::pipeline(function (Redis $pipe) {
5 for ($i = 0; $i < 1000; $i++) {
6 $pipe->set("key:$i", $i);
7 }
8});

Pub / Sub

Laravel 為 Redis publishsubscribe 命令提供了一個方便的介面。這些 Redis 命令允許您監聽給定「頻道」上的訊息。您可以從另一個應用程式發佈訊息到頻道,甚至可以使用另一種程式語言,從而實現應用程式和進程之間的輕鬆通訊。

首先,讓我們使用 subscribe 方法設定頻道監聽器。由於呼叫 subscribe 方法會啟動長時間執行的進程,因此我們將此方法呼叫放在 Artisan 命令

1<?php
2 
3namespace App\Console\Commands;
4 
5use Illuminate\Console\Command;
6use Illuminate\Support\Facades\Redis;
7 
8class RedisSubscribe extends Command
9{
10 /**
11 * The name and signature of the console command.
12 *
13 * @var string
14 */
15 protected $signature = 'redis:subscribe';
16 
17 /**
18 * The console command description.
19 *
20 * @var string
21 */
22 protected $description = 'Subscribe to a Redis channel';
23 
24 /**
25 * Execute the console command.
26 */
27 public function handle(): void
28 {
29 Redis::subscribe(['test-channel'], function (string $message) {
30 echo $message;
31 });
32 }
33}

現在我們可以使用 publish 方法將訊息發佈到頻道

1use Illuminate\Support\Facades\Redis;
2 
3Route::get('/publish', function () {
4 // ...
5 
6 Redis::publish('test-channel', json_encode([
7 'name' => 'Adam Wathan'
8 ]));
9});

萬用字元訂閱

使用 psubscribe 方法,您可以訂閱萬用字元頻道,這對於捕獲所有頻道上的所有訊息可能很有用。頻道名稱將作為第二個引數傳遞到提供的閉包

1Redis::psubscribe(['*'], function (string $message, string $channel) {
2 echo $message;
3});
4 
5Redis::psubscribe(['users.*'], function (string $message, string $channel) {
6 echo $message;
7});

Laravel 是最有效率的方式來
建構、部署和監控軟體。