キャッシュ
はじめに
アプリケーションが実行するデータ取得または処理タスクの中には、CPUを大量に消費したり、完了までに数秒かかるものがあります。このような場合、取得したデータを一定時間キャッシュしておけば、同じデータに対する後続のリクエストを迅速に処理できます。キャッシュされたデータは、通常、Memcached や Redis などの非常に高速なデータストアに保存されます。
ありがたいことに、Laravelは様々なキャッシュバックエンドに対して表現力豊かな統一されたAPIを提供しており、それらの超高速なデータ取得を活用してWebアプリケーションの速度を向上させることができます。
設定
アプリケーションのキャッシュ設定ファイルは、config/cache.php
にあります。このファイルでは、アプリケーション全体で使用するデフォルトのキャッシュストアを指定できます。Laravelは、Memcached、Redis、DynamoDB、リレーショナルデータベースなどの一般的なキャッシュバックエンドをすぐに使用できます。さらに、ファイルベースのキャッシュドライバも利用でき、array
と "null" キャッシュドライバは、自動化されたテストに便利なキャッシュバックエンドを提供します。
キャッシュ設定ファイルには、他にも様々なオプションが含まれています。デフォルトでは、Laravelはアプリケーションのデータベースにシリアライズされたキャッシュオブジェクトを保存するdatabase
キャッシュドライバを使用するように設定されています。
ドライバの前提条件
データベース
database
キャッシュドライバを使用する場合は、キャッシュデータを格納するためのデータベーステーブルが必要です。通常、これはLaravelのデフォルトの0001_01_01_000001_create_cache_table.php
データベースマイグレーション に含まれています。ただし、アプリケーションにこのマイグレーションが含まれていない場合は、make:cache-table
Artisanコマンドを使用して作成できます。
php artisan make:cache-table php artisan migrate
Memcached
Memcachedドライバを使用するには、Memcached PECLパッケージ をインストールする必要があります。すべてのMemcachedサーバーをconfig/cache.php
設定ファイルにリストできます。このファイルには、開始するためのmemcached.servers
エントリが既に含まれています。
'memcached' => [ // ... 'servers' => [ [ 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 'port' => env('MEMCACHED_PORT', 11211), 'weight' => 100, ], ],],
必要に応じて、host
オプションをUNIXソケットパスに設定できます。この場合、port
オプションは0
に設定する必要があります。
'memcached' => [ // ... 'servers' => [ [ 'host' => '/var/run/memcached/memcached.sock', 'port' => 0, 'weight' => 100 ], ],],
Redis
LaravelでRedisキャッシュを使用する前に、PECL経由でPhpRedis PHPエクステンションをインストールするか、Composer経由でpredis/predis
パッケージ(〜2.0)をインストールする必要があります。Laravel Sail には既にこのエクステンションが含まれています。さらに、Laravel Forge や Laravel Vapor などの公式Laravelデプロイメントプラットフォームには、PhpRedisエクステンションがデフォルトでインストールされています。
Redisの設定の詳細については、Laravelのドキュメントページ を参照してください。
DynamoDB
DynamoDB キャッシュドライバを使用する前に、キャッシュされたすべてのデータを格納するためのDynamoDBテーブルを作成する必要があります。通常、このテーブルの名前はcache
です。ただし、cache
設定ファイル内のstores.dynamodb.table
設定値に基づいてテーブルの名前を付ける必要があります。テーブル名は、DYNAMODB_CACHE_TABLE
環境変数でも設定できます。
このテーブルには、アプリケーションのcache
設定ファイル内のstores.dynamodb.attributes.key
設定項目の値に対応する名前の文字列パーティションキーも含まれている必要があります。デフォルトでは、パーティションキーの名前はkey
である必要があります。
通常、DynamoDBは期限切れのアイテムをテーブルからプロアクティブに削除しません。そのため、テーブルでTime to Live (TTL) を有効にする必要があります。テーブルのTTL設定を構成する際には、TTL属性名をexpires_at
に設定する必要があります。
次に、LaravelアプリケーションがDynamoDBと通信できるように、AWS SDKをインストールします。
composer require aws/aws-sdk-php
さらに、DynamoDBキャッシュストアの設定オプションに値が指定されていることを確認する必要があります。通常、AWS_ACCESS_KEY_ID
や AWS_SECRET_ACCESS_KEY
などのこれらのオプションは、アプリケーションの.env
設定ファイルで定義する必要があります。
'dynamodb' => [ 'driver' => 'dynamodb', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), 'endpoint' => env('DYNAMODB_ENDPOINT'),],
MongoDB
MongoDBを使用している場合、公式のmongodb/laravel-mongodb
パッケージによってmongodb
キャッシュドライバが提供され、mongodb
データベース接続を使用して構成できます。MongoDBはTTLインデックスをサポートしており、これを使用して期限切れのキャッシュアイテムを自動的にクリアできます。
MongoDBの設定の詳細については、MongoDBのキャッシュとロックに関するドキュメント を参照してください。
キャッシュの使用
キャッシュインスタンスの取得
キャッシュストアインスタンスを取得するには、このドキュメント全体で使用する方法であるCache
ファサードを使用できます。Cache
ファサードは、Laravelキャッシュコントラクトの基盤となる実装への便利で簡潔なアクセスを提供します。
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; class UserController extends Controller{ /** * Show a list of all users of the application. */ public function index(): array { $value = Cache::get('key'); return [ // ... ]; }}
複数のキャッシュストアへのアクセス
Cache
ファサードを使用すると、store
メソッドを使用して、様々なキャッシュストアにアクセスできます。store
メソッドに渡されるキーは、cache
設定ファイルのstores
設定配列にリストされているストアのいずれかに対応している必要があります。
$value = Cache::store('file')->get('foo'); Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes
キャッシュからのアイテムの取得
Cache
ファサードのget
メソッドは、キャッシュからアイテムを取得するために使用されます。アイテムがキャッシュに存在しない場合、null
が返されます。必要に応じて、アイテムが存在しない場合に返すデフォルト値を指定する2番目の引数をget
メソッドに渡すことができます。
$value = Cache::get('key'); $value = Cache::get('key', 'default');
デフォルト値としてクロージャを渡すこともできます。指定したアイテムがキャッシュに存在しない場合、クロージャの結果が返されます。クロージャを渡すと、データベースやその他の外部サービスからのデフォルト値の取得を遅らせることができます。
$value = Cache::get('key', function () { return DB::table(/* ... */)->get();});
アイテムの存在の確認
has
メソッドを使用して、アイテムがキャッシュに存在するかどうかを確認できます。このメソッドは、アイテムが存在するが値がnull
の場合にもfalse
を返します。
if (Cache::has('key')) { // ...}
値のインクリメント/デクリメント
increment
メソッドと decrement
メソッドは、キャッシュ内の整数アイテムの値を調整するために使用できます。これらのメソッドはどちらも、アイテムの値を増減する量を示すオプションの第2引数を受け付けます。
// Initialize the value if it does not exist...Cache::add('key', 0, now()->addHours(4)); // Increment or decrement the value...Cache::increment('key');Cache::increment('key', $amount);Cache::decrement('key');Cache::decrement('key', $amount);
取得と保存
キャッシュからアイテムを取得する際に、要求されたアイテムが存在しない場合はデフォルト値を保存することもできます。たとえば、キャッシュからすべてのユーザーを取得するか、存在しない場合はデータベースから取得してキャッシュに追加することができます。これは、Cache::remember
メソッドを使用して行うことができます。
$value = Cache::remember('users', $seconds, function () { return DB::table('users')->get();});
アイテムがキャッシュに存在しない場合、remember
メソッドに渡されたクロージャが実行され、その結果がキャッシュに格納されます。
rememberForever
メソッドを使用して、キャッシュからアイテムを取得したり、存在しない場合は永久に保存したりすることができます。
$value = Cache::rememberForever('users', function () { return DB::table('users')->get();});
陳腐化しつつ再検証(Stale While Revalidate)
Cache::remember
メソッドを使用する場合、キャッシュされた値の有効期限が切れていると、一部のユーザーは応答時間が遅くなることがあります。特定の種類のデータでは、キャッシュされた値がバックグラウンドで再計算されている間に、部分的に陳腐化したデータを提供することを許可すると、キャッシュされた値が計算されている間、一部のユーザーが応答時間の遅延を経験するのを防ぐことができます。これは多くの場合、「stale-while-revalidate」パターンと呼ばれ、Cache::flexible
メソッドはこのパターンの実装を提供します。
flexible メソッドは、キャッシュされた値が「新鮮」と見なされる期間と「陳腐化」する時期を指定する配列を受け付けます。配列の最初の値は、キャッシュが新鮮と見なされる秒数を表し、2番目の値は、再計算が必要になる前に陳腐化したデータとして提供できる期間を定義します。
新鮮な期間内(最初の値の前)にリクエストが行われた場合、キャッシュは再計算せずにすぐに返されます。陳腐化した期間内(2つの値の間)にリクエストが行われた場合、陳腐化した値がユーザーに提供され、ユーザーにレスポンスが送信された後にキャッシュされた値を更新するための遅延関数が登録されます。2番目の値の後にリクエストが行われた場合、キャッシュは期限切れと見なされ、値はすぐに再計算されます。これにより、ユーザーの応答時間が遅くなる可能性があります。
$value = Cache::flexible('users', [5, 10], function () { return DB::table('users')->get();});
取得と削除
キャッシュからアイテムを取得して削除する必要がある場合は、pull
メソッドを使用できます。get
メソッドと同様に、アイテムがキャッシュに存在しない場合はnull
が返されます。
$value = Cache::pull('key'); $value = Cache::pull('key', 'default');
キャッシュへのアイテムの保存
Cache
ファサードの put
メソッドを使用して、アイテムをキャッシュに保存できます。
Cache::put('key', 'value', $seconds = 10);
put
メソッドに保存時間が渡されない場合、アイテムは無期限に保存されます。
Cache::put('key', 'value');
秒数を整数で渡す代わりに、キャッシュされたアイテムの有効期限としてDateTime
インスタンスを渡すこともできます。
Cache::put('key', 'value', now()->addMinutes(10));
存在しない場合に保存
add
メソッドは、アイテムがキャッシュにまだ存在しない場合にのみ、アイテムをキャッシュに追加します。アイテムが実際にキャッシュに追加された場合はtrue
を返し、それ以外の場合はfalse
を返します。add
メソッドはアトミック操作です。
Cache::add('key', 'value', $seconds);
アイテムの永久保存
forever
メソッドを使用して、アイテムをキャッシュに永久的に保存できます。これらのアイテムは期限切れにならないため、forget
メソッドを使用してキャッシュから手動で削除する必要があります。
Cache::forever('key', 'value');
Memcached ドライバーを使用している場合、「永久に」保存されたアイテムは、キャッシュがサイズ制限に達すると削除される可能性があります。
キャッシュからのアイテムの削除
forget
メソッドを使用して、キャッシュからアイテムを削除できます。
Cache::forget('key');
有効期限の秒数を0または負の数値にすることによってもアイテムを削除できます。
Cache::put('key', 'value', 0); Cache::put('key', 'value', -5);
flush
メソッドを使用して、キャッシュ全体をクリアできます。
Cache::flush();
キャッシュのフラッシュは、設定されたキャッシュの「prefix」を尊重せず、キャッシュからすべてのエントリを削除します。他のアプリケーションと共有されているキャッシュをクリアする際は、注意深く検討してください。
キャッシュヘルパー
Cache
ファサードを使用することに加えて、グローバルな cache
関数を使用して、キャッシュを介してデータを取得および保存することもできます。cache
関数が単一の文字列引数で呼び出されると、指定されたキーの値を返します。
$value = cache('key');
キーと値のペアの配列と有効期限を関数に渡すと、指定された期間、キャッシュに値が保存されます。
cache(['key' => 'value'], $seconds); cache(['key' => 'value'], now()->addMinutes(10));
cache
関数が引数なしで呼び出されると、Illuminate\Contracts\Cache\Factory
実装のインスタンスを返し、他のキャッシュメソッドを呼び出すことができます。
cache()->remember('users', $seconds, function () { return DB::table('users')->get();});
グローバルな cache
関数の呼び出しをテストする際には、ファサードをテストする場合と同様に、Cache::shouldReceive
メソッドを使用できます。
アトミックロック
この機能を使用するには、アプリケーションでmemcached
、redis
、dynamodb
、database
、file
、またはarray
キャッシュドライバーをアプリケーションのデフォルトのキャッシュドライバーとして使用する必要があります。さらに、すべてのサーバーが同じ中央キャッシュサーバーと通信している必要があります。
ロックの管理
アトミックロックを使用すると、競合状態を心配することなく、分散ロックを操作できます。たとえば、Laravel Forge は、一度に1つのリモートタスクのみがサーバーで実行されるように、アトミックロックを使用しています。Cache::lock
メソッドを使用して、ロックを作成および管理できます。
use Illuminate\Support\Facades\Cache; $lock = Cache::lock('foo', 10); if ($lock->get()) { // Lock acquired for 10 seconds... $lock->release();}
get
メソッドもクロージャを受け付けます。クロージャが実行されると、Laravel は自動的にロックを解放します。
Cache::lock('foo', 10)->get(function () { // Lock acquired for 10 seconds and automatically released...});
リクエスト時にロックが使用できない場合は、指定された秒数待機するように Laravel に指示できます。指定された時間制限内にロックを取得できない場合、Illuminate\Contracts\Cache\LockTimeoutException
がスローされます。
use Illuminate\Contracts\Cache\LockTimeoutException; $lock = Cache::lock('foo', 10); try { $lock->block(5); // Lock acquired after waiting a maximum of 5 seconds...} catch (LockTimeoutException $e) { // Unable to acquire lock...} finally { $lock->release();}
上記の例は、block
メソッドにクロージャを渡すことで簡略化できます。このメソッドにクロージャを渡すと、Laravel は指定された秒数間ロックの取得を試み、クロージャが実行されるとすぐにロックを自動的に解放します。
Cache::lock('foo', 10)->block(5, function () { // Lock acquired after waiting a maximum of 5 seconds...});
プロセス間でのロックの管理
あるプロセスでロックを取得し、別のプロセスでロックを解放したい場合があります。たとえば、Web リクエスト中にロックを取得し、そのリクエストによってトリガーされたキュージョブの最後にロックを解放したい場合があります。このシナリオでは、キュージョブにロックのスコープ付き「所有者トークン」を渡す必要があります。これにより、ジョブは指定されたトークンを使用してロックを再インスタンス化できます。
以下の例では、ロックが正常に取得された場合にキュージョブをディスパッチします。また、ロックのowner
メソッドを使用して、ロックの所有者トークンをキュージョブに渡します。
$podcast = Podcast::find($id); $lock = Cache::lock('processing', 120); if ($lock->get()) { ProcessPodcast::dispatch($podcast, $lock->owner());}
アプリケーションのProcessPodcast
ジョブ内では、所有者トークンを使用してロックを復元して解放できます。
Cache::restoreLock('processing', $this->owner)->release();
現在の所有者を尊重せずにロックを解放する場合は、forceRelease
メソッドを使用できます。
Cache::lock('processing')->forceRelease();
カスタムキャッシュドライバの追加
ドライバの記述
カスタムキャッシュドライバーを作成するには、最初にIlluminate\Contracts\Cache\Store
コントラクトを実装する必要があります。したがって、MongoDB キャッシュの実装は次のようになります。
<?php namespace App\Extensions; use Illuminate\Contracts\Cache\Store; class MongoStore implements Store{ public function get($key) {} public function many(array $keys) {} public function put($key, $value, $seconds) {} public function putMany(array $values, $seconds) {} public function increment($key, $value = 1) {} public function decrement($key, $value = 1) {} public function forever($key, $value) {} public function forget($key) {} public function flush() {} public function getPrefix() {}}
MongoDB 接続を使用してこれらのメソッドをそれぞれ実装するだけです。これらのメソッドをそれぞれ実装する方法の例については、Laravel フレームワークのソースコードにあるIlluminate\Cache\MemcachedStore
を参照してください。実装が完了したら、Cache
ファサードのextend
メソッドを呼び出すことで、カスタムドライバーの登録を完了できます。
Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore);});
カスタムキャッシュドライバーコードの配置場所が不明な場合は、app
ディレクトリ内にExtensions
名前空間を作成できます。ただし、Laravelには厳格なアプリケーション構造がないことに注意してください。アプリケーションは好みに合わせて自由に構成できます。
ドライバの登録
Laravel にカスタムキャッシュドライバーを登録するには、Cache
ファサードの extend
メソッドを使用します。他のサービスプロバイダーがboot
メソッド内でキャッシュされた値を読み取ろうとする可能性があるため、booting
コールバック内でカスタムドライバーを登録します。booting
コールバックを使用することで、アプリケーションのサービスプロバイダーのboot
メソッドが呼び出される直前、ただしすべてのサービスプロバイダーのregister
メソッドが呼び出された後にカスタムドライバーが登録されるようにできます。アプリケーションのApp\Providers\AppServiceProvider
クラスのregister
メソッド内でbooting
コールバックを登録します。
<?php namespace App\Providers; use App\Extensions\MongoStore;use Illuminate\Contracts\Foundation\Application;use Illuminate\Support\Facades\Cache;use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * Register any application services. */ public function register(): void { $this->app->booting(function () { Cache::extend('mongo', function (Application $app) { return Cache::repository(new MongoStore); }); }); } /** * Bootstrap any application services. */ public function boot(): void { // ... }}
extend
メソッドに渡される最初の引数は、ドライバーの名前です。これは、config/cache.php
設定ファイルのdriver
オプションに対応します。2番目の引数は、Illuminate\Cache\Repository
インスタンスを返すクロージャです。クロージャには、サービスコンテナのインスタンスである$app
インスタンスが渡されます。
拡張機能が登録されたら、アプリケーションのconfig/cache.php
設定ファイル内のCACHE_STORE
環境変数またはdefault
オプションを拡張機能の名前に更新します。
イベント
すべてのキャッシュ操作でコードを実行するには、キャッシュによってディスパッチされたさまざまなイベントをリッスンできます。
イベント名 |
---|
Illuminate\Cache\Events\CacheHit |
Illuminate\Cache\Events\CacheMissed |
Illuminate\Cache\Events\KeyForgotten |
Illuminate\Cache\Events\KeyWritten |
パフォーマンスを向上させるために、アプリケーションのconfig/cache.php
設定ファイルで指定されたキャッシュストアのevents
設定オプションをfalse
に設定することで、キャッシュイベントを無効にすることができます。
'database' => [ 'driver' => 'database', // ... 'events' => false,],