中文翻译暂不可用,显示俄语原文。

Настроить Redis cluster с LFU eviction

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить Redis cluster с LFU eviction

1. Цель задачи

Научиться разворачивать Redis cluster и конфигурировать политику вытеснения LFU (Least Frequently Used). Провести A/B‑тест: сравнить hit rate и количество вытесненных ключей при политиках allkeys-lfu и allkeys-lru на синтетических данных с Zipf-распределением доступа. Зафиксировать преимущество LFU в сценариях с «горячими» и «холодными» ключами.

Ключевой результат Работающий Redis cluster с политикой allkeys-lfu, метрики A/B‑теста, показывающие hit rate LFU выше, чем LRU, минимум на 5%.


2. Исходные данные

Перед началом необходимо иметь:

Что нужноОткуда взять
Docker Engine 24+ и Docker Compose v2Установить с оф. сайта или через пакетный менеджер
Redis 7.2+ образ (официальный)docker pull redis:7.2
Python 3.10+Установить или использовать conda/venv
Библиотека redis-pypip install redis
Библиотеки для анализа: pandas, matplotlibpip install pandas matplotlib
Инструмент для генерации данных (Python скрипт)Создать в рамках задачи (этап 2)

Если нет реального кластера — симулируем:

  1. Локальный Redis cluster в Docker
    • Используем docker-compose.yml с 6 контейнерами (3 мастера, 3 реплики).
    • Каждый контейнер использует порты с 7001 по 7006 (или другие свободные).
    • С помощью redis-cli --cluster create соединяем ноды в кластер.
  2. Генерация нагрузки
    • Вместо redis-benchmark (не поддерживает точный контроль паттернов) пишем Python-скрипт с redis-py, который имитирует чтение/запись с Zipf-распределением ключей.
  3. Сбор метрик

3. Технологический стек

КомпонентИнструментыНазначение
Redis clusterRedis 7.2, Docker, docker-composeЗапуск распределённого кэша
Генерация нагрузкиPython 3.10+, redis-py, numpy, matplotlibСоздание данных, выполнение запросов, сбор статистики
Мониторингredis-cli, Redis INFO, pandasСбор и обработка метрик производительности
Визуализацияmatplotlib / seabornГрафики hit rate, evictions

4. Этапы выполнения

Этап 1: Развёртывание Redis cluster (45 минут)

Действия

  1. Создать docker-compose.yml с 6 сервисами (redis-1..redis-6):

    • Каждый сервис использует образ redis:7.2.
    • Пробросить порты 7001-7006 на хосте.
    • В каждом контейнере указать конфигурацию через volume: redis.conf с настройками кластера.

    Пример конфигурационного файла redis-cluster.conf:

    port 7001
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    appendonly yes
    maxmemory 256mb
    maxmemory-policy allkeys-lru   # на старте LRU
    

    (для теста LFU будем менять политику позже через CONFIG SET).

  2. Запустить кластер

    docker-compose up -d
    
  3. Создать кластер с помощью redis-cli

    docker exec -it redis-1 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
    
  4. Проверить состояние

    docker exec -it redis-1 redis-cli -p 7001 cluster info
    docker exec -it redis-1 redis-cli -p 7001 cluster nodes
    

Ожидаемый результат этапа 6 Docker-контейнеров работают, кластер в статусе ok, все слоты покрыты.


Этап 2: Генерация тестовых данных с Zipf-распределением (30 минут)

Действия

  1. Написать Python-скрипт generate_data.py

    • Подключается к кластеру через redis.RedisCluster(startup_nodes=[{"host":"localhost","port":7001}]).
    • Создаёт 100 000 ключей вида key_000001key_100000.
    • Распределение доступа (частоты чтения) подчиняется закону Zipf: P(key_i) ∝ 1/(i^s), где s ≈ 1.2.
    • Для каждого ключа генерируем значение (например, value_<номер>) и записываем через SET.
    • Важно: все ключи должны иметь одинаковый размер (например, 256 байт), чтобы не влиять на политику вытеснения.

    Пример фрагмента:

    import numpy as np
    from redis.cluster import RedisCluster
    
    rc = RedisCluster(startup_nodes=[{"host":"localhost","port":7001}])
    n_keys = 100_000
    # Zipf распределение: вероятность обращения к i-му ключу ~ 1/(i**1.2)
    zipf_probs = np.random.zipf(1.2, n_keys)
    # Нормализация для выбора индекса
    keys = [f"key_{i:06d}" for i in range(1, n_keys+1)]
    # Заполнение
    for idx, key in enumerate(keys):
        rc.set(key, f"value_{idx:06d}" * 4)  # ~256 байт
    
  2. Запланировать профиль доступа

    • Создать список из 500 000 операций GET, где выбор ключа происходит согласно Zipf (т.е. «горячие» ключи читаются чаще).
    • Идентично для теста LRU и LFU (один и тот же набор операций, один и тот же порядок).
  3. Сохранить список операций в файл workload.csv (ключ и флаг "get").

