Чому аналітика на ClickHouse MergeTree деградує зі зростанням даних
У ClickHouse таблиця MergeTree зберігає дані не єдиним масивом, а набором відсортованих частин: рядки розкладаються за ключем сортування, і всередині кожної частини лежить розріджений первинний індекс — мітки через кожні кілька тисяч рядків.
Поки обсяг невеликий, планувальник за цими мітками швидко відсікає непотрібні діапазони, і запит читає рівно те, що попросили.
Зі зростанням обсягу даних механіка ламається: якщо фільтри у звітах ідуть не за ключем сортування, ні первинний індекс, ні розбиття за місяцями не допомагають, і кожен запит перетворюється на читання всієї таблиці.
Другий ефект — фонові злиття. Чим більше вставок дрібними пакетами, тим більше частин потрібно склеїти, тим частіше сервер упирається в межу одночасних злиттів і відповідає помилкою про занадто велику кількість частин.
Деградація аналітичних запитів іде нелінійно: повільні звіти замість секунд починають рахувати хвилини, а важкі агрегації з групуванням за висококардинальним ключем падають через пам'ять, коли проміжний результат не вміщається в ліміт на запит.
Вузьке місце сховища тут майже завжди одне: схема таблиці не збігається з тим, як її запитують. Сирі події лежать одним шаром, кожна побудова звіту перераховує агрегати заново, і зростання обсягу даних б'є напряму по часу відповіді. Розбираємо, як це закривається.
Що дає грамотна схема зберігання: ефект у часі та обсягах
У ClickHouse фізичний порядок рядків задає MergeTree: ключ сортування в ORDER BY визначає, які блоки даних запит узагалі читає, а не лише як швидко фільтрує. Коли звіт ходить за часом і клієнтом, ключ із цих двох полів перетворює повне сканування на читання вузького діапазону.
На типовій аналітиці звіт за добу по великій таблиці прискорюється на порядок — з десятків секунд до часток секунди, і це не разова оптимізація, а властивість схеми.
Партиціювання за місяцем разом із політикою TTL робить зберігання керованим: застарілі партиції видаляються цілком, без перезапису таблиці та без просідання вставок.
Колонкове стиснення плюс підбір кодека під характер колонок — окремо числові, окремо словникові через LowCardinality — дають стиснення в 4–8 разів щодо початкового обсягу. Реєстр перестає зростати лінійно разом із потоком подій.
Окремий ефект — зниклий клас відмов. При неправильному ключі зростає кількість дрібних кусків, фонові злиття не встигають за вставками, і завантаження починає падати з помилкою про занадто велику кількість частин.
Правильна грануляція партицій і ключа знімає цей сценарій: вставки йдуть стабільно, злиття вкладаються у фонове вікно без конкуренції за диск.
Навантаження на сховище розподіляється передбачувано: запити читають менше даних, тому аналітика не блокує завантаження. У сумі це і є вимірюваний результат перебудови схеми — стабільний час відповіді звітів на зростаючому обсязі та відсутність деградації під піковим записом.
Варіанти організації аналітичного зберігання: порівняння підходів
Схема зберігання в ClickHouse MergeTree визначає, де вважається агрегат — на льоту при кожній вибірці чи заздалегідь у момент запису. Від цього залежать час відповіді звітів і обсяг перерахунку, який знадобиться при зміні метрики.
Нижче три схеми, між якими реально обирають на старті аналітичного контуру.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Одна широка таблиця | Зрізи ще уточнюються, склад метрик змінюється часто | Важкі вибірки, дорога деталізація за дрібними розрізами |
| Шари з агрегатами | Список звітів стабільний, важлива швидкість відповіді | Агрегати перезбираються при правці логіки |
| Окремі таблиці під зрізи | Кілька команд зі своїми розрізами та своїм темпом завантаження | Більше об'єктів, дублюється завантаження, складніше звіряти |
На практиці варіанти комбінують: широка таблиця зберігає події, матеріалізовані представлення підтримують агреговані шари за частими зрізами, партиції нарізані за датою події, а термін життя записів заданий правилом очищення за горизонтом зберігання. Тоді новий зріз — це додавання представлення, а не перезаливання всього сховища.
Орієнтир при виборі такий: якщо запит звіту читає рядків на порядок більше, ніж повертає, агрегат пора виносити в окремий шар; якщо зрізів більше п'яти і вони живуть своїм життям — розносити по таблицях.
Партиціювання, ключ сортування та термін життя записів при цьому залишаються спільними й налаштовуються один раз для всього контуру.
Етапи налаштування сховища аналітики
Налаштування MergeTree починається не з вибору движка, а з розуміння того, які запити до сховища виконуються щодня.
Порядок робіт практично однаковий для будь-якого впровадження: спочатку вимірюємо, потім проєктуємо, і лише потім перевіряємо на навантаженні, близькому до реального. Нижче — послідовність, за якою йдуть наші інженери.
- Аудит запитів і профілю навантаження. Збираємо перелік регулярних звітів і разових вибірок, дивимося заміри часу за кожним, відзначаємо, які поля фільтруються й групуються найчастіше. Результат — список запитів, які справді варто оптимізувати, і тих, що можна не чіпати.
- Проєктування схеми. Визначаємо склад таблиць, типи колонок і розподіл за шарами: сирі дані, проміжні та попередньо розраховані агрегати. Це прибирає повторні важкі обчислення на кожному зверненні до панелі звітів.
- Підбір ключа сортування. Порядок колонок у ключі вирішує, чи потрапить запит у вузький діапазон читання чи буде сканувати таблицю цілком. Варіанти перевіряємо на реальних вибірках з аудиту, а не за інтуїцією.
- Налаштування зберігання. Розбиваємо дані за датами, задаємо терміни життя та окремі правила для рідко запитуваних періодів. Обсяг сховища перестає зростати безконтрольно, при цьому історія за потрібний горизонт залишається доступною, а старі зрізи читаються без деградації.
- Попередній розрахунок повторюваних звітів. Виносимо найчастіші агрегації в матеріалізовані представлення, щоб панелі відкривалися за секунди, а не перераховували мільйони рядків при кожному зверненні.
- Перевірка на реальному навантаженні. Прогоняємо типові сценарії одночасно, як це буде в роботі: дивимося час відгуку, споживання пам'яті й диска, поведінку при піковому записі. Тут же перевіряємо, що відкат на попередню схему проходить без втрати даних.
- Передача в експлуатацію. Віддаємо регламенти обслуговування, налаштування моніторингу та задокументовані процедури резервного копіювання й відновлення, щоб команда могла супроводжувати сховище самостійно.
Комплект артефактів: схеми таблиць, конфігурації, регламенти
Ми передаємо не лише працюючий кластер, а й повний набір артефактів, за якими його можна розвивати й обслуговувати силами своєї команди. Усе лежить у вашому репозиторії та в системі контролю версій — з історією змін, а не «усними домовленостями з минулим підрядником».
- Схеми таблиць — DDL-скрипти з движком MergeTree: ключ сортування, розбиття за партиціями, правила TTL і матеріалізовані представлення. До кожної таблиці коментар, чому вибрано саме такий ключ і що зламається при його зміні.
- Конфігурації сховища — параметри злиття кусків, ліміти пам'яті на запит, налаштування реплікації та розподілених таблиць, увімкнені кодеки стиснення за колонками.
- Політики доступу — ролі та рівні прав: окремі облікові записи для сервісів запису, аналітиків і чергового інженера; квоти на важкі вибірки; для частини команд — доступ лише на читання.
- Регламент обслуговування — розклад злиття партицій, контроль зростання кількості кусків, порядок дій при відставанні репліки та правила відкату на попередню схему.
- Схема потоків даних — звідки й з якою періодичністю надходять дані, де буферизуються, що робити при затримці джерела або дублях.
- Моніторинг і оповіщення — перелік метрик, порогові значення та список тих, хто отримує повідомлення, з пріоритетами.
- Інструкція з експлуатації — типові операції, розбір частих відмов і межі, за якими потрібно кликати інженера.
- Журнал рішень — які варіанти схеми та зберігання розглядалися, які відхилено й чому: це економить час при наступному доопрацюванні.
Комплект розрахований на те, щоб новий інженер у команді розібрався у сховищі за документами, а не за листуванням. Якщо знадобиться розширення — приходимо вже до готової бази знань.
Кейс: як прискорили аналітичні запити на потокових даних
Продуктовий сервіс із безперервним потоком подій: десятки мільйонів записів на добу складалися в одну таблицю ClickHouse без виразного ключа сортування.
Звіти за воронкою збиралися хвилинами, частина запитів обривалася за часом очікування, а реєстр зростав приблизно на третину щомісяця.
Точка, з якої починали
- Час відповіді звіту — у 9 разів вищий за цільовий: аналітики чекали результат і перераховували зрізи вручну.
- Частка запитів, що падали, — 12% у години пік, тобто відмови припадали саме на найбільш навантажені вікна.
- Обсяг реєстру зростав швидше за сам потік подій: старі партиції ніхто не вичищав.
Що змінили
Перерозмітили таблицю: партиціювання за датою, ключ сортування за орендарем і типом події, щоб фільтри звітів потрапляли в межі блоків і читалося менше даних.
Важкі агрегати винесли в матеріалізовані представлення з попереднім розрахунком, вставки перевели на пакетний запис, а для історичних даних повісили політику зберігання з автоматичним очищенням.
Що стало
Час відповіді звіту скоротився в 12 разів — вивантаження перестало бути окремою задачею аналітика. Частка запитів, що падали, зійшла до нуля, обсяг активного реєстру зменшився втричі, при цьому відкат до будь-якої з останніх партицій залишається доступним. Нові типи подій додаємо за тією ж схемою, без перезаливання таблиці.
Чим MergeTree відрізняється від інших движків ClickHouse?
У ClickHouse движок задається при створенні таблиці й визначає формат зберігання, доступні вибірки та поведінку при вставці.
MergeTree — не один движок, а сімейство зі спільною механікою: дані пишуться відсортованими кусками, за ключем сортування будується розріджений індекс, а фонові злиття укрупнюють куски й згортають дублі.
Log-сімейство (Log, TinyLog, StripeLog) влаштовано простіше: колонка — окремий файл, індексу та злиттів немає. Такі таблиці дешеві на запис і підходять для довідників і проміжних буферів, але вибірка за ключем читає файл цілком, а паралельний запис обмежений.
Реплікація в ReplicatedMergeTree іде через координатор: вузли обмінюються не файлами, а описом частин і операцій, тому відстала репліка доганяє за журналом.
Це не те саме, що розподілений доступ: репліки — копії одних і тих самих даних, а таблиця Distributed розкладає запит за шардами й збирає результат.
Різниця всередині сімейства теж суттєва: ReplacingMergeTree залишає останню версію рядка, SummingMergeTree підсумовує значення при злитті, CollapsingMergeTree позначає видалення. Вибір движка змінює не лише швидкість, а й те, які дані взагалі опиняться в таблиці після злиттів.
Практичний критерій простий: потрібні точкові читання та злиття — беремо MergeTree; потрібна дедуплікація при вставці — варіант із версіями; потрібні дані на кількох вузлах — репліковані таблиці під Distributed. Для дрібних довідників Log-сімейство чесно дешевше.
Технічні сумніви перед перебудовою сховища
Перебудова схеми на працюючому сховищі — питання не «зламається чи ні», а порядку операцій і точок відкату. Нижче — відповіді на три інженерні заперечення, які звучать перед стартом майже завжди.
Дані при перебудові таблиць загубляться?
Перебудова йде через нові партиції: записи дублюються в цільову структуру, звіряються за контрольними сумами рядок за рядком, і лише після збіжності перемикається читання. Стара структура залишається на диску до кінця перевірки — відкат займає хвилини.
Міграція даних виконується порціями, тому окреме вікно недоступності не потрібне: сховище продовжує віддавати запити.
Чи доведеться переписувати звіти та вивантаження?
Зазвичай ні. Імена полів і типи зберігаємо, а при зміні гранулярності додаємо шар представлень із попереднім контрактом для аналітики.
Сумісність звітів перевіряємо порівнянням вивантажень до й після перебудови: розбіжності понад допуск ловимо на тестовому контурі, а не на бойовому.
Що буде з працюючим продакшеном?
Перебудову ведемо на репліці, перемикання трафіку — у вікно мінімального навантаження. Фонові задачі обмежені за ресурсами, щоб не забирати пропускну здатність у живих запитів.
Ризики впровадження закриваємо поетапно: спочатку партиціювання та стиснення, потім перехід на цільову структуру, із заздалегідь зафіксованим планом відкату.
Обговоримо задачу та повернемося з оцінкою й планом
Щоб оцінка впровадження була не «на око», а по суті, потрібні ваші вхідні дані. Опишіть задачу у двох словах — яка аналітика потрібна, хто і як часто будує звіти, — і додайте технічний контекст щодо навантаження й запитів. Надішліть те, що є: чернетку схеми, приклад вивантаження, список полів.
- Звідки йдуть дані: черга, збирач логів, вивантаження змін з операційної бази — з якою частотою і в якому форматі надходять події.
- Профіль навантаження: скільки рядків на добу й на місяць, середній і піковий потік, потрібна глибина зберігання.
- Типові запити: 5-10 найчастіших із фільтрами, групуваннями та періодом — саме вони визначають порядок сортування в ключі та розбиття на партиції.
- Свіжість даних: допустима затримка між подією та її появою у звіті — секунди чи години; від цього залежить періодичність вставок і набір вбудованих агрегатів.
- Відмовостійкість: чи потрібні репліки, скільки копій даних, чи є окремі вузли для координації кластера.
- Поточний стан: де буде розгорнуто, скільки місця на дисках, що вже пробували й які вузькі місця бачили — важкі злиття, роздуте число частин, повільні з'єднання таблиць.
Далі за цими вхідними збираємо план робіт: схему таблиць, порядок ключів і партицій, політики зберігання та проріджування, регламент обслуговування — і оцінку впровадження за етапами. Наступний крок — обговоримо задачу та повернемося з планом і термінами.







