Redis

Вступ

Redis є програмою з відкритим кодом, розширеним сховищем ключ-значення. Його часто називають сервером структур даних, оскільки ключі можуть містити рядки, хеші, списки, множини та впорядковані множини.

Перш ніж використовувати Redis з Laravel, ми рекомендуємо встановити та використовувати PHP-розширення PhpRedis через PECL. Це розширення складніше встановити порівняно з PHP-пакетами "user-land", але воно може забезпечити кращу продуктивність для застосунків, які інтенсивно використовують Redis. Якщо ви використовуєте Laravel Sail, це розширення вже встановлено в Docker-контейнері вашого застосунку.

Якщо ви не можете встановити розширення PhpRedis, ви можете встановити пакет predis/predis через компонувальник. Predis - це клієнт Redis, написаний повністю на PHP і не вимагає жодних додаткових розширень:

composer require predis/predis

Конфігурація

Ви можете налаштувати параметри Redis вашого застосунку через конфігураційний файл config/database.php. У цьому файлі ви побачите масив redis, що містить сервери Redis, які використовуються вашим застосунком:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'phpredis'),
 
    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
    ],
 
    'default' => [
        'url' => env('REDIS_URL'),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'username' => env('REDIS_USERNAME'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_DB', '0'),
    ],
 
    'cache' => [
        'url' => env('REDIS_URL'),
        'host' => env('REDIS_HOST', '127.0.0.1'),
        'username' => env('REDIS_USERNAME'),
        'password' => env('REDIS_PASSWORD'),
        'port' => env('REDIS_PORT', '6379'),
        'database' => env('REDIS_CACHE_DB', '1'),
    ],
 
],

Кожен сервер Redis, визначений у вашому файлі конфігурації, повинен мати ім'я, хост і порт, якщо ви не визначаєте єдиний URL для представлення з'єднання Redis:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'phpredis'),
 
    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
    ],
 
    'default' => [
        'url' => 'tcp://127.0.0.1:6379?database=0',
    ],
 
    'cache' => [
        'url' => 'tls://user:example@example.com:6380?database=1',
    ],
 
],

Налаштування Схеми Підключення

За замовчуванням клієнти Redis використовуватимуть схему tcp при підключенні до ваших серверів Redis; однак, ви можете використовувати шифрування TLS / SSL, вказавши опцію конфігурації scheme у масиві конфігурації вашого сервера Redis:

'default' => [
    'scheme' => 'tls',
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
],

Clusters

Якщо ваш застосунок використовує кластер серверів Redis, ви повинні визначити ці кластери в межах ключа clusters вашої конфігурації Redis. Цей ключ конфігурації не існує за замовчуванням, тому вам потрібно створити його у файлі конфігурації вашого застосунку config/database.php:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'phpredis'),
 
    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
    ],
 
    'clusters' => [
        'default' => [
            [
                'url' => env('REDIS_URL'),
                'host' => env('REDIS_HOST', '127.0.0.1'),
                'username' => env('REDIS_USERNAME'),
                'password' => env('REDIS_PASSWORD'),
                'port' => env('REDIS_PORT', '6379'),
                'database' => env('REDIS_DB', '0'),
            ],
        ],
    ],
 
    // ...
],

За замовчуванням Laravel використовуватиме нативне кластерування Redis, оскільки значення конфігурації options.cluster встановлено на redis. Кластерування Redis є чудовим варіантом за замовчуванням, оскільки воно плавно обробляє відмови.

Laravel також підтримує шардінг на стороні клієнта при використанні Predis. Однак, шардінг на стороні клієнта не обробляє відмовостійкість; тому він в основному підходить для тимчасових кешованих даних, які доступні з іншого основного сховища даних.

Якщо ви хочете використовувати шардінг на стороні клієнта замість нативного кластеризації Redis, ви можете видалити значення конфігурації options.cluster у файлі конфігурації config/database.php вашого застосунку:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'phpredis'),
 
    'clusters' => [
        // ...
    ],
 
    // ...
],

Predis

Якщо ви хочете, щоб ваш застосунок взаємодіяв з Redis через пакет Predis, ви повинні переконатися, що значення змінної середовища REDIS_CLIENT встановлено на predis:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'predis'),
 
    // ...
],

На додаток до параметрів конфігурації за замовчуванням, Predis підтримує додаткові параметри з'єднання, які можуть бути визначені для кожного з ваших серверів Redis. Щоб використовувати ці додаткові параметри конфігурації, додайте їх до конфігурації вашого сервера Redis у файлі конфігурації config/database.php вашого застосунку:

'default' => [
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
    'read_write_timeout' => 60,
],

PhpRedis

