コンテンツへスキップ

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

設定

アプリケーションのRedis設定は、config/database.php設定ファイルで行います。このファイル内に、アプリケーションが利用する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],

options.cluster設定値がredisに設定されているため、デフォルトでLaravelはネイティブの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ファサードでさまざまなメソッドを呼び出すことにより、Redisを操作できます。Redisファサードは動的メソッドをサポートしています。つまり、ファサードで任意のRedisコマンドを呼び出すことができ、コマンドはRedisに直接渡されます。この例では、Redisファサードで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ファサードでRedisの任意のコマンドを呼び出すことができます。Laravelは、マジックメソッドを使用してコマンドをRedisサーバに渡します。Redisコマンドが引数を期待する場合は、ファサードの対応するメソッドにそれらを渡す必要があります。

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

あるいは、Redisファサードのcommandメソッドを使用してコマンドをサーバに渡すこともできます。このメソッドは、最初の引数としてコマンドの名前を、2番目の引数として値の配列を受け入れます。

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

複数のRedis接続の使用

アプリケーションのconfig/database.php設定ファイルで、複数のRedis接続/サーバを定義できます。Redisファサードのconnectionメソッドを使用して、特定のRedis接続への接続を取得できます。

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

デフォルトのRedis接続のインスタンスを取得するには、追加の引数なしでconnectionメソッドを呼び出してください。

1$redis = Redis::connection();

トランザクション

Redisファサードのtransactionメソッドは、RedisのネイティブのMULTIコマンドとEXECコマンドの便利なラッパーを提供します。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コマンドを単一のアトミックな操作で実行するもう1つの方法を提供します。ただし、evalメソッドには、その操作中にRedisキーの値を操作および検査できるという利点があります。Redisスクリプトは、Luaプログラミング言語で記述します。

evalメソッドは最初は少し怖いかもしれませんが、基本的な例で氷を砕いてみましょう。evalメソッドはいくつかの引数を期待します。まず、Luaスクリプト(文字列として)をメソッドに渡します。次に、スクリプトが操作するキーの数(整数として)を渡します。第3に、それらのキーの名前を渡します。最後に、スクリプト内でアクセスする必要のあるその他の追加の引数を渡すことができます。

この例では、カウンタをインクリメントし、その新しい値を検査し、最初のカウンタの値が5より大きい場合は2番目のカウンタをインクリメントします。最後に、最初のカウンタの値を返します。

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コマンドを実行する必要がある場合があります。コマンドごとにRedisサーバへのネットワークトリップを行う代わりに、pipelineメソッドを使用できます。pipelineメソッドは、Redisインスタンスを受け取るクロージャを1つ引数に取ります。この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のpublishコマンドとsubscribeコマンドへの便利なインターフェイスを提供しています。これらの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メソッドを使用すると、ワイルドカードチャネルをサブスクライブできます。これは、すべてのチャネルのすべてのメッセージをキャッチするのに役立ちます。チャネル名は、指定するクロージャの2番目の引数として渡されます。

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は最も生産的な方法です
ソフトウェアを構築、デプロイ、監視します。