База данных · Использование Redis
Введение
Redis – это расширенное хранилище ключ-значение с открытым исходным кодом. Его часто называют сервером структуры данных, поскольку ключи могут содержать строки, хеши, списки, наборы и отсортированные наборы.
Перед использованием Redis с Laravel мы рекомендуем вам установить и использовать расширение PhpRedis PHP через PECL. Расширение сложнее установить по сравнению с пакетами PHP пользовательского слоя, но оно может обеспечить лучшую производительность для приложений, интенсивно использующих Redis. Если вы используете Laravel Sail, то это расширение уже установлено в контейнере Docker вашего приложения.
Если вы не можете установить расширение PhpRedis, то установите пакет predis/predis
через Composer. Predis – это клиент Redis, полностью написанный на PHP и не требующий дополнительных расширений:
composer require predis/predis:^2.0
Конфигурирование
Вы можете настроить параметры 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:[email protected]: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'),
],
Кластеры
Если ваше приложение использует кластер серверов 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 также поддерживает клиентское разделение данных (sharding). Однако клиентское разделение данных не обрабатывает аварийные ситуации, поэтому оно в основном подходит для временных кешированных данных, доступных из другого основного хранилища данных.
Если вы хотите использовать клиентское разделение данных вместо встроенной кластеризации 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
, 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],
],
],
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
(default), Redis::SERIALIZER_PHP
, Redis::SERIALIZER_JSON
, Redis::SERIALIZER_IGBINARY
, и Redis::SERIALIZER_MSGPACK
.
Поддерживаемые алгоритмы сжатия: Redis::COMPRESSION_NONE
(default), Redis::COMPRESSION_LZF
, Redis::COMPRESSION_ZSTD
, и Redis::COMPRESSION_LZ4
.
Взаимодействие с Redis
Вы можете взаимодействовать с Redis, вызывая различные методы фасада Redis
. Фасад Redis
поддерживает динамические методы, то есть вы можете вызвать любую команду Redis, используя фасад, и команда будет передана непосредственно в Redis. В этом примере мы вызовем команду Redis GET
, вызвав метод get
фасада Redis
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
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
обеспечивает удобную обертку для собственных команд MULTI
и EXEC
Redis. Метод transaction
принимает замыкание как единственный аргумент. Это замыкание получит экземпляр подключения Redis и может использовать любые необходимые вам команды, отправляемые на сервер 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);
}
});
Публикация / подписка
Laravel предлагает удобный интерфейс для команд publish
и subscribe
Redis. Эти команды 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;
});