За замовчуванням Laravel використовуватиме розширення PhpRedis для взаємодії з Redis. Клієнт, який Laravel використовуватиме для взаємодії з Redis, визначається значенням параметра конфігурації redis.client, яке зазвичай відображає значення змінної середовища REDIS_CLIENT:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'phpredis'),
 
    // ...
],

На додаток до параметрів конфігурації за замовчуванням, PhpRedis підтримує наступні додаткові параметри підключення: name, persistent, persistent_id, prefix, read_timeout, retry_interval, max_retries, backoff_algorithm, backoff_base, backoff_cap, timeout та context. Ви можете додати будь-який з цих параметрів до конфігурації вашого Redis-сервера у файлі конфігурації config/database.php:

'default' => [
    'url' => env('REDIS_URL'),
    'host' => env('REDIS_HOST', '127.0.0.1'),
    'username' => env('REDIS_USERNAME'),
    'password' => env('REDIS_PASSWORD'),
    'port' => env('REDIS_PORT', '6379'),
    'database' => env('REDIS_DB', '0'),
    'read_timeout' => 60,
    'context' => [
        // 'auth' => ['username', 'secret'],
        // 'stream' => ['verify_peer' => false],
    ],
],

Підключення через Unix Socket

Підключення Redis також можна налаштувати для використання Unix сокетів замість TCP. Це може забезпечити покращену продуктивність, усуваючи накладні витрати TCP для підключень до екземплярів Redis на тому ж сервері, що і ваш застосунок. Щоб налаштувати Redis для використання Unix сокета, встановіть змінну середовища REDIS_HOST на шлях до сокета Redis і змінну середовища REDIS_PORT на 0:

REDIS_HOST=/run/redis/redis.sock
REDIS_PORT=0

PhpRedis Сериалізація та Стиснення

Розширення PhpRedis також може бути налаштоване для використання різноманітних серіалізаторів та алгоритмів стиснення. Ці алгоритми можуть бути налаштовані через масив options вашої конфігурації Redis:

'redis' => [
 
    'client' => env('REDIS_CLIENT', 'phpredis'),
 
    'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        'serializer' => Redis::SERIALIZER_MSGPACK,
        'compression' => Redis::COMPRESSION_LZ4,
    ],
 
    // ...
],

На даний момент підтримувані серіалізатори включають: Redis::SERIALIZER_NONE (за замовчуванням), Redis::SERIALIZER_PHP, Redis::SERIALIZER_JSON, Redis::SERIALIZER_IGBINARY та Redis::SERIALIZER_MSGPACK.

Підтримувані алгоритми стиснення включають: Redis::COMPRESSION_NONE (за замовчуванням), Redis::COMPRESSION_LZF, Redis::COMPRESSION_ZSTD та Redis::COMPRESSION_LZ4.

Взаємодія з Redis

Ви можете взаємодіяти з Redis, викликаючи різні методи на фасаді Redis facade. Фасад Redis підтримує динамічні методи, що означає, що ви можете викликати будь-яку команду Redis на фасаді, і команда буде передана безпосередньо до Redis. У цьому прикладі ми викличемо команду Redis GET, викликавши метод get на фасаді Redis:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Support\Facades\Redis;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Показати профіль вказаного користувача.
*/
public function show(string $id): View
{
return view('user.profile', [
'user' => Redis::get('user:profile:'.$id)
]);
}
}

Як згадувалося вище, ви можете викликати будь-які з команд Redis на фасаді Redis. Laravel використовує магічні методи для передачі команд на сервер Redis. Якщо команда Redis очікує аргументи, ви повинні передати їх відповідному методу фасаду:

use Illuminate\Support\Facades\Redis;
 
Redis::set('name', 'Taylor');
 
$values = Redis::lrange('names', 5, 10);

Альтернативно, ви можете передавати команди на сервер, використовуючи метод command фасаду Redis, який приймає назву команди як перший аргумент і масив значень як другий аргумент:

$values = Redis::command('lrange', ['name', 5, 10]);

Використання декількох підключень Redis

Файл конфігурації config/database.php вашого застосунку дозволяє визначити декілька з'єднань / серверів Redis. Ви можете отримати з'єднання з конкретним з'єднанням Redis, використовуючи метод connection фасаду Redis:

$redis = Redis::connection('connection-name');

Щоб отримати екземпляр з'єднання Redis за замовчуванням, ви можете викликати метод connection без додаткових аргументів:

$redis = Redis::connection();

Транзакції

