メインコンテンツまでスキップ

キャッシュ

はじめに

アプリケーションで実行されるデータの取得や処理タスクの一部は、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 パッケージ をインストールする必要があります。config/cache.php 構成ファイルにすべての Memcached サーバーをリストアップすることができます。このファイルにはすでに 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 となります。

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

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'),
],

キャッシュの使用

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

キャッシュストアのインスタンスを取得するには、このドキュメント全体で使用する 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 配列にリストされているストアの1つに対応する必要があります:

    $value = Cache::store('file')->get('foo');

Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes

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

Cache ファサードの get メソッドは、キャッシュからアイテムを取得するために使用されます。アイテムがキャッシュに存在しない場合、null が返されます。必要であれば、get メソッドに2番目の引数を渡して、アイテムが存在しない場合に返されるデフォルト値を指定することができます:

    $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 メソッドを使用して、キャッシュ内の整数アイテムの値を調整することができます。これらのメソッドは、アイテムの値を増減させる量を示すオプションの第二引数を受け入れます:

    // 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();
});

取得して削除

キャッシュからアイテムを取得してからそのアイテムを削除する必要がある場合は、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');

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

    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 メソッドを使用できます。

アトミックロック

警告

この機能を利用するには、アプリケーションがデフォルトのキャッシュドライバとして 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...
});

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

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


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

```php
$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 contractを実装する必要があります。したがって、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,
],