はじめに

アプリケーションが実行するデータ取得や処理タスクの中には、CPU集約型であったり、完了するまでに数秒かかるものがあります。このような場合、同じデータに対する後続のリクエストで迅速に取得できるように、取得したデータを一定期間キャッシュすることが一般的です。キャッシュされたデータは通常、MemcachedRedisのような非常に高速なデータストアに保存されます。

幸いなことに、Laravelはさまざまなキャッシュバックエンドに対して表現力豊かで統一されたAPIを提供しており、これにより、驚異的に高速なデータ取得を活用してWebアプリケーションの速度を向上させることができます。

設定

アプリケーションのキャッシュ設定ファイルはconfig/cache.phpにあります。このファイルでは、アプリケーション全体でデフォルトとして使用するキャッシュストアを指定できます。Laravelは、MemcachedRedisDynamoDB、およびリレーショナルデータベースなどの人気のあるキャッシングバックエンドを標準でサポートしています。さらに、ファイルベースのキャッシュドライバーも利用可能で、arrayおよび「null」キャッシュドライバーは、自動化テスト用の便利なキャッシュバックエンドを提供します。

キャッシュ設定ファイルには、他にもさまざまなオプションが含まれており、確認することができます。デフォルトでは、Laravelはdatabaseキャッシュドライバーを使用するように設定されており、これはアプリケーションのデータベースにシリアライズされたキャッシュオブジェクトを保存します。

ドライバの前提条件

データベース

databaseキャッシュドライバーを使用する場合、キャッシュデータを格納するためのデータベーステーブルが必要です。通常、これはLaravelのデフォルトの0001_01_01_000001_create_cache_table.php データベースマイグレーションに含まれています。ただし、アプリケーションにこのマイグレーションが含まれていない場合は、make:cache-table Artisanコマンドを使用して作成できます:

  1. php artisan make:cache-table
  2. php artisan migrate

Memcached

Memcachedドライバーを使用するには、Memcached PECLパッケージをインストールする必要があります。config/cache.php設定ファイルにすべてのMemcachedサーバーをリストすることができます。このファイルには、開始するためのmemcached.serversエントリがすでに含まれています:

  1. 'memcached' => [
  2. // ...
  3. 'servers' => [
  4. [
  5. 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
  6. 'port' => env('MEMCACHED_PORT', 11211),
  7. 'weight' => 100,
  8. ],
  9. ],
  10. ],

必要に応じて、hostオプションをUNIXソケットパスに設定できます。この場合、portオプションは0に設定する必要があります:

  1. 'memcached' => [
  2. // ...
  3. 'servers' => [
  4. [
  5. 'host' => '/var/run/memcached/memcached.sock',
  6. 'port' => 0,
  7. 'weight' => 100
  8. ],
  9. ],
  10. ],

Redis

LaravelでRedisキャッシュを使用する前に、PECL経由でPhpRedis PHP拡張をインストールするか、Composerを介してpredis/predisパッケージ(~2.0)をインストールする必要があります。Laravel Sailには、この拡張がすでに含まれています。さらに、Laravel ForgeLaravel Vaporなどの公式Laravelデプロイメントプラットフォームには、PhpRedis拡張がデフォルトでインストールされています。

Redisの設定に関する詳細は、Laravelのドキュメントページを参照してください。

DynamoDB

https://aws.amazon.com/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に設定する必要があります。

次に、AWS SDKをインストールして、LaravelアプリケーションがDynamoDBと通信できるようにします:

  1. composer require aws/aws-sdk-php

さらに、DynamoDBキャッシュストア設定オプションに値が提供されていることを確認する必要があります。通常、AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYなどのオプションは、アプリケーションの.env設定ファイルに定義されるべきです:

  1. 'dynamodb' => [
  2. 'driver' => 'dynamodb',
  3. 'key' => env('AWS_ACCESS_KEY_ID'),
  4. 'secret' => env('AWS_SECRET_ACCESS_KEY'),
  5. 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
  6. 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
  7. 'endpoint' => env('DYNAMODB_ENDPOINT'),
  8. ],

キャッシュの使用

キャッシュインスタンスの取得