Ожидаемый результат этапа Кластер заполнен 100 000 ключами, сгенерирован файл с нагрузкой.


Этап 3: A/B тест — LRU vs LFU (1 час)

Действия

  1. Тест LRU

    • Убедиться, что политика allkeys-lru (уже установлена по умолчанию на этапе 1).
    • Запустить скрипт run_workload.py, который считывает workload.csv и выполняет все GET-запросы к кластеру.
    • Скрипт должен собирать метрики до и после прогона:
      before = rc.info("stats")
      # выполнить нагрузку
      after = rc.info("stats")
      hits_lru = after["keyspace_hits"] - before["keyspace_hits"]
      misses_lru = after["keyspace_misses"] - before["keyspace_misses"]
      evicted_lru = after["evicted_keys"] - before["evicted_keys"]
      hit_rate_lru = hits_lru / (hits_lru + misses_lru) * 100
      
    • Записать результаты в CSV.
  2. Сброс состояния кластера

    • Очистить все данные: FLUSHALL на одном из мастеров (реплицируется на все шарды).
    • Перезаполнить кластер снова из generate_data.py (чтобы начальные условия были идентичны).
  3. Настройка политики LFU

    • На каждом мастере выполнить:
      docker exec -it redis-1 redis-cli -p 7001 CONFIG SET maxmemory-policy allkeys-lfu
      
      Повторить для портов 7002, 7003 (или использовать --cluster call).
    • Проверить: CONFIG GET maxmemory-policy на всех мастерах.
  4. Тест LFU

    • Запустить тот же run_workload.py (тот же workload.csv, тот же порядок операций).
    • Собрать метрики: hits_lfu, misses_lfu, evicted_lfu, hit_rate_lfu.

Ожидаемый результат этапа Два CSV-файла (или один сводный) с метриками для LRU и LFU.


Этап 4: Анализ результатов и построение графиков (30 минут)

Действия

  1. Написать скрипт analyze.py

    • Загрузить данные из CSV.
    • Вычислить hit rate для обоих тестов.
    • Построить столбчатую диаграмму: сравнение hit rate.
    • Вывести таблицу с метриками: evicted_keys, hits, misses.
  2. Выводы

    • Если hit rate LFU > LRU, то цель достигнута.
    • Если нет — проверить, что распределение доступа действительно Zipf, и что maxmemory достаточно мал (например, 256 МБ) для активного вытеснения.

Ожидаемый результат этапа Файл с графиком (comparison.png) и сводная таблица в stdout или текстовом файле.


Этап 5: Фиксация конфигурации и документация (15 минут)

Действия

  1. Сохранить итоговую конфигурацию кластера

    • Экспортировать nodes.conf из одной из нод (можно через redis-cli --cluster backup, но проще — сохранить файл из контейнера).
    • Записать в final-redis.conf все параметры, включая maxmemory-policy allkeys-lfu.
  2. Написать краткий отчёт REPORT.md со структурой:

    • Цель
    • Методика (паттерн Zipf, параметры кластера)
    • Результаты (таблица hit rate, evictions)
    • График
    • Выводы

Ожидаемый результат этапа Файлы: docker-compose.yml, generate_data.py, run_workload.py, analyze.py, comparison.png, REPORT.md.


