#はじめに
Redis はオープンソースの高度なキー・バリュー型ストアです。キーには strings、hashes、lists、sets、および sorted sets といったデータ構造を格納できるため、データ構造サーバーと呼ばれることもあります。
LaravelでRedisを使う前に、PECL経由で PhpRedis PHP拡張をインストールして利用することを推奨します。この拡張は「ユーザーランド」のPHPパッケージよりインストールがやや複雑ですが、Redisを多用するアプリケーションではより高いパフォーマンスが期待できます。Laravel Sail を使っている場合は、この拡張がすでにアプリケーションのDockerコンテナにインストールされています。
PhpRedis拡張をインストールできない場合は、Composer経由で predis/predis パッケージをインストールできます。Predisは完全にPHPで書かれたRedisクライアントで、追加の拡張は不要です:
composer require predis/predis
#設定
アプリケーションのRedis設定は config/database.php 設定ファイルで行えます。このファイル内には、アプリケーションで使用するRedisサーバーを含む redis 配列があります:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
'cache' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_CACHE_DB', 1),
],
],
設定ファイルで定義する各Redisサーバーは、名前、ホスト、ポートが必要です。ただし、Redis接続を表す単一のURLを定義する場合は例外です:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'default' => [
'url' => 'tcp://127.0.0.1:6379?database=0',
],
'cache' => [
'url' => 'tls://user:[email protected]:6380?database=1',
],
],
#接続スキームの設定
デフォルトでは、RedisクライアントはRedisサーバーに接続する際に tcp スキームを使用しますが、Redisサーバーの設定配列に scheme オプションを指定することでTLS / SSL暗号化を利用できます:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'default' => [
'scheme' => 'tls',
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => env('REDIS_DB', 0),
],
],
#クラスター
アプリケーションでRedisサーバークラスターを利用する場合は、Redis設定の clusters キーにこれらのクラスターを定義してください。この設定キーはデフォルトでは存在しないため、config/database.php に新たに作成する必要があります:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'clusters' => [
'default' => [
[
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
],
],
],
デフォルトでは、クラスターはクライアント側シャーディングをノード間で行い、ノードをプールして大量のRAMを利用可能にします。ただし、クライアント側シャーディングはフェイルオーバーを扱わないため、主に別のプライマリデータストアから利用可能な一時的なキャッシュデータに適しています。
ネイティブのRedisクラスタリングを使いたい場合は、config/database.php の options.cluster 設定値を redis に設定してください:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
],
'clusters' => [
// ...
],
],
#Predis
Predisパッケージを使ってRedisとやり取りしたい場合は、REDIS_CLIENT 環境変数の値を predis に設定してください:
'redis' => [
'client' => env('REDIS_CLIENT', 'predis'),
// ...
],
デフォルトの host、port、database、password に加え、Predisは各Redisサーバーに対して追加の接続パラメータをサポートします。これらの追加設定を使うには、config/database.php のRedisサーバー設定に追加してください:
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_write_timeout' => 60,
],
#Redisファサードエイリアス
Laravelの config/app.php 設定ファイルには、フレームワークが登録するクラスエイリアスを定義する aliases 配列があります。デフォルトでは、PhpRedis拡張の Redis クラス名と衝突するため Redis エイリアスは含まれていません。Predisクライアントを使っていて Redis エイリアスを追加したい場合は、config/app.php の aliases 配列に追加してください:
'aliases' => Facade::defaultAliases()->merge([
'Redis' => Illuminate\Support\Facades\Redis::class,
])->toArray(),
#PhpRedis
デフォルトでLaravelはPhpRedis拡張を使ってRedisと通信します。どのクライアントを使うかは通常、redis.client 設定オプションの値で決まり、これは REDIS_CLIENT 環境変数の値を反映します:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
// Redis設定の残り...
],
デフォルトの scheme、host、port、database、password に加え、PhpRedisは以下の追加接続パラメータをサポートします:name、persistent、persistent_id、prefix、read_timeout、retry_interval、timeout、context。これらは config/database.php のRedisサーバー設定に追加できます:
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'read_timeout' => 60,
'context' => [
// 'auth' => ['username', 'secret'],
// 'stream' => ['verify_peer' => false],
],
],
#PhpRedisのシリアライズと圧縮
PhpRedis拡張は様々なシリアライザと圧縮アルゴリズムを設定できます。これらはRedis設定の options 配列で設定可能です:
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'serializer' => Redis::SERIALIZER_MSGPACK,
'compression' => Redis::COMPRESSION_LZ4,
],
// Redis設定の残り...
],
現在サポートされているシリアライザは、Redis::SERIALIZER_NONE(デフォルト)、Redis::SERIALIZER_PHP、Redis::SERIALIZER_JSON、Redis::SERIALIZER_IGBINARY、Redis::SERIALIZER_MSGPACK です。
サポートされている圧縮アルゴリズムは、Redis::COMPRESSION_NONE(デフォルト)、Redis::COMPRESSION_LZF、Redis::COMPRESSION_ZSTD、Redis::COMPRESSION_LZ4 です。
#Redisとのやり取り
Redis ファサードの様々なメソッドを呼び出すことでRedisとやり取りできます。Redis ファサードは動的メソッドをサポートしており、任意の Redisコマンド をファサードで呼び出すと、そのコマンドが直接Redisに送られます。以下の例では、Redis ファサードの get メソッドを呼び出してRedisの GET コマンドを実行しています:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
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)
]);
}
}
前述の通り、Redis ファサードで任意のRedisコマンドを呼び出せます。Laravelはマジックメソッドを使ってコマンドをRedisサーバーに渡します。Redisコマンドが引数を必要とする場合は、対応するファサードのメソッドに引数を渡してください:
use Illuminate\Support\Facades\Redis;
Redis::set('name', 'Taylor');
$values = Redis::lrange('names', 5, 10);
または、Redis ファサードの command メソッドを使ってコマンド名を第1引数に、値の配列を第2引数に渡すこともできます:
$values = Redis::command('lrange', ['name', 5, 10]);
#複数のRedis接続を使う
config/database.php で複数のRedis接続/サーバーを定義できます。特定のRedis接続を取得するには、Redis ファサードの connection メソッドに接続名を渡します:
$redis = Redis::connection('connection-name');
デフォルトのRedis接続を取得するには、引数なしで connection メソッドを呼び出します:
$redis = Redis::connection();
#トランザクション
Redis ファサードの transaction メソッドは、Redisのネイティブな MULTI と EXEC コマンドをラップする便利なメソッドです。transaction はクロージャを唯一の引数に取り、このクロージャはRedis接続インスタンスを受け取り、任意のコマンドを発行できます。クロージャ内で発行されたすべてのRedisコマンドは単一の原子トランザクションとして実行されます:
use Redis;
use Illuminate\Support\Facades;
Facades\Redis::transaction(function (Redis $redis) {
$redis->incr('user_visits', 1);
$redis->incr('total_visits', 1);
});
Redisトランザクションを定義する際、Redis接続から値を取得することはできません。トランザクションは単一の原子操作として実行され、クロージャ内のすべてのコマンドが完了するまで実行されないことを覚えておいてください。
#Luaスクリプト
eval メソッドは複数のRedisコマンドを単一の原子操作で実行する別の方法です。さらに、eval は操作中にRedisのキーの値を操作・検査できる利点があります。Redisスクリプトは Luaプログラミング言語 で書かれます。
eval メソッドは最初は難しく感じるかもしれませんが、基本的な例で解説します。eval は複数の引数を取ります。まずLuaスクリプト(文字列)を渡します。次にスクリプトが操作するキーの数(整数)を渡します。続いてキー名を渡します。最後にスクリプト内で使う追加の引数を渡せます。
この例では、カウンターをインクリメントし、その新しい値を検査し、最初のカウンターの値が5より大きければ2番目のカウンターをインクリメントします。最後に最初のカウンターの値を返します:
$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');
Redisスクリプトの詳細は Redisドキュメント を参照してください。
#コマンドのパイプライン処理
大量のRedisコマンドを実行する必要がある場合、各コマンドごとにRedisサーバーへネットワーク往復する代わりに、pipeline メソッドを使えます。pipeline は1つの引数としてRedisインスタンスを受け取るクロージャを取り、このインスタンスに対してすべてのコマンドを発行できます。これらのコマンドはまとめてRedisサーバーに送信され、ネットワーク往復を減らせます。コマンドは発行順に実行されます:
use Redis;
use Illuminate\Support\Facades;
Facades\Redis::pipeline(function (Redis $pipe) {
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", $i);
}
});
#Pub/Sub(公開/購読)
LaravelはRedisの publish と subscribe コマンドに便利なインターフェイスを提供します。これらのRedisコマンドは指定した「チャネル」のメッセージをリッスンできます。別のアプリケーションや別のプログラミング言語からチャネルにメッセージを公開でき、アプリケーションやプロセス間の通信が容易になります。
まず、subscribe メソッドを使ってチャネルリスナーを設定しましょう。subscribe は長時間実行されるため、Artisanコマンド内で呼び出します:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class RedisSubscribe extends Command
{
/**
* コンソールコマンドの名前と署名。
*
* @var string
*/
protected $signature = 'redis:subscribe';
/**
* コンソールコマンドの説明。
*
* @var string
*/
protected $description = 'Redisチャネルにサブスクライブします';
/**
* コンソールコマンドを実行します。
*/
public function handle(): void
{
Redis::subscribe(['test-channel'], function (string $message) {
echo $message;
});
}
}
これで publish メソッドを使ってチャネルにメッセージを公開できます:
use Illuminate\Support\Facades\Redis;
Route::get('/publish', function () {
// ...
Redis::publish('test-channel', json_encode([
'name' => 'Adam Wathan'
]));
});
#ワイルドカードサブスクリプション
psubscribe メソッドを使うとワイルドカードチャネルにサブスクライブでき、すべてのチャネルのメッセージをキャッチするのに便利です。チャネル名は提供したクロージャの第2引数として渡されます:
Redis::psubscribe(['*'], function (string $message, string $channel) {
echo $message;
});
Redis::psubscribe(['users.*'], function (string $message, string $channel) {
echo $message;
});