Чому база впирається в пам'ять: роль Huge Pages Linux
Поки робочий набір бази міститься в пам'яті та кеші процесора, різниця між звичайною і великою сторінкою не видна.
Як тільки обсяг даних переростає кеш, кожен промах таблиці сторінок перетворюється на обхід кількох рівнів — і це вже не одиниці тактів на один запит, а тисячі на кожен повний прохід по індексу.
Запити починають вишиковуватися в чергу не через нестачу ресурсу, а через те, як цей ресурс нарізано.
За замовчуванням ядро ділить пам'ять сторінками по 4 КБ.
Для загального випадку це зручно, але для сервера бази даних означає інше: таблиця сторінок зростає разом із буферним пулом, її записи постійно промахуються повз кеш трансляції адрес, а накладні витрати на керування пам'яттю з'їдають процесорний час, який має йти на самі дані.
Сюди додається фрагментація пам'яті ядра. За місяці роботи вільні ділянки розповзаються, зібрати безперервний регіон під великий буфер усе важче, і ядро витрачає цикли на ущільнення замість обслуговування запитів. Перезапуск служби допомагає на час — сама схема розбиття пам'яті не змінюється.
На графіках це виглядає як зростання частки системного часу й черг на сторінках. Зростання затримок запитів стартує задовго до того, як пам'ять закінчиться: замовник бачить не відмову, а «на піку стало повільніше». Це незручний клас деградації — його зазвичай списують на навантаження й терплять місяцями.
Huge Pages Linux змінюють масштаб задачі: одна сторінка закриває ділянку, яка раніше вимагала сотень записів, таблиця сторінок стискається, промахи трапляються рідше, а ядро перестає розриватися між обслуговуванням запитів і керуванням пам'яттю. Далі розбираємо, як це влаштовано і в яких сценаріях дає вимірюваний ефект.
Що дає перехід на великі сторінки: вимірюваний ефект
Великі сторінки прибирають роботу, якої в базі бути не повинно. Коли PostgreSQL, Oracle або Redis тримають робочий набір у спільній пам'яті, ядро роздає її дрібними сторінками — і майже кожне звернення змушує процесор шукати запис у таблиці сторінок. На гарячому шляху це помітна частка тактів, що йде не на запити, а на перерахунок адрес.
Перехід на великі сторінки змінює масштаб: один запис у буфері трансляції покриває в сотні разів більше пам'яті, і промахи за ним падають на порядок.
Це видно за трьома величинами — навантаження на процесор ядра за того самого запиту нижче, час відгуку бази перестає тремтіти на піку, пропускна здатність зростає без додавання заліза.
Найсильніше ефект там, де даних у пам'яті багато й звернення до них випадкові: аналітика, індекси, кеш сесій.
Другий результат — усунений клас відмов пам'яті. Безперервний сегмент під спільний буфер виділяється під час старту, і якщо пам'ять на цей момент фрагментована, сервіс або не піднімається, або падає пізніше, під час зростання навантаження.
Заздалегідь зарезервовані великі сторінки виділяються детерміновано, тому вартість простою перестає залежати від того, як давно працює хост.
Ефект перевіряємо, а не приймаємо на віру: знімаємо профіль до і після — час відгуку під піком, завантаження ядер, кількість промахів трансляції та поведінка бази під час рестарту.
Режими Huge Pages Linux: явне резервування та прозорий режим
У Linux за підтримку великих сторінок відповідають два незалежні механізми: явне резервування та прозоре об'єднання. Вони по-різному виділяють пам'ять і по-різному поводяться, коли її не вистачає, — тому вибір режиму прямо впливає на стабільність СУБД під навантаженням.
Явне резервування (hugetlbfs) закріплює пул сторінок заздалегідь, параметрами ядра, і утримує його весь час роботи. СУБД бачать уже готові сторінки, розмір пулу передбачуваний і не залежить від поточного стану пам'яті.
Плата за це — пул потрібно розрахувати під пік, а його вичерпання означає відмову розміщення, а не тиху деградацію.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Явне резервування | СУБД і сервіси з довгоживучою спільною пам'яттю: PostgreSQL, Oracle, Redis | Пул фіксується заздалегідь; перерахунок потребує перезапуску сервісу |
| Прозоре об'єднання | Загальне навантаження, розрахунки, короткоживучі процеси | Немає гарантії виділення: фонова дефрагментація може не встигнути, повернення пам'яті затримується |
| Гібрид: прозорий увімкнено, явний — точково | Змішані вузли, де база — не єдиний споживач | Потрібно стежити, щоб фон не витіснив зарезервоване |
Прозоре об'єднання не потребує ручного налаштування: ядро саме збирає великі сторінки з дрібних. Але виділення відбувається за можливістю, а не на вимогу — для довгоживучого процесу бази це означає непередбачуваний розмір робочого набору.
Тому для СУБД обирають явне резервування: лише воно дає детермінований розмір і рівну затримку. Прозорий режим залишають для інших процесів або вимикають на вузлах, де база — головний споживач пам'яті.
Як ми впроваджуємо великі сторінки: етапи інженерної роботи
Порядок робіт будуємо так, щоб пам'ять не забирали в працюючого сервісу і щоб відкат був можливий на кожному кроці.
Спочатку аудит конфігурації: знімаємо Hugepagesize і лічильники HugePages_Total/Free з /proc/meminfo, дивимося режим прозорих сторінок, ліміти процесів, розподіл за вузлами пам'яті та фактичний розмір робочих наборів бази або кешу.
- Аудит конфігурації. Фіксуємо поточне споживання, частку промахів за буфером трансляції адрес і запас вільної пам'яті — без цього розрахунок сторінок буде навмання.
- Розрахунок резерву. Рахуємо число сторінок під піковий робочий набір із запасом на зростання і окремо — під службові процеси, щоб після увімкнення резерву система не пішла у своп.
- Налаштування ядра. Прописуємо резерв сторінок, вимикаємо або переводимо в керований режим прозорі сторінки, закріплюємо параметри в завантажувачі та системних юнітах, щоб вони переживали перезавантаження.
- Пробне увімкнення. Піднімаємо на одному вузлі або репліці, перевіряємо, що сервіс підхопив сторінки, а фрагментація пам'яті не заважає їх виділити після довгого аптайму.
- Перевірка під навантаженням. Порівнюємо з базовим заміром: час відгуку під піком, поведінка кешу, стабільність затримок, відсутність перезапусків і зростання споживання під час тривалої роботи.
- Спостережуваність. Налаштовуємо збір лічильників сторінок і оповіщення про нестачу — інакше проблема проявиться як раптова відмова під час наступного старту сервісу.
- Передача супроводу. Віддаємо вашій команді конфігурації, схему розміщення, інструкцію з планового перезапуску та чек-лист діагностики.
Параметри на кожному кроці узгоджуємо з вашими інженерами: зміни в резерві пам'яті зачіпають усі сервіси вузла, тому впроваджуємо їх поетапно, зі збереженням можливості повернутися до попередньої конфігурації.
Що залишається у вашої команди після впровадження
Налаштування ми не залишаємо у вигляді усних домовленостей: кожен параметр Huge Pages фіксується разом із причиною, з якої його обрано.
Нижче — комплект артефактів, за яким ваші інженери зможуть обслуговувати конфігурацію самостійно та повторювати її на нових вузлах Linux без нашої участі.
- Комплект документації — значення параметрів ядра й СУБД з обґрунтуванням: чому обрано такий розмір резерву і що зміниться, якщо його зменшити.
- Схема розміщення пам'яті — карта вузлів: скільки сторінок закріплено за кожним інстансом, де залишено запас, як пам'ять ділиться під час перезапуску сусідніх сервісів.
- Регламент обслуговування — порядок перевірки резерву після оновлення ядра та перезавантаження, контроль фрагментації й витоків закріплених сторінок за розкладом.
- Політики доступу — хто має право змінювати параметри, через яку систему керування конфігураціями, які правки потребують узгодження і як ведеться аудит.
- Інструкція з відкату — покрокове повернення до звичайних сторінок: які сервіси зупиняються, у якому порядку, як переконатися, що показники повернулися до вихідних.
- Перелік перевірок нового вузла — що проганяється до введення в роботу: резерв виділено, СУБД його бачить, метрики в нормі.
Артефакти оформлені так, як ними реально користуються: готові команди, шаблони конфігурацій, історія змін. Обслуговування ви ведете самі — ми долучаємося лише до нових завдань.
Кейс: як скоротили затримки запитів на навантаженій базі
Бойовий контур — сервіс із базою на PostgreSQL, де рівномірне навантаження раз на добу переходить у короткий, але жорсткий пік.
До робіт хвостові затримки запитів на цьому піку виходили за цільовий поріг, а резидентна пам'ять процесу зростала ступенями й не поверталася до вихідної після спаду трафіку.
Заміри знімали на бойовому контурі, не зупиняючи сервіс: розподіл затримок за перцентилями, споживання пам'яті, частоту промахів під час трансляції адрес і обсяг службових структур під спільні сторінки.
Окремо зафіксували профіль саме під піковим навантаженням — там деградація проявлялася найпомітніше, а в спокійні години її майже не було видно.
Спільні структури перевели на великі сторінки та зарезервували пул на старті процесу, щоб він не дробився під час роботи.
У регламент запуску додали перевірку доступного пулу, а в моніторинг — сигнал про його вичерпання: без цього виграш зникає після першого ж перезавантаження вузла.
Хвостові затримки на піку скоротилися приблизно втричі, частка запитів, що виходять за цільовий поріг, упала з 4% до 0,3%. Процес перестав виходити за заздалегідь зарезервований обсяг, пік проходить без ступінчастого зростання пам'яті та без позапланових перезапусків.
Заміри повторювали на тому самому навантаженні й з тим самим профілем запитів, тому цифри зіставні, а не вирвані з зручного вікна. Відкат тут — правка конфігурації та перезапуск, тому такий прогін можна спочатку провести на копії вашого контуру й звірити з поточними замірами.
Що робити, якщо великі сторінки вже частково налаштовані?
Частково налаштований контур — це коли великі сторінки вже виділені, але не узгоджені між собою: частина параметрів виставлена вручну в працюючій системі й живе до наступного завантаження, частина описана в профілі завантажувача, а розмір пулу задано в налаштуваннях СУБД.
Перевірка поточного стану починається з /proc/meminfo: зіставляємо загальний, зарезервований і вільний обсяг сторінок — розрив між ними одразу показує, яке виділення простоює й до процесу не доходить.
Друге типове джерело — конфлікт параметрів.
СУБД переходить на великі сторінки, лише якщо обсяг під її буфери повністю покритий виділеними сторінками й знято ліміт на блоковану пам'ять процесу; за неповного збігу сервіс або мовчки піднімається на базових сторінках, або не стартує взагалі.
Окрема пастка — прозорі великі сторінки ядра: під навантаженням вони перекроюють розкладку, і ручне виділення втрачає сенс, тому їх вимикають явно.
Далі наводимо лад: залишаємо одне джерело істини для параметрів завантаження й системних лімітів, прибираємо ручні підстроювання з автозапуску, приводимо обсяг виділення до розміру пулів із запасом на репліки та службові процеси.
Перехід без простою будуємо почергово: спочатку налаштовуємо й перевіряємо на репліці, переводимо роль основного вузла, потім повторюємо те саме на колишньому основному. Крок із перезавантаженням залишається, але доступність застосунку не переривається.
Після перемикання повертаємося до перевірки: дивимося частку фактично використовуваних великих сторінок і відмови виділення в журналі ядра. Якщо лічильник відмов зростає, обсяг або ліміти підібрані неправильно, і правку потрібно повторювати до стабільного результату.
Технічні сумніви: відповідаємо на часті запитання
Впровадження Huge Pages змінює спосіб виділення пам'яті на рівні ядра: база даних отримує сторінки великого розміру замість набору малих.
Тому технічні сумніви виникають раніше за проблеми — інженер хоче розуміти, де межа ризику: що буде з репліками й кластером, як швидко повернутися назад, якщо профіль навантаження виявиться іншим.
Правка локальна для вузла: передавання змін між первинним вузлом і реплікою йде окремим потоком і від розміру сторінок не залежить. Нижче — те, що найчастіше випливає на узгодженні.
Чи не розійдеться поведінка первинного вузла та реплік?
Ні, якщо розмір сторінок задано однаково на всіх вузлах. Окремої уваги потребує спільна пам'ять процесу: вона виділяється цілісним блоком, і за різного розміру сторінок спільний сегмент на репліці не підніметься.
Тому налаштування розгортається на кластер за одне вікно, а не по одному вузлу — тоді вплив на реплікацію нульовий.
Як виглядає відкат, якщо щось піде не так?
Відкат дворівневий. Параметр ядра повертається до попереднього значення й застосовується після перезавантаження вузла, налаштування самої бази — до базової лінії, знятої до впровадження.
Обидві конфігурації ми тримаємо робочими й перевіреними, тому повернення не потребує перезбирання: це керована операція в відомому вікні.
Які ризики впровадження закладати заздалегідь?
Головний — нестача безперервної фізичної пам'яті: якщо резервування не зроблено під час старту, частину сторінок виділити не вдасться і вузол повернеться до звичайного режиму. Другий — помилка в розрахунку резерву, коли він витісняє інші процеси.
Обидва знімаються заміром на копії бойового вузла під реальним навантаженням: дивимося затримку запитів, частку часу в системних викликах і стабільність буферів. Розгортання в бойовий контур іде лише після цього і з планом відкату на руках.
Обговоримо завдання: що уточнимо і як почнемо роботу
Старт — це одна розмова по суті: ви описуєте контур і завдання, ми повертаємося з планом робіт і оцінкою. Щоб не з'ясовувати ввідні дані по ходу, фіксуємо їх одразу — від цього залежить, які налаштування пам'яті взагалі має сенс чіпати.
- профіль навантаження: що переважає — короткі транзакції, аналітичні вибірки чи звернення до кешу, і на яких піках помітна деградація;
- стан пам'яті хоста: чи увімкнено прозорий режим huge pages, які параметри ядра задано, як розподілено ресурси між ролями;
- схема контуру: де розміщено PostgreSQL, Oracle і Redis, чи є репліки та розділення читання й запису;
- вікна обслуговування: коли допустимий перезапуск сервісу бази і на який строк вузол можна вивести з роботи;
- вимоги до відкату: як швидко потрібно повернути попередні параметри, якщо метрики підуть униз;
- доступи: стенд, близький до продакшену, і дані моніторингу, за якими видно реальний розподіл пам'яті;
- критерій успіху: що вважаємо результатом — зниження затримки запитів, зростання попадань у кеш або стійкість під піком.
У відповідь ви отримуєте план робіт: з чого починаємо, що перевіряємо на стенді, за якими метриками приймаємо результат і за яких значень відкочуємо зміни. Порядок старту простий — узгоджуємо ввідні дані по контуру, зафіксуємо вікно й прийдемо з оцінкою. Опишіть завдання, і ми повернемося з конкретним планом.







