From df4d07be66c4df1dc0031f58225feede53a1551a Mon Sep 17 00:00:00 2001 From: ReWiG Date: Thu, 3 Jul 2025 21:55:59 +0300 Subject: [PATCH] Update cache.md --- cache.md | 437 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 278 insertions(+), 159 deletions(-) diff --git a/cache.md b/cache.md index b981efae..a949a6d0 100644 --- a/cache.md +++ b/cache.md @@ -1,5 +1,5 @@ --- -git: 88b6f0d99c9f89ca38266d9fa0c3b4ea3df3c85e +git: 694c3b5ee4e580f45382ebae8ddb35b676c7121e --- # Кэширование @@ -37,36 +37,40 @@ php artisan migrate Для использования драйвера Memcached требуется установить [пакет Memcached PECL](https://pecl.php.net/package/memcached). Вы можете перечислить все ваши серверы Memcached в файле конфигурации `config/cache.php`. Этот файл уже содержит запись `memcached.servers` для начала: - 'memcached' => [ - // ... +```php +'memcached' => [ + // ... - 'servers' => [ - [ - 'host' => env('MEMCACHED_HOST', '127.0.0.1'), - 'port' => env('MEMCACHED_PORT', 11211), - 'weight' => 100, - ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, ], ], +], +``` При необходимости вы можете задать параметр `host` сокета UNIX. Если вы это сделаете, то параметр `port` должен быть задан как `0`: - 'memcached' => [ - // ... +```php +'memcached' => [ + // ... - 'servers' => [ - [ - 'host' => '/var/run/memcached/memcached.sock', - 'port' => 0, - 'weight' => 100 - ], + 'servers' => [ + [ + 'host' => '/var/run/memcached/memcached.sock', + 'port' => 0, + 'weight' => 100 ], ], +], +``` #### Предварительная подготовка драйвера на основе Redis -Перед использованием драйвера кеша Redis, вам нужно будет либо установить расширение PHP PhpRedis через PECL, либо установить пакет `predis/predis` (~ 2.0) через Composer. [Laravel Sail](/docs/{{version}}/sail) уже включает это расширение. Кроме того, на официальных платформах развертывания Laravel, таких как [Laravel Forge](https://forge.laravel.com) и [Laravel Vapor](https://vapor.laravel.com), расширение PhpRedis установлено по умолчанию. +Перед использованием драйвера кеша Redis, вам нужно будет либо установить расширение PHP PhpRedis через PECL, либо установить пакет `predis/predis` (~ 2.0) через Composer. [Laravel Sail](/docs/{{version}}/sail) уже включает это расширение. Кроме того, на официальных платформах приложений Laravel, таких как [Laravel Cloud](https://cloud.laravel.com) и [Laravel Forge](https://forge.laravel.com), расширение PhpRedis установлено по умолчанию. Для получения дополнительной информации о настройке Redis обратитесь к его [странице документации Laravel](/docs/{{version}}/redis#configuration). @@ -113,90 +117,106 @@ composer require aws/aws-sdk-php Чтобы получить экземпляр хранилища кеша, вы можете использовать фасад `Cache`, который мы будем использовать в этой документации. Фасад `Cache` обеспечивает удобный и краткий доступ к базовым реализациям контрактов кеширования Laravel: - #### Доступ к различным кеш-хранилищам Используя фасад `Cache`, вы можете получить доступ к различным хранилищам кеша с помощью метода `store`. Ключ, переданный методу `store`, должен соответствовать одному из хранилищ, перечисленных в массиве `stores` вашего конфигурационного файла `config/cache.php`: - $value = Cache::store('file')->get('foo'); +```php +$value = Cache::store('file')->get('foo'); - Cache::store('redis')->put('bar', 'baz', 600); // 10 Минут +Cache::store('redis')->put('bar', 'baz', 600); // 10 Минут +``` ### Получение элементов из кеша Метод `get` фасада `Cache` используется для извлечения элементов из кеша. Если элемент не существует в кеше, будет возвращено значение `null`. Если хотите, то вы можете передать второй аргумент методу `get`, указав значение по умолчанию, которое вы хотите вернуть, если элемент отсутствует: - $value = Cache::get('key'); +```php +$value = Cache::get('key'); - $value = Cache::get('key', 'default'); +$value = Cache::get('key', 'default'); +``` Вы даже можете передать замыкание в качестве значения по умолчанию. Результат замыкания будет возвращен, если указанный элемент не существует в кеше. Передача замыкания позволяет отложить получение значений по умолчанию из базы данных или другой внешней службы: - $value = Cache::get('key', function () { - return DB::table(/* ... */)->get(); - }); +```php +$value = Cache::get('key', function () { + return DB::table(/* ... */)->get(); +}); +``` #### Проверка наличия элемента Метод `has` используется для определения того, существует ли элемент в кеше. Этот метод также вернет `false`, если элемент существует, но его значение равно `null`: - if (Cache::has('key')) { - // ... - } +```php +if (Cache::has('key')) { + // ... +} +``` #### Увеличение и уменьшение отдельных значений в кеше Методы `increment` и `decrement` могут использоваться для изменения значений целочисленных элементов в кеше. Оба метода принимают необязательный второй аргумент, указывающий величину увеличения или уменьшения значения элемента: - // Инициализируем значение, если оно не существует... - Cache::add('key', 0, now()->addHours(4)); - - // Увеличиваем или уменьшаем значение... - Cache::increment('key'); - Cache::increment('key', $amount); - Cache::decrement('key'); - Cache::decrement('key', $amount); +```php +// Инициализируем значение, если оно не существует... +Cache::add('key', 0, now()->addHours(4)); + +// Увеличиваем или уменьшаем значение... +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(); - }); +```php +$value = Cache::remember('users', $seconds, function () { + return DB::table('users')->get(); +}); +``` Если элемент не существует в кеше, то замыкание, переданное методу `remember`, будет выполнено, и его результат будет помещен в кеш. Вы можете использовать метод `rememberForever`, чтобы получить элемент из кеша или сохранить его навсегда, если он не существует: - $value = Cache::rememberForever('users', function () { - return DB::table('users')->get(); - }); +```php +$value = Cache::rememberForever('users', function () { + return DB::table('users')->get(); +}); +``` #### Устарело при повторной проверке @@ -207,47 +227,61 @@ composer require aws/aws-sdk-php Если запрос сделан в свежем периоде (до первого значения), кэш возвращается сразу без пересчета. Если запрос сделан в течение периода устаревания (между двумя значениями), устаревшее значение передается пользователю, и [отложенная функция](/docs/{{version}}/helpers#deferred-functions) регистрируется в обновить кэшированное значение после отправки ответа пользователю. Если запрос сделан после второго значения, кеш считается просроченным, и значение немедленно пересчитывается, что может привести к более медленному ответу пользователя: - $value = Cache::flexible('users', [5, 10], function () { - return DB::table('users')->get(); - }); +```php +$value = Cache::flexible('users', [5, 10], function () { + return DB::table('users')->get(); +}); +``` #### Получение данных с последующим удалением элемента Если вам нужно получить элемент из кеша, а затем удалить этот элемент, вы можете использовать метод `pull`. Как и в методе `get`, если элемент не существует в кеше, то будет возвращен `null`: - $value = Cache::pull('key'); +```php +$value = Cache::pull('key'); - $value = Cache::pull('key', 'default'); +$value = Cache::pull('key', 'default'); +``` ### Сохранение элементов в кеше Вы можете использовать метод `put` фасада` Cache` для сохранения элементов в кеше: - Cache::put('key', 'value', $seconds = 10); +```php +Cache::put('key', 'value', $seconds = 10); +``` Если время хранения не передается методу `put`, то элемент будет храниться бесконечно: - Cache::put('key', 'value'); +```php +Cache::put('key', 'value'); +``` Вместо того чтобы передавать количество секунд как целое число, вы также можете передать экземпляр `DateTime`, представляющий желаемое время хранения кешированного элемента: - Cache::put('key', 'value', now()->addMinutes(10)); +```php +Cache::put('key', 'value', now()->addMinutes(10)); +``` #### Сохранение значений при условии их отсутствия Метод `add` добавит элемент в кеш, только если он еще не существует в хранилище кеша. Метод вернет `true`, если элемент был действительно добавлен в кеш. В противном случае метод вернет `false`. Метод `add` – это [атомарная операция](https://ru.wikipedia.org/wiki/Атомарная_операция): - Cache::add('key', 'value', $seconds); +```php +Cache::add('key', 'value', $seconds); +``` #### Сохранение элементов на постоянной основе Метод `forever` используется для постоянного хранения элемента в кеше. Поскольку срок действия этих элементов не истекает, то их необходимо вручную удалить из кеша с помощью метода `forget`: - Cache::forever('key', 'value'); +```php +Cache::forever('key', 'value'); +``` > [!NOTE] > Если вы используете драйвер `memcached`, то элементы, которые хранятся «на постоянной основе», могут быть удалены, когда кеш достигнет предельного размера. @@ -257,39 +291,95 @@ composer require aws/aws-sdk-php Вы можете удалить элементы из кеша с помощью метода `forget`: - Cache::forget('key'); +```php +Cache::forget('key'); +``` Вы также можете удалить элементы, указав нулевое или отрицательное количество секунд срока хранения: - Cache::put('key', 'value', 0); +```php +Cache::put('key', 'value', 0); - Cache::put('key', 'value', -5); +Cache::put('key', 'value', -5); +``` Вы можете очистить весь кеш, используя метод `flush`: - Cache::flush(); +```php +Cache::flush(); +``` > [!WARNING] > Очистка кеша не учитывает ваш настроенный «префикс» кеша и удаляет все записи из кеша. Внимательно учитывайте это при очистке кеша, который используется другими приложениями. + +### Кэширование с использованием мемоизации + +Драйвер кэша `memo` в Laravel позволяет временно сохранять полученные значения кэша в памяти во время обработки одного запроса или выполнения задачи. Это предотвращает повторные обращения к кэшу в рамках одного выполнения и значительно повышает производительность. + +Чтобы использовать мемоизированный кэш, вызовите метод `memo`: + +```php +use Illuminate\Support\Facades\Cache; + +$value = Cache::memo()->get('key'); +``` + +Метод `memo` опционально принимает имя хранилища кэша, которое указывает базовое хранилище кэша, которое будет декорировать мемоизированный драйвер: + +```php +// Using the default cache store... +$value = Cache::memo()->get('key'); + +// Using the Redis cache store... +$value = Cache::memo('redis')->get('key'); +``` + +Первый вызов `get` для заданного ключа извлекает значение из вашего кэш-хранилища, но последующие вызовы в том же запросе или задании будут извлекать значение из памяти: + +```php +// Hits the cache... +$value = Cache::memo()->get('key'); + +// Does not hit the cache, returns memoized value... +$value = Cache::memo()->get('key'); +``` + +При вызове методов, которые изменяют значения кэша (например, `put`, `increment`, `remember` и т. д.), мемоизированный кэш автоматически забывает мемоизированное значение и делегирует вызов изменяющего метода базовому хранилищу кэша: + +```php +Cache::memo()->put('name', 'Taylor'); // Writes to underlying cache... +Cache::memo()->get('name'); // Hits underlying cache... +Cache::memo()->get('name'); // Memoized, does not hit cache... + +Cache::memo()->put('name', 'Tim'); // Forgets memoized value, writes new value... +Cache::memo()->get('name'); // Hits underlying cache again... +``` + ### Глобальный помощник кеша Помимо использования фасада `Cache`, вы также можете использовать глобальную функцию `cache` для извлечения и хранения данных через кеш. Когда функция `cache` вызывается с одним строковым аргументом, она возвращает значение переданного ключа: - $value = cache('key'); +```php +$value = cache('key'); +``` Если вы передадите массив пар ключ / значение и срок хранения в функцию, то она будет хранить значения в кеше в течение указанного времени: - cache(['key' => 'value'], $seconds); +```php +cache(['key' => 'value'], $seconds); - cache(['key' => 'value'], now()->addMinutes(10)); +cache(['key' => 'value'], now()->addMinutes(10)); +``` Когда функция `cache` вызывается без каких-либо аргументов, то она возвращает экземпляр реализации `Illuminate\Contracts\Cache\Factory`, позволяя вам вызывать другие методы кеширования: - cache()->remember('users', $seconds, function () { - return DB::table('users')->get(); - }); +```php +cache()->remember('users', $seconds, function () { + return DB::table('users')->get(); +}); +``` > [!NOTE] > При тестировании вызова глобальной функции `cache` вы можете использовать метод `Cache::shouldReceive` так же, как если бы вы [тестировали фасад](/docs/{{version}}/mocking#mocking-facades). @@ -303,45 +393,53 @@ composer require aws/aws-sdk-php ### Управление блокировками -Атомарные блокировки позволяют управлять распределенными блокировками, не беспокоясь об условиях приоритетности. Например, [Laravel Forge](https://forge.laravel.com) использует атомарные блокировки, чтобы гарантировать, что на сервере одновременно выполняется только одна удаленная задача. Вы можете создавать и управлять блокировками, используя метод `Cache::lock`: +Атомарные блокировки позволяют управлять распределенными блокировками, не беспокоясь об условиях приоритетности. Например, [Laravel Cloud](https://cloud.laravel.com) использует атомарные блокировки, чтобы гарантировать, что на сервере одновременно выполняется только одна удаленная задача. Вы можете создавать и управлять блокировками, используя метод `Cache::lock`: - use Illuminate\Support\Facades\Cache; +```php +use Illuminate\Support\Facades\Cache; - $lock = Cache::lock('foo', 10); +$lock = Cache::lock('foo', 10); - if ($lock->get()) { - // Блокировка получена на 10 секунд... +if ($lock->get()) { + // Блокировка получена на 10 секунд... - $lock->release(); - } + $lock->release(); +} +``` Метод `get` также принимает замыкание. После выполнения замыкания Laravel автоматически снимет блокировку: - Cache::lock('foo', 10)->get(function () { - // Блокировка установлена на 10 секунд и автоматически снимается... - }); +```php +Cache::lock('foo', 10)->get(function () { + // Блокировка установлена на 10 секунд и автоматически снимается... +}); +``` Если блокировка недоступна в тот момент, когда вы ее запрашиваете, вы можете указать Laravel подождать определенное количество секунд. Если блокировка не может быть получена в течение указанного срока, то будет выброшено исключение `Illuminate\Contracts\Cache\LockTimeoutException`: - use Illuminate\Contracts\Cache\LockTimeoutException; +```php +use Illuminate\Contracts\Cache\LockTimeoutException; - $lock = Cache::lock('foo', 10); +$lock = Cache::lock('foo', 10); - try { - $lock->block(5); +try { + $lock->block(5); - // Блокировка получена после ожидания максимум 5 секунд... - } catch (LockTimeoutException $e) { - // Невозможно получить блокировку... - } finally { - $lock->release(); - } + // Блокировка получена после ожидания максимум 5 секунд... +} catch (LockTimeoutException $e) { + // Невозможно получить блокировку... +} finally { + $lock->release(); +} +``` Приведенный выше пример можно упростить, передав замыкание методу `block`. Когда замыкание передается этому методу, Laravel будет пытаться получить блокировку на указанное количество секунд и автоматически снимет блокировку, как только замыкание будет выполнено: - Cache::lock('foo', 10)->block(5, function () { - // Блокировка получена после ожидания максимум 5 секунд... - }); +```php +Cache::lock('foo', 10)->block(5, function () { + // Блокировка получена после ожидания максимум 5 секунд... +}); +``` ### Управление блокировками между процессами @@ -350,21 +448,27 @@ composer require aws/aws-sdk-php В приведенном ниже примере мы отправим задание в очередь, если блокировка будет успешно получена. Кроме того, мы передадим токен инициатора блокировки заданию в очереди с помощью метода `owner` блокировки: - $podcast = Podcast::find($id); +```php +$podcast = Podcast::find($id); - $lock = Cache::lock('processing', 120); +$lock = Cache::lock('processing', 120); - if ($lock->get()) { - ProcessPodcast::dispatch($podcast, $lock->owner()); - } +if ($lock->get()) { + ProcessPodcast::dispatch($podcast, $lock->owner()); +} +``` В рамках задания `ProcessPodcast` нашего приложения мы можем восстановить и снять блокировку с помощью токена инициатора: - Cache::restoreLock('processing', $this->owner)->release(); +```php +Cache::restoreLock('processing', $this->owner)->release(); +``` Если вы хотите принудительно снять блокировку без учета текущего инициатора, то вы можете использовать метод `forceRelease`: - Cache::lock('processing')->forceRelease(); +```php +Cache::lock('processing')->forceRelease(); +``` ## Добавление собственных драйверов кеша @@ -374,31 +478,35 @@ composer require aws/aws-sdk-php Чтобы создать собственный драйвер кеша, сначала нужно реализовать [контракт](/docs/{{version}}/contracts) `Illuminate\Contracts\Cache\Store`. Итак, реализация кеша MongoDB может выглядеть примерно так: - [!NOTE] > Если вам интересно, где разместить свой собственный код драйвера кеша, то вы можете создать пространство имен `Extensions` в своем каталоге `app`. Однако имейте в виду, что Laravel не имеет жесткой структуры приложения, и вы можете организовать свое приложение в соответствии со своими предпочтениями. @@ -408,37 +516,39 @@ composer require aws/aws-sdk-php Чтобы зарегистрировать свой драйвер кеша в Laravel, мы будем использовать метод `extend` фасада `Cache`. Поскольку другие поставщики служб могут попытаться прочитать кешированные значения в рамках своего метода `boot`, мы зарегистрируем свой драйвер в замыкании `booting`. Используя замыкание `booting`, мы можем гарантировать, что наш драйвер зарегистрирован непосредственно перед тем, как метод `boot` вызывается поставщиками служб нашего приложения, и после того, как метод `register` вызывается для всех поставщиков служб. Мы зарегистрируем наше замыкание `booting` в методе `register` класса `App\Providers\AppServiceProvider` нашего приложения: - app->booting(function () { - Cache::extend('mongo', function (Application $app) { - return Cache::repository(new MongoStore); - }); + $this->app->booting(function () { + Cache::extend('mongo', function (Application $app) { + return Cache::repository(new MongoStore); }); - } + }); + } - /** - * Загрузка любых служб приложения. - */ - public function boot(): void - { - // ... - } + /** + * Загрузка любых служб приложения. + */ + public function boot(): void + { + // ... } +} +``` Первым аргументом, передаваемым методу `extend`, является имя драйвера. Оно будет соответствовать вашему параметру `driver` в файле конфигурации `config/cache.php`. Второй аргумент – это замыкание, которое должно возвращать экземпляр `Illuminate\Cache\Repository`. Замыкание будет передано экземпляру `$app`, который является экземпляром [контейнера служб](/docs/{{version}}/container). @@ -449,12 +559,21 @@ composer require aws/aws-sdk-php Чтобы выполнить код при каждой операции с кэшем, вы можете прослушивать [события](/docs/{{version}}/events), запускаемые кэшем: -| Наименование события | -| -------------------------------------- | -| `Illuminate\Cache\Events\CacheHit` | -| `Illuminate\Cache\Events\CacheMissed` | -| `Illuminate\Cache\Events\KeyForgotten` | -| `Illuminate\Cache\Events\KeyWritten` | +| Наименование события | +|----------------------------------------------| +| `Illuminate\Cache\Events\CacheFlushed` | +| `Illuminate\Cache\Events\CacheFlushing` | +| `Illuminate\Cache\Events\CacheHit` | +| `Illuminate\Cache\Events\CacheMissed` | +| `Illuminate\Cache\Events\ForgettingKey` | +| `Illuminate\Cache\Events\KeyForgetFailed` | +| `Illuminate\Cache\Events\KeyForgotten` | +| `Illuminate\Cache\Events\KeyWriteFailed` | +| `Illuminate\Cache\Events\KeyWritten` | +| `Illuminate\Cache\Events\RetrievingKey` | +| `Illuminate\Cache\Events\RetrievingManyKeys` | +| `Illuminate\Cache\Events\WritingKey` | +| `Illuminate\Cache\Events\WritingManyKeys` | Чтобы повысить производительность, вы можете отключить события кэширования, установив для параметра конфигурации `events` значение `false` для данного хранилища кэша в файле конфигурации `config/cache.php` вашего приложения: