Раскапываем Redis
2024
О спикере
Сидоркин Алексей
Архитектор в ГК Иннотех
В разработке с 2015
Проекты
2
План
- AOF vs RDB
- Что такое Redis
- PHP клиенты для Redis
- Redis Cluster и Redis Sentinel
- Структуры данных в Redis
- Redis Streams vs Kafka
3
Конфигураци Redis
Redis — это высокоскоростная NoSQL in memory база данных
CPU (Главное узкое место!)
Пропускная способность сети
4
Конфигураци Redis
5
Redis Cluster vs Sentinel
Как выбрать стратегию для отказоустойчивости и распределения нагрузки ?
6
Redis Cluster vs Sentinel
7
Redis Cluster vs Sentinel
// 1. Создаем объект Sentinel
$sentinel = new RedisSentinel('sentinel.example.com', 26379, 0.5,null, 100);
// 2. Получаем текущего мастера
$master = $sentinel->getMasterAddrByName('mymaster');
// 3. Подключаемся к мастеру
$redis = new Redis();
$redis->connect($master[0], $master[1]);
// 4. Работаем с Redis
$redis->set('presentation', 'Redis+Sentinel works!');
echo $redis->get('presentation');
8
Redis Cluster vs Sentinel
// 1. Создаем объект Sentinel
$sentinel = new RedisSentinel('sentinel.example.com', 26379, 0.5,null, 100);
// 2. Получаем текущего мастера
$master = $sentinel->getMasterAddrByName('mymaster');
// 3. Подключаемся к мастеру
$redis = new Redis();
$redis->connect($master[0], $master[1]);
// 4. Работаем с Redis
$redis->set('presentation', 'Redis+Sentinel works!');
echo $redis->get('presentation');
9
Redis Cluster vs Sentinel
// 1. Создаем объект Sentinel
$sentinel = new RedisSentinel('sentinel.example.com', 26379, 0.5,null, 100);
// 2. Получаем текущего мастера
$master = $sentinel->getMasterAddrByName('mymaster');
// 3. Подключаемся к мастеру
$redis = new Redis();
$redis->connect($master[0], $master[1]);
// 4. Работаем с Redis
$redis->set('presentation', 'Redis+Sentinel works!');
echo $redis->get('presentation');
10
Redis Cluster vs Sentinel
// 1. Создаем объект Sentinel
$sentinel = new RedisSentinel('sentinel.example.com', 26379, 0.5,null, 100);
// 2. Получаем текущего мастера
$master = $sentinel->getMasterAddrByName('mymaster');
// 3. Подключаемся к мастеру
$redis = new Redis();
$redis->connect($master[0], $master[1]);
// 4. Работаем с Redis
$redis->set('presentation', 'Redis+Sentinel works!');
echo $redis->get('presentation');
11
Redis Cluster vs Sentinel
Redis Sentinel — Мониторинг и автоматический failover
12
Redis Cluster vs Sentinel
13
Redis Cluster vs Sentinel
Redis Cluster — Горизонтальное масштабирование
Содержание:
14
Redis Cluster vs Sentinel
Транзакции в кластере (PHP пример)
Содержание:
$redis->multi()
->set("{order:100}:status", "paid")
->hset("{order:100}:items", "product1", 2)
->expire("{order:100}:lock", 3600)
->exec(); // Сработает, если все ключи в одном слоте
Ошибка:
$redis->multi()
->set("order:100", "data")
->set("user:50", "data")
->exec();
15
Redis Cluster vs Sentinel
Обработка ошибок шардирования��try {
$redis->mget(["key1", "key2"]); // Может вызвать MOVED ошибку
} catch (RedisException $e) {
if (strpos($e->getMessage(), 'MOVED') !== false) {
// разделяем запрос
$redis->get("key1");� $redis->get("key2");
}
}
16
Redis Cluster vs Sentinel
Топологии развертывания
17
Redis Cluster vs Sentinel
18
AOF vs RDB
Выбор стратегии сохранения данных в Redis
19
AOF vs RDB
RDB (Snapshotting) — Снимки данных
Содержание:
20
AOF vs RDB
AOF (Append-Only File) — Лог команд
Содержание:
21
AOF vs RDB
Гибридная стратегия: Совместное использование AOF и RDB в Redis
Гибридный формат (RDB-preamble)
[RDB-данные] + [AOF-команды после снэпшота]
Этапы загрузки AOF с RDB-preamble
22
AOF vs RDB
Механизм | Основные риски | Методы минимизации |
RDB | - Потеря данных между снэпшотами - Блокировка при SAVE (не BGSAVE) - Несовместимость формата между версиями | Увеличить частоту снэпшотов (save 60 1000) Всегда использовать BGSAVE Проверять версии при миграции |
AOF | - Рост размера файла - Высокая нагрузка на диск при appendfsync always - Медленное восстановление | Регулярный AOF rewrite Использовать everysec вместо always SSD-диски |
RDB-preamble | - Двойной расход CPU при перезаписи - Временный рост использования памяти (~2x) | Мониторить auto-aof-rewrite-percentage Выделять резерв памяти |
Золотые правила:
Гибридный режим с RDB-preamble — оптимальный выбор для большинства продакшен-сценариев, сочетающий преимущества обоих подходов. Для специфичных случаев (например, pure-кэширование) допустимы чистые RDB или AOF.
23
PHP клиенты для Redis
Битва клиентов
24
PHP клиенты для Redis
Параметр | phpredis (C-расширение) | predis (Pure PHP) | amphp/redis (Async) | swoole/redis (Async, корутины) |
Зависимости | Требует установки C-расширения | Чистый PHP (Composer) | Требует amphp/amp | Требует swoole (C-расширение) |
25
PHP клиенты для Redis
Общая Производительность
Операция | phpredis (ms) | predis (ms) | amphp/redis (ms) | swoole/redis (ms) |
SET | 120 | 350 | 180 | 140 |
GET | 110 | 340 | 170 | 130 |
INCR | 115 | 360 | 190 | 135 |
LPUSH | 125 | 370 | 200 | 145 |
LRANGE | 130 | 380 | 210 | 150 |
Тестирование производительности
Для теста были выполнены 10 000 операций следующих типов:
26
PHP клиенты для Redis
Общая Работа с Sentinel и Cluster
Параметр | phpredis (C-расширение) | predis (Pure PHP) | amphp/redis (Async) | swoole/redis (Async, корутины) |
Поддержка Sentinel | ✅ Да (с PHP 8.2+) | ✅ Да (гибкая конфигурация) | ❌ Нет (только прямое подключение) | ✅ Да (через конфигурацию) |
Поддержка кластера | ✅ Да RedisCluster | ✅ Да (гибко) | ❌ Нет | ✅ Да (через Swoole\Redis\Cluster) |
27
PHP клиенты для Redis
Общая Работа c Асинхронностью
Параметр | phpredis (C-расширение) | predis (Pure PHP) | amphp/redis (Async) | swoole/redis (Async, корутины) |
Асинхронность | ❌ Нет (синхронный) | ❌ Нет (синхронный) | ✅ Да (на основе Amp) | ✅ Да (корутины Swoole) |
28
PHP клиенты для Redis
Когда что выбирать?
Выбирайте phpredis если:
Выбирайте Predis если:
Выбирайте amphp/redis если:
Выбирайте swoole/redis если:
29
Структуры данных в Redis
Я Расскажу тебе о своих структурах
30
Структуры данных в Redis
Strings (Строки)��Основное использование:
$redis->set('user:100:profile', json_encode($userData));
$redis->incr('page:home:views'); // Инкремент счетчика
Особенности:
31
Структуры данных в Redis
Hashes (Хеши)�
Идеально для:
$redis->hMSet('product:123', [
'name' => 'Phone',
'price' => 599,
'stock' => 10
]);
$price = $redis->hGet('product:123', 'price');
Плюсы:
32
Структуры данных в Redis
Lists (Списки)�
Сценарии:
// Добавление в очередь
$redis->lPush('emails:queue', json_encode($emailTask));
// Обработка
while ($task = $redis->rPop('emails:queue')) {
process_email(json_decode($task, true));
}
Важно:
33
Структуры данных в Redis
Sets (Множества)�
Применение:
// Добавление тегов
$redis->sAdd('article:42:tags', 'php', 'redis', 'database');
// Поиск общих тегов
$commonTags = $redis->sInter('article:42:tags', 'article:56:tags');
Операции:
34
Структуры данных в Redis
Sorted Sets (Упорядоченные множества)�
Кейсы:
Особенности:
35
Структуры данных в Redis
HyperLogLog�
Для чего:
// Подсчет уникальных посещений
$redis->pfAdd('site:2023-10:visitors', ['user1', 'user2', 'user1']);
$uniqueVisitors = $redis->pfCount('site:2023-10:visitors'); // => 2
Плюсы:
36
Структуры данных в Redis
Geospatial�
Для чего:
/ Добавление точек
$redis->geoAdd('stores', 13.361389, 38.115556, 'store1');
// Поиск в радиусе
$nearby = $redis->geoRadius('stores', 13.361389, 38.115556, 10, 'km');
Особенности:
37
Структуры данных в Redis
Bitmaps�
Кейсы:
// Отметить посещение
$redis->setBit('user:logins:2023-10', 100, 1);
// Проверить активность
$wasActive = $redis->getBit('user:logins:2023-10', 100);
Особенности:
38
Структуры данных в Redis
Redis Streams: Event-Driven �Аналогия: Kafka в Redis�Использование:
39
Структуры данных в Redis
Redis Streams: Event-Driven �Аналогия: Kafka в Redis�Использование:
Терминология:
40
Структуры данных в Redis
Redis Streams: Event-Driven �Аналогия: Kafka в Redis�Использование:
Терминология:
[ Стрим "orders" ]
├─ 1698765432000-0: { "event": "created", "user_id": 42 }
├─ 1698765432001-0: { "event": "paid", "user_id": 42 }
└─ 1698765432002-0: { "event": "shipped", "user_id": 42 }
41
Структуры данных в Redis
Redis Streams
42
Структуры данных в Redis
Базовые команды (PHP примеры)
$id = $redis->xAdd('user:events', '*', [
'action' => 'login',
'ip' => '192.168.1.1',
'user_id' => 100
]);
// $id = "1698765432000-0"
Публикация события:
Чтение с начала:
$events = $redis->xRead(['user:events' => '0'], 10);
// [
// "user:events" => [
// "1698765432000-0" => ["action" => "login", ...]
// ]
// ]
43
Структуры данных в Redis
Группы потребителей
Создание группы:
���Чтение группой:
������
Подтверждение� обработки:
44
Структуры данных в Redis
Характеристика | Redis Streams | Apache Kafka |
Установка | Redis (≥5.0) | Отдельный кластер |
Персистентность | Зависит от настроек AOF/RDB | Гарантирована |
Производительность | ~1M ops/sec на ноду | ~100K-500K ops/sec |
Макс. размер | Доступная память | Терабайты |
Идеальный кейс | Реал-тайм события, небольшие задержки | Большие объемы данных |
45
Структуры данных в Redis
Если вам нужно... | Выбирайте | Пример использования |
Кеширование, счетчики, флаги | Strings | Кеш страниц, счетчик лайков |
Очереди, стеки, ленты новостей | Lists | Фоновые задачи, история действий |
Уникальные элементы, теги | Sets | Подписчики, черные списки |
Рейтинги, сортировка | Sorted Sets | Топ игроков, таймлайн постов |
Хранение объектов (JSON-подобные данные) | Hashes | Профили пользователей, настройки |
Бинарные флаги, битовая аналитика | Bitmaps | Онлайн-статусы, аудит активности |
Подсчет уникальных значений (приблизительный) | HyperLogLog | Уникальные посетители за день |
Работа с геоданными | Geospatial | Поиск ближайших кафе, трекинг курьеров |
Потоковая обработка событий | Streams | Логи действий, event-driven архитектура |
46
Redis не Open Source
Критерий | Redis | Valkey |
Происхождение | Разработан Salvatore Sanfilippo (2009), сейчас поддерживается Redis Ltd. | Форк Redis 7.2.4, разрабатывается сообществом (Linux Foundation) |
Лицензия | С 2024: Redis Source Available License (RSAL) – ограничения на коммерческое использование | BSD 3-Clause – полностью открытая |
Поддержка | Коммерческая (Redis Ltd.) + облачные решения (Redis Cloud) | Сообществом (альтернатива из-за лицензирования Redis) |
Основная цель | Универсальное key-value хранилище + дополнительные модули (JSON, поиск и т. д.) | Сохранение open-source альтернативы с акцентом на совместимость |
47
Дополнительная информация, �ссылки и код
Спасибо за внимание!
Вопросы?