キャッシュストアインスタンスを取得するには、Cacheファサードを使用できます。これは、このドキュメント全体で使用するものです。Cacheファサードは、Laravelキャッシュ契約の基盤となる実装への便利で簡潔なアクセスを提供します:

  1. <?php
  2. namespace App\Http\Controllers;
  3. use Illuminate\Support\Facades\Cache;
  4. class UserController extends Controller
  5. {
  6. /**
  7. * Show a list of all users of the application.
  8. */
  9. public function index(): array
  10. {
  11. $value = Cache::get('key');
  12. return [
  13. // ...
  14. ];
  15. }
  16. }

複数のキャッシュストアへのアクセス

  1. ``````php
  2. $value = Cache::store('file')->get('foo');
  3. Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes
  4. `

キャッシュからのアイテムの取得

  1. ``````php
  2. $value = Cache::get('key');
  3. $value = Cache::get('key', 'default');
  4. `

デフォルト値としてクロージャを渡すこともできます。指定されたアイテムがキャッシュに存在しない場合、クロージャの結果が返されます。クロージャを渡すことで、デフォルト値の取得をデータベースや他の外部サービスから遅延させることができます:

  1. $value = Cache::get('key', function () {
  2. return DB::table(/* ... */)->get();
  3. });

アイテムの存在確認

  1. ``````php
  2. if (Cache::has('key')) {
  3. // ...
  4. }
  5. `

値のインクリメント / デクリメント

  1. ``````php
  2. // Initialize the value if it does not exist...
  3. Cache::add('key', 0, now()->addHours(4));
  4. // Increment or decrement the value...
  5. Cache::increment('key');
  6. Cache::increment('key', $amount);
  7. Cache::decrement('key');
  8. Cache::decrement('key', $amount);
  9. `

取得と保存

時には、キャッシュからアイテムを取得したいが、要求されたアイテムが存在しない場合にデフォルト値を保存したいことがあります。たとえば、キャッシュからすべてのユーザーを取得したい場合、存在しない場合はデータベースから取得してキャッシュに追加することができます。これをCache::rememberメソッドを使用して行うことができます:

  1. $value = Cache::remember('users', $seconds, function () {
  2. return DB::table('users')->get();
  3. });

アイテムがキャッシュに存在しない場合、rememberメソッドに渡されたクロージャが実行され、その結果がキャッシュに格納されます。

  1. ``````php
  2. $value = Cache::rememberForever('users', function () {
  3. return DB::table('users')->get();
  4. });
  5. `

再検証中の古いデータの提供

  1. この柔軟なメソッドは、キャッシュされた値が「新鮮」と見なされる時間と「古い」と見なされる時間を指定する配列を受け取ります。配列の最初の値は、キャッシュが新鮮と見なされる秒数を表し、2番目の値は再計算が必要になる前に古いデータとして提供できる時間を定義します。
  2. 新鮮な期間内(最初の値の前)にリクエストが行われた場合、キャッシュは再計算なしですぐに返されます。古い期間中(2つの値の間)にリクエストが行われた場合、古い値がユーザーに提供され、[遅延関数](18a7b2a8da62b09e.md#deferred-functions)がユーザーへの応答が送信された後にキャッシュされた値を更新するために登録されます。2番目の値の後にリクエストが行われた場合、キャッシュは期限切れと見なされ、値は即座に再計算され、ユーザーにとって遅い応答が発生する可能性があります:
  3. ``````php
  4. $value = Cache::flexible('users', [5, 10], function () {
  5. return DB::table('users')->get();
  6. });
  7. `

取得と削除

キャッシュからアイテムを取得して削除する必要がある場合、pullメソッドを使用できます。getメソッドと同様に、アイテムがキャッシュに存在しない場合はnullが返されます:

  1. $value = Cache::pull('key');
  2. $value = Cache::pull('key', 'default');

キャッシュにアイテムを保存

  1. ``````php
  2. Cache::put('key', 'value', $seconds = 10);
  3. `

ストレージ時間がputメソッドに渡されない場合、アイテムは無期限に保存されます:

  1. Cache::put('key', 'value');

整数として秒数を渡す代わりに、キャッシュアイテムの希望する有効期限を表すDateTimeインスタンスを渡すこともできます:

  1. Cache::put('key', 'value', now()->addMinutes(10));

存在しない場合に保存

  1. ``````php
  2. Cache::add('key', 'value', $seconds);
  3. `