Метод transaction фасаду Redis надає зручну обгортку навколо нативних команд Redis MULTI та EXEC. Метод transaction приймає замикання як єдиний аргумент. Це замикання отримає екземпляр з'єднання Redis і може виконувати будь-які команди, які воно забажає, до цього екземпляра. Усі команди Redis, виконані в межах замикання, будуть виконані в одній, атомарній транзакції:

use Redis;
use Illuminate\Support\Facades;
 
Facades\Redis::transaction(function (Redis $redis) {
    $redis->incr('user_visits', 1);
    $redis->incr('total_visits', 1);
});

Коли ви визначаєте транзакцію Redis, ви не можете отримувати жодних значень з підключення Redis. Пам'ятайте, що ваша транзакція виконується як одна атомарна операція, і ця операція не виконується, доки весь ваш замикання не завершить виконання своїх команд.

Скрипти Lua

Метод eval надає інший спосіб виконання декількох команд Redis в одній, атомарній операції. Однак метод eval має перевагу у можливості взаємодії з ключами Redis та їх перевірки під час цієї операції. Скрипти Redis пишуться на мові програмування Lua.

Метод eval може спочатку здаватися трохи страшним, але ми розглянемо базовий приклад, щоб розвіяти сумніви. Метод eval очікує кілька аргументів. По-перше, ви повинні передати Lua-скрипт (у вигляді рядка) до методу. По-друге, ви повинні передати кількість ключів (у вигляді цілого числа), з якими взаємодіє скрипт. По-третє, ви повинні передати назви цих ключів. Нарешті, ви можете передати будь-які інші додаткові аргументи, які вам потрібно використовувати у вашому скрипті.

У цьому прикладі ми збільшимо лічильник, перевіримо його нове значення та збільшимо другий лічильник, якщо значення першого лічильника більше п'яти. Нарешті, ми повернемо значення першого лічильника:

$value = Redis::eval(<<<'LUA'
    local counter = redis.call("incr", KEYS[1])
 
    if counter > 5 then
        redis.call("incr", KEYS[2])
    end
 
    return counter
LUA, 2, 'first-counter', 'second-counter');

Будь ласка, зверніться до документації Redis для отримання додаткової інформації про скрипти Redis.

Конвеєризація команд

Іноді вам може знадобитися виконати десятки команд Redis. Замість того, щоб здійснювати мережеву поїздку до вашого сервера Redis для кожної команди, ви можете використовувати метод pipeline. Метод pipeline приймає один аргумент: замикання, яке отримує екземпляр Redis. Ви можете виконати всі ваші команди на цьому екземплярі Redis, і вони будуть відправлені на сервер Redis одночасно, щоб зменшити кількість мережевих поїздок до сервера. Команди все ще будуть виконані в тому порядку, в якому вони були видані:

use Redis;
use Illuminate\Support\Facades;
 
Facades\Redis::pipeline(function (Redis $pipe) {
    for ($i = 0; $i < 1000; $i++) {
        $pipe->set("key:$i", $i);
    }
});

Pub / Sub

Laravel надає зручний інтерфейс до команд Redis publish та subscribe. Ці команди Redis дозволяють слухати повідомлення на заданому "каналі". Ви можете публікувати повідомлення на каналі з іншого застосунку або навіть використовуючи іншу мову програмування, що дозволяє легко здійснювати комунікацію між застосунками та процесами.

Спочатку налаштуємо слухача каналу за допомогою методу subscribe. Ми розмістимо цей виклик методу в Artisan команді, оскільки виклик методу subscribe запускає довготривалий процес:

<?php
 
namespace App\Console\Commands;
 
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
 
class RedisSubscribe extends Command
{
    /**
     * Ім'я та підпис команди консолі.
     *
     * @var string
     */
    protected $signature = 'redis:subscribe';
 
    /**
     * Опис команди консолі.
     *
     * @var string
     */
    protected $description = 'Subscribe to a Redis channel';
 
    /**
     * Виконайте консольну команду.
     */
    public function handle(): void
    {
        Redis::subscribe(['test-channel'], function (string $message) {
            echo $message;
        });
    }
}

Тепер ми можемо публікувати повідомлення в канал, використовуючи метод publish:

use Illuminate\Support\Facades\Redis;
 
Route::get('/publish', function () {
    // ...
 
    Redis::publish('test-channel', json_encode([
        'name' => 'Adam Wathan'
    ]));
});

Підписки з шаблонами

Використовуючи метод psubscribe, ви можете підписатися на канал з шаблоном, що може бути корисним для перехоплення всіх повідомлень на всіх каналах. Ім'я каналу буде передано як другий аргумент у надану замикання:

Redis::psubscribe(['*'], function (string $message, string $channel) {
    echo $message;
});
 
Redis::psubscribe(['users.*'], function (string $message, string $channel) {
    echo $message;
});