Собрал материалы по работе с Redis. Уверен многим будет полезно. Текст объёмный, но вся информация важна. Когда-то я бы был очень рад такому описанию. Оно поможет сделать старт, но может послужить и обычным справочником.


Содержание

  1. Введение в Redis
  2. Режимы работы Redis
  3. Redis Sentinel - отказоустойчивость для Standalone
  4. Архитектура Redis Cluster - детали
  5. Типы данных Redis и базовые команды
  6. Практические рекомендации
  7. Источники и дальнейшее изучение

1. Введение в Redis

Что такое Redis?

Redis (Remote Dictionary Server) — это открытая in-memory структура данных, которая может использоваться как база данных, кэш, брокер сообщений и хранилище очередей. Redis хранит данные в оперативной памяти, что обеспечивает исключительно высокую производительность операций чтения и записи.

Основные характеристики

  • In-memory хранение: Данные хранятся в оперативной памяти, что обеспечивает скорость доступа в микросекундах
  • Key-value store: Простая модель данных “ключ-значение” с поддержкой различных типов значений
  • Персистентность: Опциональная возможность сохранения данных на диск (RDB snapshots, AOF)
  • Высокая производительность: Сотни тысяч операций в секунду на одном ядре CPU
  • Атомарные операции: Все команды выполняются атомарно, что обеспечивает консистентность данных
  • Поддержка различных структур данных: Строки, списки, множества, хеши, отсортированные множества, потоки и др.

Типичные сценарии использования

  1. Кэширование: Ускорение доступа к часто запрашиваемым данным
  2. Управление сессиями: Хранение сессий пользователей веб-приложений
  3. Очереди сообщений: Реализация pub/sub (Publish–Subscribe) и очередей задач
  4. Рейт-лимитинг: Ограничение частоты запросов от клиентов
  5. Счетчики и аналитика: Реал-тайм метрики и статистика
  6. Геопространственные данные: Хранение и запросы по координатам
  7. 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 слотов. Каждый узел кластера отвечает за определенный диапазон слотов.

КлючCRC16(key)%16384HashSlotУзелкластера

Пример:

  • Ключ 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 режима

  1. Multi-key команды: Команды, работающие с несколькими ключами, требуют, чтобы все ключи находились на одном узле (в одном hash slot)

    • ❌ Не работают: MGET key1 key2 (если ключи на разных узлах)
    • ✅ Работают: MGET {user}:1 {user}:2 (использование hash tags)
  2. Hash Tags: Для группировки ключей на одном узле используются hash tags

    {user}:123:profile  → только {user} используется для вычисления слота
    {user}:123:settings → тот же слот, что и выше
    
  3. Базы данных: Поддерживается только DB 0 (SELECT не работает)

  4. Некоторые команды не поддерживаются:

    • 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

ХарактеристикаStandaloneCluster
МасштабируемостьВертикальная (один сервер)Горизонтальная (множество узлов)
Максимальный объем данныхОграничен 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

Sent1inel(MRaesdtiesr)Senti2nel(RReepdliisc)aSenti3nel

Минимальная конфигурация:

  • 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()

Автоматическое обнаружение мастера

Клиент автоматически:

  1. Подключается к Sentinel
  2. Запрашивает адрес текущего мастера
  3. Подключается к мастеру
  4. Обновляет адрес при 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 — временные метки для heartbeat
  • config-epoch — эпоха конфигурации (currentEpoch)
  • link-state — состояние связи (connected, disconnected)
  • slots — распределение слотов

IP-адреса и порты

В Redis Cluster каждый узел использует два порта:

  1. Порт клиентов (обычно 6379, 7000-7005) — для подключения клиентов
  2. Порт кластера (обычно +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

Типы репликации

  1. Синхронная репликация (по умолчанию):

    • Мастер ждет подтверждения от реплик перед ответом клиенту
    • Гарантирует консистентность данных
    • Может снижать производительность
  2. Асинхронная репликация:

    • Мастер не ждет подтверждения
    • Выше производительность
    • Возможна потеря данных при падении мастера

Распределение слотов (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.

Как это работает:

  1. При обнаружении недоступности мастера, реплики начинают выборы
  2. Каждая реплика запрашивает голоса у других мастеров
  3. Мастер голосует только один раз за каждую эпоху
  4. Реплика с большинством голосов становится новым мастером
  5. 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:

ХарактеристикаPipelineTxPipeline
АтомарностьНетДа
Откат при ошибкеНетДа
ПроизводительностьВышеНиже (дополнительные проверки)
Использование в кластереМожет работать с ключами на разных узлахВсе ключи должны быть на одном узле

Когда использовать транзакции:

  • Операции, требующие атомарности (например, перевод денег)
  • Операции, где критична консистентность данных
  • Когда все ключи гарантированно на одном узле (или используются 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

  1. Переименуйте опасные команды:
# redis.conf
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG "CONFIG_9a7b8c5d4e3f2g1h0i9j8k7l6m5n4o3p2q1r0"
  1. Используйте отдельные инстансы для разных целей:
    • 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. Оптимизация производительности

  1. Используйте Pipeline для множественных операций:
pipe := rdb.Pipeline()
for i := 0; i < 1000; i++ {
    pipe.Set(ctx, fmt.Sprintf("key:%d", i), "value", 0)
}
pipe.Exec(ctx)  // Одна сетевая операция вместо 1000
  1. Настройте connection pooling:
rdb := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    PoolSize:     20,  // Количество соединений в пуле
    MinIdleConns: 10,  // Минимальное количество idle соединений
})
  1. Используйте подходящие структуры данных:
    • Строки для простых значений
    • Хеши для объектов
    • Списки для очередей
    • 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 и практического опыта разработки.