アイテムを永遠に保存

  1. ``````php
  2. Cache::forever('key', 'value');
  3. `

Memcachedドライバーを使用している場合、「永遠に」保存されたアイテムは、キャッシュがサイズ制限に達したときに削除される可能性があります。

キャッシュからアイテムを削除

  1. ``````php
  2. Cache::forget('key');
  3. `

ゼロまたは負の有効期限秒数を指定することでアイテムを削除することもできます:

  1. Cache::put('key', 'value', 0);
  2. Cache::put('key', 'value', -5);
  1. ``````php
  2. Cache::flush();
  3. `

キャッシュをフラッシュすると、設定されたキャッシュ「プレフィックス」を尊重せず、キャッシュからすべてのエントリが削除されます。他のアプリケーションと共有されているキャッシュをクリアする際は、これを慎重に考慮してください。

キャッシュヘルパー

  1. ``````php
  2. $value = cache('key');
  3. `

キー/値ペアの配列と有効期限を関数に提供すると、指定された期間キャッシュに値が保存されます:

  1. cache(['key' => 'value'], $seconds);
  2. cache(['key' => 'value'], now()->addMinutes(10));

引数なしでcache関数が呼び出されると、Illuminate\Contracts\Cache\Factory実装のインスタンスが返され、他のキャッシングメソッドを呼び出すことができます:

  1. cache()->remember('users', $seconds, function () {
  2. return DB::table('users')->get();
  3. });

グローバルcache関数への呼び出しをテストする際は、ファサードをテストするのと同様にCache::shouldReceiveメソッドを使用できます。

原子的ロック

この機能を利用するには、アプリケーションがmemcachedredisdynamodbdatabasefile、またはarrayキャッシュドライバーをデフォルトのキャッシュドライバーとして使用している必要があります。さらに、すべてのサーバーが同じ中央キャッシュサーバーと通信している必要があります。

ロックの管理

原子的ロックを使用すると、競合状態を心配することなく分散ロックを操作できます。たとえば、Laravel Forgeは、サーバー上で同時に1つのリモートタスクのみが実行されることを保証するために原子的ロックを使用します。Cache::lockメソッドを使用してロックを作成および管理できます:

  1. use Illuminate\Support\Facades\Cache;
  2. $lock = Cache::lock('foo', 10);
  3. if ($lock->get()) {
  4. // Lock acquired for 10 seconds...
  5. $lock->release();
  6. }
  1. ``````php
  2. Cache::lock('foo', 10)->get(function () {
  3. // Lock acquired for 10 seconds and automatically released...
  4. });
  5. `

ロックが要求した時点で利用できない場合、Laravelに指定された秒数待機するよう指示できます。指定された時間内にロックを取得できない場合、Illuminate\Contracts\Cache\LockTimeoutExceptionがスローされます:

  1. use Illuminate\Contracts\Cache\LockTimeoutException;
  2. $lock = Cache::lock('foo', 10);
  3. try {
  4. $lock->block(5);
  5. // Lock acquired after waiting a maximum of 5 seconds...
  6. } catch (LockTimeoutException $e) {
  7. // Unable to acquire lock...
  8. } finally {
  9. $lock->release();
  10. }

上記の例は、blockメソッドにクロージャを渡すことで簡略化できます。このメソッドにクロージャを渡すと、Laravelは指定された秒数ロックを取得し、クロージャが実行された後に自動的にロックを解放します:

  1. Cache::lock('foo', 10)->block(5, function () {
  2. // Lock acquired after waiting a maximum of 5 seconds...
  3. });

プロセス間のロックの管理

時には、1つのプロセスでロックを取得し、別のプロセスでそれを解放したいことがあります。たとえば、Webリクエスト中にロックを取得し、そのリクエストによってトリガーされたキューイングされたジョブの終了時にロックを解放したい場合です。このシナリオでは、ロックのスコープされた「オーナートークン」をキューイングされたジョブに渡す必要があります。

以下の例では、ロックが正常に取得された場合にキューイングされたジョブをディスパッチします。さらに、ロックのオーナートークンをロックのownerメソッドを介してキューイングされたジョブに渡します:

  1. $podcast = Podcast::find($id);
  2. $lock = Cache::lock('processing', 120);
  3. if ($lock->get()) {
  4. ProcessPodcast::dispatch($podcast, $lock->owner());
  5. }

アプリケーションのProcessPodcastジョブ内で、オーナートークンを使用してロックを復元および解放できます:

  1. Cache::restoreLock('processing', $this->owner)->release();

現在のオーナーを尊重せずにロックを解放したい場合は、forceReleaseメソッドを使用できます:

  1. Cache::lock('processing')->forceRelease();

カスタムキャッシュドライバーの追加

ドライバーの作成

カスタムキャッシュドライバーを作成するには、まずIlluminate\Contracts\Cache\Store 契約を実装する必要があります。したがって、MongoDBキャッシュの実装は次のようになります:

  1. <?php
  2. namespace App\Extensions;
  3. use Illuminate\Contracts\Cache\Store;
  4. class MongoStore implements Store
  5. {
  6. public function get($key) {}
  7. public function many(array $keys) {}
  8. public function put($key, $value, $seconds) {}
  9. public function putMany(array $values, $seconds) {}
  10. public function increment($key, $value = 1) {}
  11. public function decrement($key, $value = 1) {}
  12. public function forever($key, $value) {}
  13. public function forget($key) {}
  14. public function flush() {}
  15. public function getPrefix() {}
  16. }

これらのメソッドをMongoDB接続を使用して実装するだけです。これらのメソッドの実装方法の例については、LaravelフレームワークのソースコードIlluminate\Cache\MemcachedStoreを参照してください。実装が完了したら、Cacheファサードのextendメソッドを呼び出してカスタムドライバーの登録を完了できます:

  1. Cache::extend('mongo', function (Application $app) {
  2. return Cache::repository(new MongoStore);
  3. });

カスタムキャッシュドライバーコードをどこに置くか疑問に思っている場合は、appディレクトリ内にExtensions名前空間を作成できます。ただし、Laravelには厳密なアプリケーション構造がないため、好みに応じてアプリケーションを整理することができます。

ドライバーの登録

Laravelにカスタムキャッシュドライバーを登録するには、Cacheファサードのextendメソッドを使用します。他のサービスプロバイダーがbootメソッド内でキャッシュされた値を読み取ろうとする可能性があるため、bootingコールバック内でカスタムドライバーを登録します。bootingコールバックを使用することで、アプリケーションのサービスプロバイダーでbootメソッドが呼び出される直前にカスタムドライバーが登録されることを保証できますが、すべてのサービスプロバイダーでregisterメソッドが呼び出された後です。アプリケーションのApp\Providers\AppServiceProviderクラスのregisterメソッド内でbootingコールバックを登録します:

  1. <?php
  2. namespace App\Providers;
  3. use App\Extensions\MongoStore;
  4. use Illuminate\Contracts\Foundation\Application;
  5. use Illuminate\Support\Facades\Cache;
  6. use Illuminate\Support\ServiceProvider;
  7. class AppServiceProvider extends ServiceProvider
  8. {
  9. /**
  10. * Register any application services.
  11. */
  12. public function register(): void
  13. {
  14. $this->app->booting(function () {
  15. Cache::extend('mongo', function (Application $app) {
  16. return Cache::repository(new MongoStore);
  17. });
  18. });
  19. }
  20. /**
  21. * Bootstrap any application services.
  22. */
  23. public function boot(): void
  24. {
  25. // ...
  26. }
  27. }
  1. 拡張機能が登録されたら、アプリケーションの`````config/cache.php`````設定ファイル内の`````CACHE_STORE`````環境変数または`````default`````オプションを拡張機能の名前に更新します。
  2. <a name="events"></a>
  3. ## イベント
  4. キャッシュ操作ごとにコードを実行するには、キャッシュによって発行されたさまざまな[イベント](/read/laravel-11-x/2fd66143d78d4ae2.md)をリッスンできます:
  5. | イベント名 |
  6. | --- |
  7. | `````Illuminate\Cache\Events\CacheHit````` |
  8. | `````Illuminate\Cache\Events\CacheMissed````` |
  9. | `````Illuminate\Cache\Events\KeyForgotten````` |
  10. | `````Illuminate\Cache\Events\KeyWritten````` |
  11. パフォーマンスを向上させるために、アプリケーションの`````config/cache.php`````設定ファイル内の特定のキャッシュストアに対して`````events`````設定オプションを`````false`````に設定することでキャッシュイベントを無効にできます:
  12. ``````php
  13. 'database' => [
  14. 'driver' => 'database',
  15. // ...
  16. 'events' => false,
  17. ],
  18. `