Собрал материалы по работе с Redis. Уверен многим будет полезно. Текст объёмный, но вся информация важна. Когда-то я бы был очень рад такому описанию. Оно поможет сделать старт, но может послужить и обычным справочником.
Содержание
- Введение в Redis
- Режимы работы Redis
- Redis Sentinel - отказоустойчивость для Standalone
- Архитектура Redis Cluster - детали
- Типы данных Redis и базовые команды
- Практические рекомендации
- Источники и дальнейшее изучение
1. Введение в Redis
Что такое Redis?
Redis (Remote Dictionary Server) — это открытая in-memory структура данных, которая может использоваться как база данных, кэш, брокер сообщений и хранилище очередей. Redis хранит данные в оперативной памяти, что обеспечивает исключительно высокую производительность операций чтения и записи.
Основные характеристики
- In-memory хранение: Данные хранятся в оперативной памяти, что обеспечивает скорость доступа в микросекундах
- Key-value store: Простая модель данных “ключ-значение” с поддержкой различных типов значений
- Персистентность: Опциональная возможность сохранения данных на диск (RDB snapshots, AOF)
- Высокая производительность: Сотни тысяч операций в секунду на одном ядре CPU
- Атомарные операции: Все команды выполняются атомарно, что обеспечивает консистентность данных
- Поддержка различных структур данных: Строки, списки, множества, хеши, отсортированные множества, потоки и др.
Типичные сценарии использования
- Кэширование: Ускорение доступа к часто запрашиваемым данным
- Управление сессиями: Хранение сессий пользователей веб-приложений
- Очереди сообщений: Реализация pub/sub (Publish–Subscribe) и очередей задач
- Рейт-лимитинг: Ограничение частоты запросов от клиентов
- Счетчики и аналитика: Реал-тайм метрики и статистика
- Геопространственные данные: Хранение и запросы по координатам
- Leaderboards: Рейтинги и таблицы лидеров
2. Режимы работы Redis
Redis может работать в двух основных режимах: Standalone (однонодовый) и Cluster (кластерный). Выбор режима зависит от требований к масштабируемости, отказоустойчивости и объему данных.
2.1. Standalone (однонодовый режим)
Standalone — это режим работы с одним экземпляром Redis. Все данные хранятся на одном сервере, все операции выполняются на этом сервере.
Плюсы Standalone режима
- ✅ Простота настройки и управления: Минимальная конфигурация, легко развернуть
- ✅ Низкая задержка: Нет накладных расходов на маршрутизацию между узлами
- ✅ Поддержка всех команд: Все команды Redis доступны без ограничений
- ✅ Простое резервное копирование: Один файл дампа для всей базы
- ✅ Поддержка множественных баз данных: Можно использовать SELECT для переключения между DB 0-15
Минусы Standalone режима
- ❌ Ограниченная масштабируемость: Ограничен объемом памяти одного сервера
- ❌ Единая точка отказа: При падении сервера теряется доступ ко всем данным
- ❌ Ограниченная производительность: Производительность ограничена одним CPU
Когда использовать Standalone
- Разработка и тестирование
- Небольшие и средние проекты с объемом данных до нескольких десятков GB
- Приложения, где простота важнее масштабируемости
- Системы, где допустимы простои при обслуживании
Пример конфигурации Standalone
# redis.conf
port 6379
bind 0.0.0.0
save 900 1
save 300 10
save 60 10000
2.2. Cluster (кластерный режим)
Cluster — это режим работы Redis с несколькими узлами, где данные автоматически распределяются (шардируются) между узлами. Redis Cluster обеспечивает высокую доступность и горизонтальное масштабирование.
Принцип шардирования (Sharding)
Redis Cluster использует концепцию hash slots (хеш-слотов). Весь ключевой пространство разделено на 16384 слотов. Каждый узел кластера отвечает за определенный диапазон слотов.
Пример:
- Ключ
user:123→ Hash Slot 5461 → Узел A (слоты 0-5460) - Ключ
product:456→ Hash Slot 10923 → Узел B (слоты 5461-10922) - Ключ
order:789→ Hash Slot 16383 → Узел C (слоты 10923-16383)
Репликация и отказоустойчивость
В Redis Cluster каждый мастер-узел может иметь один или несколько реплик (replica nodes). При падении мастер-узла одна из реплик автоматически становится мастером, обеспечивая непрерывность работы.
Минимальная конфигурация кластера:
- 3 мастер-узла (для обеспечения кворума)
- 3 реплики (по одной на каждый мастер)
- Итого: 6 узлов
Ограничения Cluster режима
Multi-key команды: Команды, работающие с несколькими ключами, требуют, чтобы все ключи находились на одном узле (в одном hash slot)
- ❌ Не работают:
MGET key1 key2(если ключи на разных узлах) - ✅ Работают:
MGET {user}:1 {user}:2(использование hash tags)
- ❌ Не работают:
Hash Tags: Для группировки ключей на одном узле используются hash tags
{user}:123:profile → только {user} используется для вычисления слота {user}:123:settings → тот же слот, что и вышеБазы данных: Поддерживается только DB 0 (SELECT не работает)
Некоторые команды не поддерживаются:
FLUSHDB,FLUSHALL— не работают в кластереKEYS *— работает только на локальных ключах узлаSCAN— сканирует все узлы, но медленнее
Когда использовать Cluster
- Большие объемы данных (сотни GB и более)
- Требования к высокой доступности (99.9%+ uptime)
- Необходимость горизонтального масштабирования
- Высокая нагрузка, требующая распределения по нескольким CPU
Пример конфигурации Cluster
# redis-cluster-node-1.conf
port 7001
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 5000
appendonly yes
2.3. Сравнительная таблица: Standalone vs Cluster
| Характеристика | Standalone | Cluster |
|---|---|---|
| Масштабируемость | Вертикальная (один сервер) | Горизонтальная (множество узлов) |
| Максимальный объем данных | Ограничен RAM сервера | Сумма RAM всех узлов |
| Отказоустойчивость | Нет (единая точка отказа) | Да (автоматический failover) |
| Производительность | Высокая (один CPU) | Очень высокая (множество CPU) |
| Сложность настройки | Низкая | Высокая |
| Поддержка всех команд | Да | Ограниченная (multi-key команды) |
| Количество баз данных | 0-15 | Только 0 |
| Использование | Dev, тесты, малые проекты | Production, большие проекты |
3. Redis Sentinel - отказоустойчивость для Standalone
Redis Sentinel — это система мониторинга и автоматического переключения при отказе (failover) для Redis в режиме Standalone. Sentinel обеспечивает высокую доступность без необходимости использования кластера.
Что такое Sentinel?
Sentinel — это отдельный процесс (или набор процессов), который:
- Мониторит состояние мастер-узлов и реплик
- Автоматически выполняет failover при падении мастера
- Уведомляет клиентов о смене мастера
- Предоставляет конфигурацию для автоматического обнаружения текущего мастера
Архитектура Sentinel
Минимальная конфигурация:
- 3 Sentinel процесса (для кворума при принятии решений)
- 1 Master Redis
- 1 или более Replica Redis
Конфигурация Sentinel
Конфигурация Sentinel (sentinel.conf)
# sentinel.conf
port 26379
# Мониторинг мастера
# sentinel monitor <master-name> <ip> <port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 2
# Пароль мастера (если используется)
sentinel auth-pass mymaster mypassword
# Время, после которого мастер считается недоступным (мс)
sentinel down-after-milliseconds mymaster 5000
# Параллельные реплики для синхронизации при failover
sentinel parallel-syncs mymaster 1
# Таймаут failover (мс)
sentinel failover-timeout mymaster 10000
# Уведомления (опционально)
sentinel notification-script mymaster /var/redis/notify.sh
Параметры конфигурации:
quorum— минимальное количество Sentinel, которые должны согласиться, что мастер недоступенdown-after-milliseconds— время без ответа, после которого узел считается недоступнымparallel-syncs— сколько реплик могут одновременно синхронизироваться с новым мастеромfailover-timeout— максимальное время для завершения failover
Запуск Sentinel
# Запуск Sentinel
redis-sentinel sentinel.conf
# Или через redis-server
redis-server sentinel.conf --sentinel
Команды Sentinel
Проверка состояния
# Подключение к Sentinel
redis-cli -p 26379
# Получить информацию о мастерах
SENTINEL masters
# Получить информацию о конкретном мастере
SENTINEL master mymaster
# Получить реплики мастера
SENTINEL replicas mymaster
# Получить информацию о других Sentinel
SENTINEL sentinels mymaster
Ручной failover
# Принудительный failover (только если мастер недоступен)
SENTINEL failover mymaster
# Сброс состояния Sentinel (после изменений конфигурации)
SENTINEL reset mymaster
Работа клиентов с Sentinel
Go клиент
import (
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/v9/sentinel"
)
// Настройка Sentinel
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{
"127.0.0.1:26379",
"127.0.0.1:26380",
"127.0.0.1:26381",
},
Password: "mypassword",
})
// Использование как обычного клиента
val, err := rdb.Get(ctx, "key").Result()
Автоматическое обнаружение мастера
Клиент автоматически:
- Подключается к Sentinel
- Запрашивает адрес текущего мастера
- Подключается к мастеру
- Обновляет адрес при failover
Преимущества и ограничения Sentinel
Преимущества:
- ✅ Высокая доступность для Standalone режима
- ✅ Автоматический failover
- ✅ Проще, чем Cluster (нет шардирования)
- ✅ Поддержка всех команд Redis
- ✅ Поддержка множественных баз данных (SELECT)
Ограничения:
- ❌ Нет горизонтального масштабирования (один мастер)
- ❌ Все данные на одном узле
- ❌ Производительность ограничена одним CPU
- ❌ Требует дополнительных процессов (Sentinel)
Когда использовать Sentinel
- ✅ Нужна отказоустойчивость для Standalone
- ✅ Объем данных помещается на одном сервере
- ✅ Нужны все команды Redis без ограничений
- ✅ Простота важнее масштабируемости
4. Архитектура Redis Cluster - детали
Node ID (Идентификатор узла)
Каждый узел в Redis Cluster имеет уникальный Node ID — 160-битный случайный идентификатор, который генерируется при первом запуске узла.
Просмотр Node ID
# Подключение к узлу кластера
redis-cli -c -p 7001
# Получить информацию о текущем узле
CLUSTER MYID
# Ответ: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
# Получить информацию о всех узлах
CLUSTER NODES
Формат вывода CLUSTER NODES:
<node-id> <ip:port@cport> <flags> <master-node-id> <ping-sent> <pong-recv> <config-epoch> <link-state> <slots>
Пример:
a1b2c3... 127.0.0.1:7001@17001 myself,master - 0 0 0 connected 0-5460
b2c3d4... 127.0.0.1:7002@17002 master - 0 1699123456789 1 connected 5461-10922
c3d4e5... 127.0.0.1:7003@17003 master - 0 1699123456789 2 connected 10923-16383
d4e5f6... 127.0.0.1:7004@17004 slave a1b2c3... 0 1699123456789 0 connected
e5f6g7... 127.0.0.1:7005@17005 slave b2c3d4... 0 1699123456789 0 connected
f6g7h8... 127.0.0.1:7006@17006 slave c3d4e5... 0 1699123456789 0 connected
Разбор формата:
node-id— уникальный идентификатор узла (160 бит)ip:port@cport— IP адрес, порт клиентов, порт кластераflags— роли узла (master, slave, myself, fail, …)master-node-id— ID мастера (для реплик),-для мастеровping-sent,pong-recv— временные метки для heartbeatconfig-epoch— эпоха конфигурации (currentEpoch)link-state— состояние связи (connected, disconnected)slots— распределение слотов
IP-адреса и порты
В Redis Cluster каждый узел использует два порта:
- Порт клиентов (обычно 6379, 7000-7005) — для подключения клиентов
- Порт кластера (обычно +10000, например 16379, 17000-17005) — для коммуникации между узлами
Конфигурация портов
# redis-cluster-node-1.conf
port 7001 # Порт для клиентов
cluster-announce-ip 10.0.0.1 # Внешний IP (для Docker/K8s)
cluster-announce-port 7001 # Внешний порт клиентов
cluster-announce-bus-port 17001 # Внешний порт кластера
Важно:
- В Docker/Kubernetes нужно указывать внешние IP и порты
- Все узлы должны быть доступны друг другу по порту кластера
- Firewall должен разрешать соединения между узлами
Роли узлов: Master и Replica
Master (Мастер-узел)
Мастер-узел — это узел, который:
- Обрабатывает запросы на чтение и запись
- Управляет определенным диапазоном hash slots
- Реплицирует данные на replica узлы
Идентификация мастера:
CLUSTER NODES | grep master
# a1b2c3... 127.0.0.1:7001@17001 myself,master - 0 0 0 connected 0-5460
Replica (Реплика, ранее Slave)
Replica-узел — это узел, который:
- Копирует данные с мастера (репликация)
- Обрабатывает только запросы на чтение (опционально)
- Автоматически становится мастером при failover
Идентификация реплики:
CLUSTER NODES | grep slave
# d4e5f6... 127.0.0.1:7004@17004 slave a1b2c3... 0 0 0 connected
Важно: Термин “slave” устарел, в новых версиях Redis используется “replica”, но в командах и выводе может встречаться “slave”.
Связи Master-Replica
Настройка репликации
# На реплике указываем мастера
redis-cli -p 7004
CLUSTER REPLICATE a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
Проверка репликации
# На мастере - посмотреть реплики
CLUSTER REPLICAS a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
# На реплике - посмотреть мастера
INFO replication
# role:slave
# master_host:127.0.0.1
# master_port:7001
# master_link_status:up
# master_sync_in_progress:0
Типы репликации
Синхронная репликация (по умолчанию):
- Мастер ждет подтверждения от реплик перед ответом клиенту
- Гарантирует консистентность данных
- Может снижать производительность
Асинхронная репликация:
- Мастер не ждет подтверждения
- Выше производительность
- Возможна потеря данных при падении мастера
Распределение слотов (Hash Slots)
Redis Cluster использует 16384 hash slots для распределения данных.
Вычисление слота для ключа
import "hash/crc16"
func getSlot(key string) int {
// Использование hash tags для группировки
tagStart := strings.Index(key, "{")
tagEnd := strings.Index(key, "}")
var hashKey string
if tagStart != -1 && tagEnd != -1 && tagEnd > tagStart {
// Используем только часть между { и }
hashKey = key[tagStart+1 : tagEnd]
} else {
hashKey = key
}
// CRC16 и модуль 16384
return int(crc16.ChecksumCCITT([]byte(hashKey))) % 16384
}
Просмотр распределения слотов
# Посмотреть, какие слоты обслуживает узел
CLUSTER SLOTS
# Ответ:
# 1) 1) (integer) 0 # Начало диапазона
# 2) (integer) 5460 # Конец диапазона
# 3) 1) "127.0.0.1" # IP мастера
# 2) (integer) 7001 # Порт мастера
# 3) "a1b2c3..." # Node ID мастера
# 4) 1) "127.0.0.1" # IP реплики
# 2) (integer) 7004 # Порт реплики
# 3) "d4e5f6..." # Node ID реплики
Перемещение слотов (Resharding)
# Переместить слот 1000 с узла A на узел B
redis-cli --cluster reshard 127.0.0.1:7001
# Интерактивный процесс:
# 1. Сколько слотов переместить? (например, 1)
# 2. Откуда? (node-id источника или "all")
# 3. Куда? (node-id назначения)
Shard ID
Shard ID — это идентификатор шарда (части данных) в кластере. В Redis Cluster каждый мастер-узел представляет собой отдельный шард.
Важно: В Redis Cluster нет явного понятия “shard-id” как отдельного параметра. Вместо этого используется:
- Node ID — для идентификации узла
- Hash Slots — для идентификации данных
Однако, в некоторых контекстах (например, при мониторинге) шарды могут идентифицироваться по Node ID мастера.
Эпохи конфигурации (Configuration Epochs)
currentEpoch
currentEpoch — это глобальный счетчик, который увеличивается при каждом изменении конфигурации кластера (failover, добавление/удаление узлов, перемещение слотов).
Назначение:
- Разрешение конфликтов при одновременных изменениях конфигурации
- Определение актуальной версии конфигурации кластера
- Предотвращение split-brain ситуаций
Просмотр currentEpoch:
CLUSTER INFO
# cluster_current_epoch:15
# Или для конкретного узла
CLUSTER NODES
# ... <config-epoch> ...
lastVoteEpoch
lastVoteEpoch — это эпоха, за которую узел последний раз голосовал при выборе нового мастера во время failover.
Как это работает:
- При обнаружении недоступности мастера, реплики начинают выборы
- Каждая реплика запрашивает голоса у других мастеров
- Мастер голосует только один раз за каждую эпоху
- Реплика с большинством голосов становится новым мастером
lastVoteEpochобновляется, чтобы предотвратить повторное голосование
Просмотр lastVoteEpoch:
# Внутренняя информация (не доступна через стандартные команды)
# Хранится в кластерном конфигурационном файле (nodes.conf)
Пример процесса failover:
1. Мастер A (currentEpoch=10) становится недоступным
2. Реплика B начинает выборы с currentEpoch=11
3. Мастер C голосует за B (lastVoteEpoch=11)
4. Мастер D голосует за B (lastVoteEpoch=11)
5. Реплика B становится мастером (currentEpoch=11)
6. Мастеры C и D обновляют конфигурацию (currentEpoch=11)
Структура кластерного конфигурационного файла
Каждый узел хранит конфигурацию в файле nodes-<port>.conf:
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 127.0.0.1:7001@17001 myself,master - 0 0 0 connected 0-5460
b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 127.0.0.1:7002@17002 master - 0 1699123456789 1 connected 5461-10922
c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 127.0.0.1:7003@17003 master - 0 1699123456789 2 connected 10923-16383
d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 127.0.0.1:7004@17004 slave a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 0 1699123456789 0 connected
vars currentEpoch 15 lastVoteEpoch 12
Последняя строка содержит:
currentEpoch— текущая эпоха конфигурацииlastVoteEpoch— последняя эпоха, за которую узел голосовал
Практические команды для работы с кластером
Создание кластера
# Создание кластера из 6 узлов (3 мастера + 3 реплики)
redis-cli --cluster create \
127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 \
127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 \
--cluster-replicas 1
Проверка состояния кластера
# Информация о кластере
CLUSTER INFO
# Список всех узлов
CLUSTER NODES
# Проверка здоровья кластера
redis-cli --cluster check 127.0.0.1:7001
Добавление нового узла
# Добавить узел как реплику
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001 \
--cluster-slave \
--cluster-master-id a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
Удаление узла
# Удалить узел (слоты должны быть перемещены заранее)
redis-cli --cluster del-node 127.0.0.1:7001 d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
5. Типы данных Redis и базовые команды
Redis поддерживает множество типов данных, каждый из которых оптимизирован для определенных сценариев использования. Рассмотрим основные команды для работы с Redis.
3.1. Строки (Strings)
Строки — это самый простой тип данных в Redis. Значением может быть строка, число или бинарные данные до 512 MB.
GET / SET / DEL
Назначение: Базовые операции для работы с ключами и значениями.
Синтаксис:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
GET key
DEL key [key ...]
Примеры через CLI:
redis-cli> SET user:1:name "Иван"
OK
redis-cli> GET user:1:name
"Иван"
redis-cli> SET user:1:age 25 EX 3600 # TTL 1 час
OK
redis-cli> DEL user:1:name
(integer) 1
Пример на Go:
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
func main() {
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// SET
err := rdb.Set(ctx, "user:1:name", "Иван", 0).Err()
if err != nil {
panic(err)
}
// GET
val, err := rdb.Get(ctx, "user:1:name").Result()
if err != nil {
panic(err)
}
fmt.Println("user:1:name", val)
// SET с TTL
err = rdb.Set(ctx, "user:1:age", "25", time.Hour).Err()
if err != nil {
panic(err)
}
// DEL
err = rdb.Del(ctx, "user:1:name").Err()
if err != nil {
panic(err)
}
}
Особенности:
SET— атомарная операция, O(1)GET— O(1)DEL— O(N), где N — количество ключейSETс флагомNX— устанавливает значение только если ключ не существуетSETс флагомXX— устанавливает значение только если ключ существует
3.2. Множества (Sets)
Множества — это неупорядоченные коллекции уникальных строк. Операции с множествами очень быстрые.
SADD / SMEMBERS
Назначение: Добавление элементов в множество и получение всех элементов.
Синтаксис:
SADD key member [member ...]
SMEMBERS key
Примеры через CLI:
redis-cli> SADD tags:user:1 "golang" "redis" "docker"
(integer) 3
redis-cli> SMEMBERS tags:user:1
1) "docker"
2) "golang"
3) "redis"
redis-cli> SADD tags:user:1 "golang" # Дубликат не добавится
(integer) 0
Пример на Go:
// SADD
err := rdb.SAdd(ctx, "tags:user:1", "golang", "redis", "docker").Err()
if err != nil {
panic(err)
}
// SMEMBERS
members, err := rdb.SMembers(ctx, "tags:user:1").Result()
if err != nil {
panic(err)
}
fmt.Println("Tags:", members) // [docker golang redis]
Особенности:
SADD— O(N), где N — количество добавляемых элементовSMEMBERS— O(N), где N — количество элементов в множестве- Автоматическое удаление дубликатов
- Поддержка операций пересечения, объединения, разности множеств
3.3. Метаданные ключей
TYPE
Назначение: Определение типа данных, хранящегося по ключу.
Синтаксис:
TYPE key
Примеры через CLI:
redis-cli> SET key1 "value"
OK
redis-cli> SADD key2 "member"
(integer) 1
redis-cli> TYPE key1
string
redis-cli> TYPE key2
set
redis-cli> TYPE nonexistent
none
Пример на Go:
keyType, err := rdb.Type(ctx, "key1").Result()
if err != nil {
panic(err)
}
fmt.Println("Type:", keyType) // string, set, list, hash, zset, stream, none
Возвращаемые типы:
string,list,set,zset,hash,stream,none
EXPIRE / TTL
Назначение: Установка времени жизни ключа и проверка оставшегося времени.
Синтаксис:
EXPIRE key seconds
TTL key
Примеры через CLI:
redis-cli> SET session:abc123 "user_data"
OK
redis-cli> EXPIRE session:abc123 3600
(integer) 1
redis-cli> TTL session:abc123
(integer) 3598
redis-cli> TTL nonexistent
(integer) -2
redis-cli> TTL persistent_key
(integer) -1 # Ключ существует, но TTL не установлен
Пример на Go:
// EXPIRE
err := rdb.Expire(ctx, "session:abc123", time.Hour).Err()
if err != nil {
panic(err)
}
// TTL
ttl, err := rdb.TTL(ctx, "session:abc123").Result()
if err != nil {
panic(err)
}
fmt.Println("TTL:", ttl) // time.Duration или -1 (нет TTL), -2 (ключ не существует)
Особенности:
EXPIRE— O(1)TTL— O(1)- Ключ автоматически удаляется по истечении TTL
- Можно установить TTL при создании ключа:
SET key value EX 3600
3.4. Пайплайны и транзакции
PIPELINE
Назначение: Группировка нескольких команд для отправки на сервер одной партией. Команды выполняются последовательно, но без гарантии атомарности.
Особенности Pipeline:
- Команды отправляются одной партией (меньше сетевых round-trips)
- Команды выполняются последовательно
- НЕ атомарно: Если одна команда упадет, остальные все равно выполнятся
- Результаты возвращаются в том же порядке, что и команды
Пример на Go:
pipe := rdb.Pipeline()
pipe.Set(ctx, "key1", "value1", 0)
pipe.Set(ctx, "key2", "value2", 0)
pipe.Get(ctx, "key1")
cmds, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
// Обработка результатов
for _, cmd := range cmds {
fmt.Println(cmd.String())
}
Когда использовать Pipeline:
- Множественные операции, которые не требуют атомарности
- Оптимизация производительности при большом количестве команд
- Операции с ключами, которые могут быть на разных узлах в кластере
TXPIPELINE (MULTI/EXEC)
Назначение: Транзакция — группа команд, выполняемых атомарно. Все команды выполняются или не выполняются вообще.
Особенности транзакций:
- Атомарность: Все команды выполняются как единое целое
- Изоляция: Команды выполняются последовательно, без прерываний
- В Redis Cluster: Все ключи в транзакции должны быть на одном узле (один hash slot)
Пример на Go:
pipe := rdb.TxPipeline()
pipe.Set(ctx, "account:1:balance", "1000", 0)
pipe.Incr(ctx, "account:1:balance")
pipe.Get(ctx, "account:1:balance")
_, err := pipe.Exec(ctx)
if err != nil {
// Если любая команда упадет, вся транзакция откатывается
panic(err)
}
Разница между Pipeline и TxPipeline:
| Характеристика | Pipeline | TxPipeline |
|---|---|---|
| Атомарность | Нет | Да |
| Откат при ошибке | Нет | Да |
| Производительность | Выше | Ниже (дополнительные проверки) |
| Использование в кластере | Может работать с ключами на разных узлах | Все ключи должны быть на одном узле |
Когда использовать транзакции:
- Операции, требующие атомарности (например, перевод денег)
- Операции, где критична консистентность данных
- Когда все ключи гарантированно на одном узле (или используются hash tags)
3.5. Итерация по ключам
SCAN
Назначение: Инкрементальная итерация по ключам базы данных. Безопасная альтернатива команде KEYS *.
Почему SCAN, а не KEYS?
KEYS *блокирует сервер на время выполнения (может занять секунды или минуты)SCANработает инкрементально, не блокируя серверSCANможно прервать в любой моментSCANбезопасен для использования в production
Синтаксис:
SCAN cursor [MATCH pattern] [COUNT count]
Примеры через CLI:
redis-cli> SET user:1:name "Иван"
OK
redis-cli> SET user:2:name "Петр"
OK
redis-cli> SET product:1:name "Товар"
OK
redis-cli> SCAN 0 MATCH user:* COUNT 10
1) "3" # Следующий cursor
2) 1) "user:1:name"
2) "user:2:name"
redis-cli> SCAN 3 MATCH user:* COUNT 10
1) "0" # 0 означает конец итерации
2) (empty list or set)
Пример на Go:
var cursor uint64
var keys []string
for {
var batch []string
batch, cursor, err = rdb.Scan(ctx, cursor, "user:*", 100).Result()
if err != nil {
panic(err)
}
keys = append(keys, batch...)
if cursor == 0 { // Конец итерации
break
}
}
fmt.Println("Found keys:", keys)
Варианты SCAN:
SCAN— итерация по всем ключамSSCAN— итерация по элементам множестваHSCAN— итерация по полям хешаZSCAN— итерация по элементам отсортированного множества
Особенности:
COUNT— это подсказка, реальное количество может отличаться- Один и тот же ключ может быть возвращен несколько раз (но это редко)
- В кластере
SCANсканирует все узлы, что может быть медленнее
3.6. Очистка базы данных
FLUSHDB / FLUSHALL
Назначение: Удаление всех ключей из текущей базы данных (FLUSHDB) или всех баз данных (FLUSHALL).
⚠️ ВАЖНО: Эти команды ОПАСНЫ в production!
Синтаксис:
FLUSHDB [ASYNC]
FLUSHALL [ASYNC]
Примеры через CLI:
redis-cli> SET key1 "value1"
OK
redis-cli> SET key2 "value2"
OK
redis-cli> FLUSHDB
OK
redis-cli> GET key1
(nil) # Ключ удален
Ограничения:
- ❌ Не работает в Redis Cluster: Эти команды отключены в кластерном режиме
- ⚠️ Удаляет ВСЕ данные: Невозможно откатить операцию
- ⚠️ Блокирует сервер: Может занять время на больших базах
Безопасная альтернатива в кластере:
// Использование SCAN + DEL для безопасной очистки
func safeFlush(ctx context.Context, rdb *redis.Client) error {
var cursor uint64
for {
keys, nextCursor, err := rdb.Scan(ctx, cursor, "*", 1000).Result()
if err != nil {
return err
}
if len(keys) > 0 {
err = rdb.Del(ctx, keys...).Err()
if err != nil {
return err
}
}
cursor = nextCursor
if cursor == 0 {
break
}
}
return nil
}
Когда использовать:
- ✅ Только в тестовой/разработческой среде
- ✅ При полном сбросе данных (с предварительным бэкапом)
- ❌ НИКОГДА в production без явной необходимости
3.7. Redis Streams
Streams — это структура данных для работы с логами событий и сообщениями. Похожа на Kafka, но встроена в Redis.
XADD / XREAD / XDEL
Назначение:
XADD— добавление сообщения в потокXREAD— чтение сообщений из потокаXDEL— удаление сообщения из потока
Синтаксис:
XADD stream * field1 value1 [field2 value2 ...]
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
XDEL stream id [id ...]
Примеры через CLI:
# Добавление сообщения
redis-cli> XADD events * user_id 123 action "login" timestamp 1234567890
"1699123456789-0"
# Чтение всех сообщений с начала
redis-cli> XREAD STREAMS events 0
1) 1) "events"
2) 1) 1) "1699123456789-0"
2) 1) "user_id"
2) "123"
3) "action"
4) "login"
5) "timestamp"
6) "1234567890"
# Чтение новых сообщений (блокирующее)
redis-cli> XREAD BLOCK 1000 STREAMS events $
(nil) # Нет новых сообщений, вернулось через 1 секунду
# Удаление сообщения
redis-cli> XDEL events 1699123456789-0
(integer) 1
Пример на Go:
// XADD - добавление события
id, err := rdb.XAdd(ctx, &redis.XAddArgs{
Stream: "events",
Values: map[string]interface{}{
"user_id": 123,
"action": "login",
"timestamp": time.Now().Unix(),
},
}).Result()
if err != nil {
panic(err)
}
fmt.Println("Event ID:", id)
// XREAD - чтение событий
streams, err := rdb.XRead(ctx, &redis.XReadArgs{
Streams: []string{"events", "0"}, // 0 = с начала
Count: 10,
Block: time.Second,
}).Result()
if err != nil {
panic(err)
}
for _, stream := range streams {
for _, message := range stream.Messages {
fmt.Printf("ID: %s, Values: %v\n", message.ID, message.Values)
}
}
// XDEL - удаление события
err = rdb.XDel(ctx, "events", id).Err()
if err != nil {
panic(err)
}
Особенности Streams:
- Сообщения упорядочены по времени (ID содержит timestamp)
- Поддержка consumer groups для распределенной обработки
- Автоматическое удаление старых сообщений (MAXLEN)
- В кластере stream должен быть на одном узле (используйте hash tags)
Использование в кластере:
// Важно: stream ключ должен быть обернут в hash tag
streamKey := "{events}:myStream" // Все операции с этим stream на одном узле
id, err := rdb.XAdd(ctx, &redis.XAddArgs{
Stream: streamKey, // Используем обернутый ключ
Values: map[string]interface{}{"data": "value"},
}).Result()
Типичные сценарии использования:
- Event sourcing (хранение истории событий)
- Очереди сообщений
- Логирование и аудит
- Real-time аналитика
3.8. Дополнительные полезные команды
INCR / DECR
Атомарное увеличение/уменьшение числового значения.
// Счетчик просмотров
views, err := rdb.Incr(ctx, "page:123:views").Result()
// Атомарное увеличение на N
views, err := rdb.IncrBy(ctx, "page:123:views", 5).Result()
HSET / HGET / HGETALL
Работа с хешами (hash maps).
// Сохранение объекта пользователя
err := rdb.HSet(ctx, "user:1", map[string]interface{}{
"name": "Иван",
"email": "ivan@example.com",
"age": 25,
}).Err()
// Получение всех полей
user, err := rdb.HGetAll(ctx, "user:1").Result()
// map[string]string{"name": "Иван", "email": "ivan@example.com", "age": "25"}
LPUSH / RPUSH / LPOP / RPOP
Работа со списками (очереди, стеки).
// Добавление в очередь
err := rdb.LPush(ctx, "queue:tasks", "task1", "task2").Err()
// Извлечение из очереди
task, err := rdb.RPop(ctx, "queue:tasks").Result()
ZADD / ZRANGE
Работа с отсортированными множествами (sorted sets).
// Добавление в рейтинг
err := rdb.ZAdd(ctx, "leaderboard", redis.Z{
Score: 1000,
Member: "player1",
}).Err()
// Получение топ-10
topPlayers, err := rdb.ZRevRange(ctx, "leaderboard", 0, 9).Result()
6. Практические рекомендации
4.1. Безопасное использование Redis в production
Аутентификация
Всегда используйте пароль для защиты Redis:
# redis.conf
requirepass your_strong_password_here
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "your_strong_password_here",
})
Защита от FLUSHDB
- Переименуйте опасные команды:
# redis.conf
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG "CONFIG_9a7b8c5d4e3f2g1h0i9j8k7l6m5n4o3p2q1r0"
- Используйте отдельные инстансы для разных целей:
- Production данные
- Кэш
- Очереди
Ограничение доступа
# redis.conf
# Разрешить подключения только с определенных IP
bind 127.0.0.1 10.0.0.0/8
# Ограничить количество подключений
maxclients 10000
4.2. Когда использовать кластер, а когда standalone
Используйте Standalone, если:
- ✅ Объем данных < 50-100 GB
- ✅ Нет критических требований к uptime
- ✅ Простота важнее масштабируемости
- ✅ Разработка и тестирование
Используйте Cluster, если:
- ✅ Объем данных > 100 GB
- ✅ Требуется 99.9%+ uptime
- ✅ Нужно горизонтальное масштабирование
- ✅ Высокая нагрузка (миллионы операций в секунду)
4.3. Ошибки новичков
❌ Использование KEYS * в production
// ПЛОХО - блокирует сервер
keys, err := rdb.Keys(ctx, "*").Result()
// ХОРОШО - используйте SCAN
var cursor uint64
for {
batch, nextCursor, err := rdb.Scan(ctx, cursor, "*", 100).Result()
// обработка batch
cursor = nextCursor
if cursor == 0 {
break
}
}
❌ Игнорирование TTL
// ПЛОХО - данные накапливаются
rdb.Set(ctx, "cache:key", "value", 0)
// ХОРОШО - устанавливайте TTL
rdb.Set(ctx, "cache:key", "value", time.Hour)
❌ Использование транзакций с ключами на разных узлах
// ПЛОХО - в кластере не сработает
pipe := rdb.TxPipeline()
pipe.Set(ctx, "user:1", "value1", 0) // Узел A
pipe.Set(ctx, "user:2", "value2", 0) // Узел B
pipe.Exec(ctx) // ОШИБКА: CROSSSLOT
// ХОРОШО - используйте hash tags
pipe := rdb.TxPipeline()
pipe.Set(ctx, "{user}:1", "value1", 0) // Оба на одном узле
pipe.Set(ctx, "{user}:2", "value2", 0) // благодаря hash tag
pipe.Exec(ctx) // OK
❌ Неправильная обработка ошибок
// ПЛОХО
val, err := rdb.Get(ctx, "key").Result()
fmt.Println(val) // Может быть пустая строка, если ключ не существует
// ХОРОШО
val, err := rdb.Get(ctx, "key").Result()
if err == redis.Nil {
// Ключ не существует
return
} else if err != nil {
// Другая ошибка
return err
}
// Ключ существует
fmt.Println(val)
4.4. Оптимизация производительности
- Используйте Pipeline для множественных операций:
pipe := rdb.Pipeline()
for i := 0; i < 1000; i++ {
pipe.Set(ctx, fmt.Sprintf("key:%d", i), "value", 0)
}
pipe.Exec(ctx) // Одна сетевая операция вместо 1000
- Настройте connection pooling:
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
PoolSize: 20, // Количество соединений в пуле
MinIdleConns: 10, // Минимальное количество idle соединений
})
- Используйте подходящие структуры данных:
- Строки для простых значений
- Хеши для объектов
- Списки для очередей
- Sets для уникальных коллекций
- Sorted Sets для рейтингов
7. Источники и дальнейшее изучение
Официальная документация
Redis Documentation: https://redis.io/documentation
- Полная документация по всем командам и возможностям
- Руководства по настройке и оптимизации
- Примеры использования
Redis Commands Reference: https://redis.io/commands
- Справочник по всем командам Redis
- Описание параметров и возвращаемых значений
- Примеры использования
Книги
Redis in Action by Josiah L. Carlson
- Практическое руководство по использованию Redis
- Реальные примеры и паттерны
- Оптимизация и best practices
Redis: The Definitive Guide by Josiah L. Carlson, Carlos Baquero, and others
- Подробное описание архитектуры Redis
- Глубокое погружение в типы данных
- Администрирование и мониторинг
Онлайн-ресурсы
Redis University: https://university.redis.com/
- Бесплатные курсы по Redis
- Видео-лекции и практические задания
- Сертификация
Redis Playground: https://redis.io/docs/ui/play/
- Интерактивная песочница для экспериментов
- Примеры команд и сценариев
- Без необходимости установки Redis локально
GitHub и сообщество
Redis GitHub: https://github.com/redis/redis
- Исходный код Redis
- Issue tracker и обсуждения
- Документация разработчика
Redis Community: https://redis.io/community
- Форум сообщества
- Discord канал
- Meetups и конференции
Дополнительные материалы
Redis Labs Blog: https://redis.com/blog/
- Статьи о best practices
- Кейсы использования
- Новости и обновления
Redis YouTube Channel: https://www.youtube.com/c/RedisLabs
- Видео-туториалы
- Вебинары и презентации
- Демонстрации возможностей
Заключение
Redis — это мощный инструмент для работы с данными в памяти, который может значительно ускорить ваше приложение при правильном использовании. Понимание различий между режимами работы, знание основных команд и следование best practices помогут вам эффективно использовать Redis в ваших проектах.
Помните:
- Выбирайте режим работы в зависимости от ваших требований
- Используйте подходящие структуры данных для ваших задач
- Всегда устанавливайте TTL для временных данных
- Избегайте блокирующих команд в production
- Используйте hash tags в кластере для группировки связанных данных
Удачи в изучении Redis! 🚀
Документ подготовлен на основе официальной документации Redis 7.x и практического опыта разработки.