Программа курса

Модуль 05 / Урок 21 из 27

Пайплайны и сетевые оптимизации

Теория и практика

Часто Redis «медленный» не из-за самого Redis, а из-за тысяч сетевых round-trip'ов. Пайплайны и батчирование снимают это ограничение в разы.

Почему каждый запрос — это RTT

Без пайплайна цикл такой: отправить команду → ждать ответ → отправить следующую. При 0.5 мс RTT и 1000 команд это 0.5 секунды чистого ожидания, хотя сам Redis исполнил команды за микросекунды. Пайплайн отправляет пачку команд, не дожидаясь ответов, и читает ответы одним заходом:

# Псевдокод: 1000 GET одним round-trip'ом
pipe = redis.pipeline(transaction=False)
for key in keys:
    pipe.get(key)
results = pipe.execute()

Пайплайн ≠ транзакция

Команды пайплайна могут исполняться вперемешку с командами других клиентов. Пайплайн — только сетевая оптимизация; атомарность — это MULTI/EXEC или Lua (урок 5.3).

Размер батча

  • Практический диапазон: 100–1000 команд на батч. Гигантский пайплайн накапливает ответы в буферах обеих сторон (следите за client output-buffer);
  • Для чистых чтений одной команды лучше MGET/MSET — одна команда вместо N;
  • Смешанные операции — пайплайн.

Батчирование и структуры данных

Анти-паттернРешение
1000 × GET в циклеMGET или пайплайн
1000 × SET в циклеMSET или пайплайн
HGET поля по одномуHMGET всех нужных полей
10 × SADD по одномуодин SADD с 10 элементами
ZADD по одномуZADD с пачкой пар score-member

Клиентская гигиена

  • Пулы соединений: создание соединения — дорогая операция; переиспользуйте;
  • Таймауты: всегда задавайте connect/operation timeouts — зависший сокет без таймаута уронит пул потоков приложения;
  • Обработка ошибок: OOM (noeviction), NOAUTH, MOVED, READONLY — клиент должен уметь ретраить/переподключаться с бэкоффом, а не сыпать стек-трейсами;
  • Десериализация: тяжёлый JSON-парсинг на стороне приложения часто дороже самого GET — считайте весь путь;
  • Сжатие: для значений > 1 КБ сжимайте (gzip/snappy) — экономия трафика и памяти в 3–10 раз.

Серверные параметры сети

# redis.conf
tcp-backlog 511          # очередь приёма соединений
timeout 0                # не рвать idle-соединения (пул сам следит)
tcp-keepalive 300
maxclients 10000         # и ulimit -n соответствующий

Практика

  1. Замерьте 1000 GET: по одному, MGET, пайплайном по 100. Зафиксируйте три цифры и объясните разницу.
  2. Поднимите RTT: доступ к Redis через сеть с 20 мс latency (docker в другой сети/simulate) — перепроверьте, что пайплайн спасает именно там, где RTT большой.
  3. Добавьте в свой клиентский слой пул соединений + таймауты + ретраи с джиттером; напишите тест «Redis недоступен 2 секунды» — приложение должно пережить без каскадного падения.
Мини-квиз

1. Зачем MGET, если есть пайплайн? — Одна команда вместо N: меньше парсинга, буферов и сетевых пакетов.

2. Гарантирует ли пайплайн атомарность? — Нет; только батч-отправку. Атомарность — MULTI/EXEC или Lua.

3. Первая причина «Redis тормозит из приложения, но redis-benchmark быстрый»? — Множественные RTT без батчирования/пайплайна, отсутствие пула соединений.