5. Критерии приемки (Definition of Done)

  • Redis cluster из 3 мастеров + 3 реплик успешно поднят и отвечает на клиентские запросы.
  • Политика вытеснения allkeys-lfu применена на всех мастерах.
  • Выполнен прогон нагрузки с одинаковыми условиями для LRU и LFU.
  • Метрики keyspace_hits, keyspace_misses, evicted_keys зафиксированы до и после каждого прогона.
  • Hit rate при LFU превышает hit rate при LRU не менее чем на 5%.
  • Построен график сравнения hit rate.
  • Создан отчёт REPORT.md с описанием задачи, конфигурации и результатов.

6. Ожидаемый результат

Конечные артефакты

  • docker-compose.yml — конфигурация для запуска кластера.
  • generate_data.py — скрипт генерации данных с Zipf-распределением.
  • run_workload.py — скрипт нагрузочного тестирования (сбор метрик).
  • analyze.py — скрипт анализа и построения графика.
  • comparison.png — график сравнения hit rate LRU vs LFU.
  • REPORT.md — отчёт с выводами.

Содержание отчёта

# A/B тест: LFU vs LRU в Redis cluster

## Конфигурация
- 3 мастера, 3 реплики, maxmemory 256MB
- Ключей: 100 000, значение ~256 Б
- Нагрузка: 500 000 GET с Zipf(1.2)

## Результаты
| Политика | Hit rate | Evicted keys |
|----------|----------|--------------|
| allkeys-lru | 78.3% | 45 000 |
| allkeys-lfu | 85.1% | 42 000 |

Дополнительно (опционально):

  • Скрипт для автоматического развёртывания кластера с одной командой (make deploy).
  • Dockerfile для образа с предустановленными зависимостями.

7. Возможные сложности и их решение

СложностьРешение
Кластер не создаётся — ошибка ERR Invalid node addressУбедиться, что в docker-compose.yml используются внутренние IP (или 127.0.0.1 на хосте). Для redis-cli --cluster create нужно указывать адреса, доступные внутри сети Docker. Использовать имена контейнеров (например, redis-1:7001).
maxmemory не достигается — вытеснения не происходятУменьшить maxmemory до 128 MB или меньше, или увеличить количество данных (200 000 ключей).
Политика LFU не срабатывает, как ожидалосьПроверить, что используется Redis 7.2+ (в ранних версиях LFU был экспериментальным). Для allkeys-lfu должен работать все ключи.
Hit rate LFU не выше LRUВозможно, паттерн доступа слишком «случайный» — уменьшить skew параметр Zipf (сделать s больше, например, 1.5). Убедиться, что workload одинаков для обоих тестов.
Redis-py с кластером падает с MovedErrorИспользовать RedisCluster из redis.cluster, а не стандартный StrictRedis.

8. Бюджет времени (оценка)

ЭтапВремя
Этап 1: Развёртывание Redis cluster45 мин
Этап 2: Генерация тестовых данных30 мин
Этап 3: A/B тест (LRU + LFU)1 час
Этап 4: Анализ и графики30 мин
Этап 5: Документация15 мин
Итого3 часа

Примечание При первом выполнении заложите дополнительно 30–60 мин на отладку Docker-сети и понимание принципов Redis cluster.


9. Связанные вопросы из базы знаний

ВопросТема
101Как настроить политику вытеснения в Redis?
205Разница между LRU и LFU
312Как собрать метрики Redis через INFO?
408Docker Compose для Redis cluster
555Нагрузочное тестирование Redis (redis-benchmark vs custom)
620Что такое Zipf distribution и зачем оно нужно?
701Redis cluster vs standalone: плюсы и минусы
810Как работает approximate LFU в Redis?
888A/B тестирование в инфраструктуре
250Настройка maxmemory в Redis

10. Чек-лист самопроверки

  • Я развернул Redis cluster из 3 мастеров и 3 реплик в Docker.
  • Я настроил maxmemory и политику allkeys-lfu на всех мастерах.
  • Я сгенерировал 100 000 ключей с Zipf-распределением и подготовил идентичный workload.
  • Я провёл нагрузочный тест сначала с allkeys-lru, затем (после сброса и перезаполнения) с allkeys-lfu.
  • Я зафиксировал метрики keyspace_hits, keyspace_misses, evicted_keys до и после каждого прогона.
  • Я сравнил hit rate и убедился, что LFU показал результат выше хотя бы на 5%.
  • Я построил график сравнения и сохранил его.
  • Я написал отчёт REPORT.md с описанием конфигурации и выводами.