Подключение Redis
Внимание
Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!
Руководство по подключению к Redis в IO7 фреймворке.
Содержание
Требования
Для работы с Redis необходимо PHP расширение redis:
pecl install redis
Или через apt:
apt-get install php-redis
RedisManager
Пространство имён: IO
Менеджер Redis с поддержкой строк, хешей, списков, множеств, сортированных множеств, Pub/Sub и Lua скриптов. Реализован как синглтон.
Инициализация
init()
public static function init(array $config): self
Инициализировать или получить существующий экземпляр.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$config | array<string, mixed> | — | Да | Конфигурация подключения |
Параметры конфигурации:
| Ключ | Тип | По умолчанию | Обязателен | Описание |
|---|---|---|---|---|
host | string | — | Да | Адрес сервера |
port | int | 6379 | Нет | Порт сервера |
timeout | int | 0 | Нет | Таймаут подключения (сек) |
password | string | — | Да | Пароль |
database | int | 0 | Нет | Номер базы данных |
persistent | bool | false | Нет | Persistent-подключение |
use IO\RedisManager;
RedisManager::init([
'host' => '127.0.0.1',
'port' => 6379,
'timeout' => 0,
'password' => 'secret',
'database' => 0,
'persistent' => false,
]);
getInstance()
public static function getInstance(): self
Получить синглтон-экземпляр. Выбрасывает \Exception если не инициализирован.
$redis = RedisManager::getInstance();
getClient()
public function getClient(): \Redis
Получить объект \Redis для прямого доступа.
$redis = RedisManager::getInstance()->getClient();
$redis->ping();
Типы данных
Строки
get()
public function get(string $key): mixed
Получить значение по ключу. Возвращает null если ключ не найден.
$value = RedisManager::getInstance()->get('user:123');
set()
public function set(string $key, mixed $value, ?int $ttl = null): bool
Установить значение с опциональным TTL.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$key | string | — | Да | Ключ |
$value | mixed | — | Да | Значение (сериализуется) |
$ttl | ?\int | null | Нет | Время жизни в секундах (null — без ограничений) |
RedisManager::getInstance()->set('user:123', ['name' => 'Иван'], 3600);
exists()
public function exists(string $key): int
Проверить существование ключа.
if (RedisManager::getInstance()->exists('cache:users')) {
// Ключ существует
}
expire()
public function expire(string $key, int $seconds): bool
Установить время жизни ключа.
ttl()
public function ttl(string $key): int
Получить оставшееся время жизни ключа. -1 — без срока, -2 — ключ не найден.
Хеши
hSet()
public function hSet(string $key, string $field, mixed $value): bool
Установить поле хеша.
RedisManager::getInstance()->hSet('user:123', 'email', 'ivan@example.com');
hGet()
public function hGet(string $key, string $field): mixed
Получить значение поля хеша.
$email = RedisManager::getInstance()->hGet('user:123', 'email');
hGetAll()
public function hGetAll(string $key): array
Получить все поля и значения хеша.
$user = RedisManager::getInstance()->hGetAll('user:123');
// ['name' => 'Иван', 'email' => 'ivan@example.com']
Списки (очереди)
lPush()
public function lPush(string $key, mixed $value): int
Добавить элемент в начало списка.
RedisManager::getInstance()->lPush('queue:tasks', ['task' => 'process']);
rPop()
public function rPop(string $key): mixed
Извлечь элемент из конца списка.
$task = RedisManager::getInstance()->rPop('queue:tasks');
lRange()
public function lRange(string $key, int $start, int $stop): array
Получить диапазон элементов списка.
$items = RedisManager::getInstance()->lRange('list:abc', 0, -1);
Множества
sAdd()
public function sAdd(string $key, mixed $value): bool
Добавить значение в множество.
RedisManager::getInstance()->sAdd('tags:post:1', 'php');
RedisManager::getInstance()->sAdd('tags:post:1', 'redis');
sMembers()
public function sMembers(string $key): array
Получить все элементы множества.
$tags = RedisManager::getInstance()->sMembers('tags:post:1');
// ['php', 'redis']
Сортированные множества
zAdd()
public function zAdd(string $key, float $score, mixed $value): bool
Добавить значение с весом.
RedisManager::getInstance()->zAdd('leaderboard', 100.5, ['player' => 'Alice']);
RedisManager::getInstance()->zAdd('leaderboard', 200.0, ['player' => 'Bob']);
zRange()
public function zRange(
string $key,
int $start,
int $stop,
bool $withScores = false
): array
Получить диапазон элементов.
$top = RedisManager::getInstance()->zRange('leaderboard', 0, 9, true);
// [['player' => 'Bob', score], ['player' => 'Alice', score]]
Pub/Sub
publish()
public function publish(string $channel, mixed $message): int
Опубликовать сообщение в канал.
RedisManager::getInstance()->publish('notifications', [
'type' => 'alert',
'msg' => 'Новое уведомление',
]);
subscribe()
public function subscribe(array $channels, callable $callback): void
Подписаться на каналы (блокирующий вызов).
RedisManager::getInstance()->subscribe(
['notifications', 'alerts'],
function (string $channel, mixed $message): void {
echo "Получено в {$channel}: " . json_encode($message) . "\n";
}
);
Транзакции
multi()
public function multi(): \Redis
Начать транзакцию.
exec()
public function exec(): array
Выполнить транзакцию.
discard()
public function discard(): bool
Отменить транзакцию.
$redis = RedisManager::getInstance();
$redis->multi();
$redis->set('key1', 'value1');
$redis->set('key2', 'value2');
$results = $redis->exec();
Lua скрипты
eval()
public function eval(string $script, array $args = [], int $numKeys = 0): mixed
Выполнить Lua скрипт.
| Параметр | Тип | Значение по умолчанию | Обязателен | Описание |
|---|---|---|---|---|
$script | string | — | Да | Lua скрипт |
$args | array | [] | Нет | Аргументы (ключи и значения) |
$numKeys | int | 0 | Нет | Количество ключей (начальная позиция в $args) |
// Атомарное увеличение с лимитом
$result = RedisManager::getInstance()->eval('
local current = tonumber(redis.call("GET", KEYS[1]) or "0")
local max = tonumber(ARGV[1])
if current < max then
redis.call("INCR", KEYS[1])
return current + 1
end
return -1
', ['counter:limited'], 1);
Примеры
Кэширование
use IO\RedisManager;
$redis = RedisManager::getInstance();
$cacheKey = 'user:' . $userId;
// Попытка получить из кэша
$user = $redis->get($cacheKey);
if ($user === null) {
// Кэш промах — загружаем из БД
$user = DB::table('users')->find($userId);
$redis->set($cacheKey, $user, 3600); // кэш на 1 час
}
echo $user['name'];
Сессии
$redis = RedisManager::getInstance();
$sessionId = session_id();
// Сохранить данные сессии
$redis->set("session:{$sessionId}", $data, 1800); // 30 минут
// Получить данные сессии
$data = $redis->get("session:{$sessionId}");
// Продлить сессию
$redis->expire("session:{$sessionId}", 1800);
Очередь задач
$redis = RedisManager::getInstance();
// Добавить задачу
$redis->lPush('queue:email', [
'to' => 'user@example.com',
'subject' => 'Привет!',
'body' => 'Текст письма',
]);
// Обработать задачи
while (true) {
$task = $redis->rPop('queue:email');
if ($task === null) {
sleep(1);
continue;
}
// Отправка email
sendEmail($task['to'], $task['subject'], $task['body']);
}
Rate Limiting
$redis = RedisManager::getInstance();
$ip = $_SERVER['REMOTE_ADDR'];
$key = "ratelimit:{$ip}";
$count = $redis->get($key);
if ($count !== null && $count >= 100) {
http_response_code(429);
echo "Too many requests";
exit;
}
$redis->set($key, ($count ?? 0) + 1, 60); // окно 1 минута
Pub/Sub для уведомлений
// Publisher (в API-эндпоинте)
$redis = RedisManager::getInstance();
$redis->publish('user:' . $userId, [
'type' => 'order_status',
'data' => ['order_id' => 123, 'status' => 'shipped'],
]);
// Subscriber (в WebSocket-сервере)
$redis->subscribe(['user:' . $userId], function (string $channel, mixed $message): void {
// Отправить клиенту через WebSocket
sendToWebSocket($message);
});
Leaderboard
$redis = RedisManager::getInstance();
// Обновить результат
$redis->zAdd('game:leaderboard', $score, ['user' => $userId]);
// Топ-10
$top = $redis->zRange('game:leaderboard', 0, 9, true);
// Рейтинг пользователя
$rank = $redis->getClient()->zRevRank('game:leaderboard', ['user' => $userId]);
Рекомендации
- Используйте
persistent => trueдля высоконагруженных приложений - Разделяйте базы данных (
database => N) для разных целей - Устанавливайте TTL для временных данных
- Избегайте
KEYS *в продакшене — используйтеSCAN - Используйте Lua скрипты для атомарных операций
- Мониторьте память через
INFO memory
FAQ
Как проверить подключение?
$redis = RedisManager::getInstance();
if ($redis->getClient()->ping() === '+PONG') {
echo "Redis подключён\n";
}
Как работать с несколькими Redis серверами?
RedisManager::init(['host' => 'cache1.local', 'database' => 0]);
// Или используйте不同的 database numbers
Что хранится в кэше?
Все значения сериализуются через serialize(). При чтении автоматически десериализуются.
Как очистить кэш?
$redis->getClient()->flushDB(); // Очистить текущую БД
$redis->getClient()->flushAll(); // Очистить все БД
Как узнать размер кэша?
$info = $redis->getClient()->info('memory');
echo "Использовано памяти: {$info['used_memory_human']}\n";