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 із запасом за обсягом.
Контроль тримається на метриці витіснених ключів і частці ключів без строку життя: якщо вона зростає в базі черг, режим вибрано неправильно.







