Redis maxmemory: почему прод-инстанс падает по памяти
maxmemory задаёт потолок, от которого инстанс отсчитывает всю дальнейшую логику: при каждой команде, выделяющей память, он сверяет текущий объём с лимитом и, если запас исчерпан, переходит к политике вытеснения. Сложность в том, что в лимит входит не только ваша модель данных: служебные структуры, оверхед аллокатора, фрагментация и буферы репликации съедают заметную долю, поэтому инстанс подходит к границе раньше, чем показывал расчёт по объёму ключей.
Первым ломается запись. При политике без вытеснения команды, изменяющие данные, начинают возвращать ошибку записи при переполнении, тогда как чтения продолжают обслуживаться — приложение выглядит живым, а часть операций молча теряется, если код не обрабатывает отказ. Именно поэтому переполнение памяти redis находят не по графику мониторинга, а по расхождению данных между сервисами.
Второй по очереди страдает репликация: растущий буфер отставания, разрыв соединения с репликой и полная повторная синхронизация, которая нагружает основной узел ровно в момент, когда ему и так тяжело. Параллельно приходит деградация отклика под нагрузкой — форк под фоновое сохранение и вытесненные в своп страницы растягивают обработку запросов, а таймауты клиентов множатся каскадом.
Отсюда вывод: лимит и политику считают по профилю доступа — какие ключи кэшируемые, какие обязаны жить до явного удаления, какие имеют срок жизни. Дальше разбираем, как измерять реальный оверхед и подбирать вытеснение так, чтобы отказ записи был осознанным решением, а не аварией в ночь релиза.
Что даёт настроенная политика вытеснения ключей
Явно выставленный предел памяти превращает поведение хранилища из «как получится» в измеримое. В Redis за это отвечают две настройки: предельный объём и политика вытеснения ключей. Пока порог не задан, потребление растёт вместе с нагрузкой и упирается в лимит операционной системы — тогда падает весь процесс вместе с очередями и блокировками, а не отдельные ключи.
Выбранная политика переводит этот сценарий в управляемый: место освобождается точечно, по заранее понятному правилу. Каждое внедрение проверяем по одному и тому же списку.
- Стабильная память инстанса — потребление держится в заданной границе с запасом под фрагментацию и служебные копии; реестр больше не растёт ступенями до аварийного завершения.
- Предсказуемое время отклика — на пиках задержки растут в разы меньше: освобождение памяти перестаёт быть событием, которое замечает пользователь.
- Отсутствие отказов записи — уходит целый класс ошибок «запись запрещена», когда приложение получает отказ на каждую операцию после исчерпания памяти.
- Осознанность вытеснения — под удаление попадают ключи, которые действительно можно потерять, а не данные, ради которых хранилище и держат.
- Устойчивость копий — резерв под служебные копии заложен заранее, поэтому рост нагрузки не приводит к рассинхронизации узлов.
- Наблюдаемость — счётчики вытесненных ключей и доля попаданий показывают, что граница подобрана по нагрузке, а не на глаз.
Варианты ограничения памяти в Redis maxmemory
maxmemory задаёт верхнюю границу памяти инстанса, но сам по себе ничего не решает: как только лимит достигнут, поведение определяется политикой вытеснения. Выбор политики — это по сути выбор между отказом в записи и потерей части данных, и он должен совпадать с тем, чем для приложения являются данные в Redis.
| Вариант | Когда подходит | Чем ограничивает |
|---|---|---|
noeviction |
Кэш подчинён данным: очереди, счётчики, блокировки — потеря ключа недопустима | Запись падает с ошибкой, чтение продолжает работать; приложение обязано корректно деградировать |
allkeys-lru / allkeys-lfu |
Чистый кэш, любой ключ восстановим из источника | Вытесняет и горячие ключи при всплеске — промахи идут пачками |
volatile-lru |
Смешанные данные: часть ключей с TTL, часть постоянные | Освобождает только ключи с истечением; если их доля мала — фактически превращается в noeviction |
volatile-ttl |
Сессии, временные токены, лимиты — важен порядок дожития | Работает исключительно по TTL; ключи без истечения память не освобождают |
allkeys-random |
Однородная нагрузка без выраженных горячих ключей | Не учитывает частоту обращений, доля промахов непредсказуема |
Практический момент: maxmemory ставят ниже лимита контейнера или инстанса, оставляя запас на служебные расходы и фоновые операции — иначе вытеснение сработает позже, чем закончится память у процесса. Смена политики применяется на лету, но оценивать её нужно по метрикам вытесненных ключей и доле попаданий, а не по одному факту отсутствия ошибок.
Компромисс здесь всегда один: чем агрессивнее режим, тем реже отказы в записи и тем выше доля промахов. Разбираем, как подобрать этот баланс под конкретный профиль нагрузки.
Порядок внедрения лимитов памяти в продакшене
Лимит памяти — не одно значение в конфиге, а решение, которое затрагивает политику вытеснения, поведение реплик и запас по отказу. Порядок ниже выстроен так, чтобы сначала понять профиль данных, затем поставить границу и только потом проверять её на живом трафике.
-
Аудит потребления памяти. Снимаем used_memory, фрагментацию и суточную динамику, отдельно — пик нагрузки. Так находятся ключи-тяжеловесы, из-за которых расход растёт быстрее трафика.
-
Профиль ключей и сроков жизни. Разбираем пространство по префиксам: объём, количество, наличие TTL, что здесь источник данных, а что кэш. Видно, что допустимо вытеснять, а что обязано дожить до сохранения.
-
Расчёт границы. Отталкиваемся от профиля, а не от свободной памяти на хосте: оставляем запас на фрагментацию, фоновое сохранение и репликацию. Политику подбираем по классу данных — allkeys-lru для кэша, volatile-* там, где TTL выставлен осознанно, noeviction для очередей и сессий.
-
Настройка продакшен-инстанса. Значение и политику фиксируем в конфигурации, а не разовой командой: иначе первый же перезапуск вернёт прежнее поведение. Проверяем, что настройка применилась на всех узлах, включая реплики.
-
Проверка под нагрузкой. Воспроизводим сценарий, который выбивает память за границу, и смотрим на число вытесненных ключей, долю попаданий и отказы записи. Заметная деградация — повод вернуться к расчёту, а не чистить вручную по факту.
-
Наблюдение и передача регламентов. Оповещения на долю вытеснения, рост фрагментации и падение попаданий ловят неверную границу раньше инцидента. Вместе с регламентом отдаём порядок действий при росте данных — чтобы следующий релиз не потребовал повторного разбора.
Конфигурации, схемы и регламенты после внедрения
После внедрения вы получаете не набор разрозненных команд, а комплект, по которому ваш администратор или дежурный инженер сможет повторить конфигурацию с нуля, понять поведение Redis под нагрузкой и безопасно откатить изменение. Каждый артефакт версионируется в репозитории рядом с кодом приложения — правки проходят ревью, а не вносятся руками на работающем узле.
- Файлы конфигурации redis: зафиксированные
maxmemoryи политика вытеснения под профиль нагрузки, режим сохранения на диск, лимиты клиентских соединений и таймауты. - Схема репликации и отказоустойчивости: топология «ведущий — реплики», правила автоматического переключения, кворум, размещение по зонам доступности.
- Политики доступа и роли: отдельные учётные записи для приложения, мониторинга и администратора, права на изменение параметров и на служебные команды, ротация учётных данных.
- Панели мониторинга и правила оповещения: заполнение памяти, доля вытесненных ключей, доля промахов кэша, отставание реплик, пороги для ночного дежурства.
- Регламент для дежурной смены: что проверять при росте потребления памяти, на каком шаге эскалировать, какие команды безопасны на работающем узле.
- План на случай роста: критерии перехода на несколько узлов, порядок переноса ключей, проверка, что приложение переживает смену топологии.
- Инструкция по откату: снимок состояния, порядок возврата предыдущего профиля, прогон восстановления на тестовом контуре.
Комплект закрывает типовой сценарий: память растёт, вытеснение начинает выбрасывать нужные ключи, база отвечает медленнее. Покажем, где это ловится заранее, а где — уже поздно.
Кейс: инстанс перестал падать при пиковой нагрузке
Инстанс Redis в продакшене держал корзины, сессии и справочник товаров — и падал каждую распродажу. Лимит памяти не был выставлен, поэтому процесс доходил до потолка хоста и дальше в дело вступал системный убийца процессов: инстанс перезапускался, кэш остывал, а запросы первых минут после старта ложились на базу.
Цена проявлялась не только в падении. На пике около 6% записей — добавление в корзину, обновление счётчика акции — получали ошибку памяти, и часть заказов собирали заново вручную. В ночном потоке заказов отклик проседал в 4-5 раз: очередь запросов к базе росла быстрее, чем инстанс успевал отвечать.
Что изменили
Данные развели по классам и политикам вытеснения: для кэша справочника включили allkeys-lru, для сессий — volatile-ttl, а критичные записи получили лимит только по времени жизни ключа, чтобы вытеснение не задевало активные заказы. Лимит памяти задали с запасом под внутреннюю фрагментацию, а не в потолок, плюс добавили наблюдение за объёмом вытесняемых ключей и долей попаданий в кэш — алерт срабатывает не по факту падения, а раньше.
Что стало
Отказы записи на пике — 0 вместо 6%: ни один заказ не потерялся. Отклик на 95-м процентиле вернулся к уровню спокойного дня, просадка в 4-5 раз ушла. Вытеснение идёт, в часы пик освобождается до пятой части объёма, но под него попадают справочники и истёкшие корзины, а не активные заказы.
Ночной наплыв перестал быть отдельным сценарием риска: инстанс держит поток без перезапусков, кэш остаётся тёплым, а поведение при росте нагрузки видно по метрикам, а не по интуиции дежурного.
Технические сомнения по вытеснению и персистентности
Сомнения вокруг вытеснения почти всегда одни и те же: не пропадут ли нужные данные, не сломает ли политика снимки на диск, можно ли переключить режим на работающем контуре. Ниже — короткие ответы, которые чаще всего снимают вопрос до начала работ.
Вытеснение уничтожит горячие ключи?
Политика вытесняет только то, что ей разрешено настройкой. При volatile-* под удаление попадают исключительно ключи со сроком жизни — постоянные данные не трогаются вообще. Если волатилен весь набор, мы размечаем критичные ключи без TTL и выносим их в отдельный экземпляр, а долю вытесненных ключей держим под наблюдением: всплеск виден сразу, а не после жалоб.
Вытеснение конфликтует со снимками на диск?
Нет. Снимок фиксирует состояние на момент записи, вытеснение идёт после него. При включённом журнале изменений туда попадают и удаления по политике — восстановление даст ровно тот набор, что был в памяти. Проверяем только, чтобы запись файла не совпала с пиком нагрузки: окна разносим и держим запас по диску.
Режим можно сменить без простоя?
Да. Смена политики применяется на живом экземпляре и действует для последующих вытеснений. Делаем это в окно низкой нагрузки, предварительно сняв снимок и прогнав сценарий на копии. Откат — тем же способом, за секунды, без перезапуска и потери соединений.
Обсудим задачу и вернёмся с оценкой и планом
Чтобы обсудить задачу настройки предметно, достаточно нескольких вводных по инфраструктуре: как сейчас выставлен maxmemory, какая политика вытеснения действует, есть ли репликация или кластер и как выглядит профиль нагрузки на инстанс. Этого хватает, чтобы понять класс задачи — разовая правка параметров или пересмотр модели хранения с разделением ключей по срокам жизни.
На созвоне уточняем то, чего не видно в конфигурации: что происходит при переполнении памяти сейчас, какие данные допустимо терять при вытеснении, а какие обязаны пережить пик записи. Отдельно проговариваем окно на изменения и способ проверки результата на стенде до вывода в боевой контур.
Возвращаемся с конкретным набором:
- целевым значением
maxmemoryи обоснованием политики вытеснения под ваш профиль ключей; - разбором сценариев отказа: где теряются данные, а где инстанс переходит в отказ записи вместо тихого вытеснения;
- планом работ по этапам с точками проверки и порядком выката;
- требованиями к мониторингу: метрики вытеснения, доля промахов, рост рабочего набора;
- распределением зон ответственности — что делаем мы, а что остаётся на вашей стороне: доступы, окно, регламент дежурств;
- чек-листом приёмки, по которому вы принимаете результат.
Опишите задачу в двух-трёх абзацах или пришлите текущий конфиг — вернёмся с оценкой, планом работ и явными зонами ответственности.
Чем отличается вытеснение по сроку жизни от вытеснения по объёму?
maxmemory задаёт жёсткий потолок памяти, а maxmemory-policy определяет, что произойдёт при его достижении. Политики делятся на два класса: вытесняющие только ключи со сроком жизни (volatile-lru, volatile-lfu, volatile-ttl) и готовые забрать любой ключ (allkeys-lru, allkeys-lfu). Разница между ними не в скорости освобождения памяти, а в том, какие данные вы теряете.
Вытеснение по сроку жизни снимает в первую очередь то, что и так должно было истечь, — у ключей с TTL самый предсказуемый профиль потерь. Если в базе рядом живут кэш и данные без срока жизни, режим volatile-* гарантирует: под давлением уйдёт кэш, а не справочник или счётчик. Обратная сторона — когда ключи с TTL закончились, а память всё ещё нужна, Redis начинает отклонять запись, и приложение должно быть к этому готово.
Вытеснение по объёму различий не делает: под нож идёт любой ключ, включая записанный без срока жизни. Для чистого кэша это самый простой вариант — потери незаметны, потому что значение всегда восстанавливается из источника. Для очередей и фоновых заданий это класс отказов, который не лечится повтором: вытесненное задание никто не вернёт, и оно исчезает без следа.
Поэтому очередь и кэш разводят по разным базам или экземплярам. Для очередей ставят noeviction и следят за свободной памятью, для кэша — allkeys-lru с запасом по объёму. Контроль держится на метрике вытесненных ключей и доле ключей без срока жизни: если она растёт в базе очередей, режим выбран неверно.







