コンテンツへスキップ

キャッシュ

はじめに

アプリケーションが実行するデータ取得または処理タスクの中には、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コマンドを使用して作成できます。

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 ForgeLaravel 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_IDAWS_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');
lightbulb

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

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

forget メソッドを使用して、キャッシュからアイテムを削除できます。

Cache::forget('key');

有効期限の秒数を0または負の数値にすることによってもアイテムを削除できます。

Cache::put('key', 'value', 0);
 
Cache::put('key', 'value', -5);

flush メソッドを使用して、キャッシュ全体をクリアできます。

Cache::flush();
exclamation

キャッシュのフラッシュは、設定されたキャッシュの「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();
});
lightbulb

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

アトミックロック

exclamation

この機能を使用するには、アプリケーションでmemcachedredisdynamodbdatabasefile、または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);
});
lightbulb

カスタムキャッシュドライバーコードの配置場所が不明な場合は、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,
],