Добавить в корзинуПозвонить
Найти в Дзене
Цифровая Переплавка

🚫 Redis как rate limiter: мифы, ошибки и реальность

Redis — это быстрый, популярный, удобный инструмент для кэширования и хранения данных в памяти. Но когда его пытаются приспособить для ограничения частоты запросов (rate limiting), он внезапно превращается в источник неожиданных багов, гонок и утечек памяти. На первый взгляд всё просто: пара команд SET + INCR или хитрое именование ключей по времени, и вот у нас уже готов «идеальный» ограничитель. Но под капотом — хаос. Из статьи Андрея Миллера следует, что любые «простые» схемы в Redis быстро ломаются. Вот основные ловушки: Да, Lua в Redis решает часть проблем: вся логика выполняется на сервере, гонки исчезают. Но мы приходим к абсурду — используем Redis как контейнер для куска кода, который прекрасно мог бы жить в приложении или в специализированном rate limiting-сервисе. При этом: Мне кажется, основная причина, почему компании идут по этому пути, — это инфраструктурная инерция. Redis уже есть в проекте, он быстрый, знакомый, а до продакшн-проблем далеко. На тестах всё «летает», но в
Оглавление
Изометрическая сцена с логотипом Redis и индикатором ограничения запросов, рядом монитор с ошибкой и разорванным кабелем — визуальный символ ненадёжности Redis для rate limiting.
Изометрическая сцена с логотипом Redis и индикатором ограничения запросов, рядом монитор с ошибкой и разорванным кабелем — визуальный символ ненадёжности Redis для rate limiting.

Redis — это быстрый, популярный, удобный инструмент для кэширования и хранения данных в памяти. Но когда его пытаются приспособить для ограничения частоты запросов (rate limiting), он внезапно превращается в источник неожиданных багов, гонок и утечек памяти.

На первый взгляд всё просто: пара команд SET + INCR или хитрое именование ключей по времени, и вот у нас уже готов «идеальный» ограничитель. Но под капотом — хаос.

⚙️ Проблемы, о которых молчит документация

Из статьи Андрея Миллера следует, что любые «простые» схемы в Redis быстро ломаются. Вот основные ловушки:

  • 🕒 Гонка между установкой TTL и инкрементом
    При фиксированном окне (SET NX EX + INCR) ключ может истечь
    до инкремента, и тогда он создаётся без срока жизни. Так мы получаем «вечный счётчик» и неконтролируемый рост значения.
  • Сбой EXPIRE
    При подходе с ключами, завязанными на время (rate_123456), если команда EXPIRE не дойдёт до сервера (сбой сети, перегрузка), ключ останется навсегда. Через час это может дать удвоенный лимит на целое окно.
  • Расхождение часов
    Даже при NTP-синхронизации у разных клиентов могут быть десятки миллисекунд расхождения. Для API с высокой нагрузкой это значит, что часть запросов «провалится» в соседнее окно и обойдёт лимит.
  • 🪣 Сложные алгоритмы
    Токен-бакет или скользящее окно требуют нескольких переменных и атомарных операций. На чистых командах Redis их реализовать либо невозможно без гонок, либо придётся писать Lua-скрипты, фактически превращая Redis в исполняющую среду.

🧪 Почему Lua-скрипт не спасает

Да, Lua в Redis решает часть проблем: вся логика выполняется на сервере, гонки исчезают. Но мы приходим к абсурду — используем Redis как контейнер для куска кода, который прекрасно мог бы жить в приложении или в специализированном rate limiting-сервисе. При этом:

  • 🚫 Теряем преимущества простоты Redis.
  • 📉 Ограничиваем масштабируемость (кластерные конфигурации сложны и часто не поддерживают такие транзакции).
  • 🔒 «Подвешиваем» всю базу на время исполнения скрипта.

🧐 Личный взгляд: зачем вообще пытаться

Мне кажется, основная причина, почему компании идут по этому пути, — это инфраструктурная инерция. Redis уже есть в проекте, он быстрый, знакомый, а до продакшн-проблем далеко. На тестах всё «летает», но в бою появляются редкие, сложно воспроизводимые баги — а чинить уже поздно.

Вместо этого разумнее:

  • 🏗 Использовать специализированные решения (Envoy, NGINX with limit_req, Kong, API Gateway).
  • 📍 Если нужен распределённый rate limiting, хранить счётчики в атомарных KV-хранилищах, спроектированных для этого (например, Aerospike, Tarantool, или даже Postgres с SELECT FOR UPDATE).
  • 🎯 Если всё-таки Redis, то только со 100% серверной логикой (Lua) и учётом кластерных ограничений.

📌 Вывод

Redis — отличный инструмент, но не волшебная палочка. В роли rate limiter’а он часто даёт ложное ощущение надёжности, скрывая проблемы до того момента, когда от них уже сложно избавиться. Как и в любой инженерной задаче, выбор инструмента должен исходить из архитектуры и требований, а не из того, что «он у нас уже есть».

📚 Источники: