Команда продукту каже «звіти повільні», а під цим зазвичай мають на увазі: дашборди таймаутяться, експорт CSV займає хвилини,
і головний вузол MariaDB виглядає так, ніби майнить криптовалюту. Тим часом затримка оформлення замовлення зростає і ви починаєте
отримувати той особливий вид сигналів на пейджер, що руйнує вихідні.
Це не загадка. Ви намагаєтеся виконувати аналітику на OLTP-рушії, в умовах OLTP, з моделями даних для OLTP,
і вважаєте, що «це ж просто SQL», тож все нормально. Це не нормально. Це дорого, ризиковано і зазвичай уникнути можливо.
Чому «звіти» вбивають MariaDB (і чому індекси — не релігія)
MariaDB (і MySQL) чудово підходять для транзакційних навантажень: невеликі зчитування й записи, щільні індекси,
передбачувані шляхи доступу і патерни конкуренції, сформовані під запити користувачів. Ваш додаток робить багато
«отримати одне замовлення», «вставити один платіж», «оновити одну сесію». Движок створено, щоб це було швидко і безпечно.
Звіти — інші. Вони роблять широкі сканування, великі join’и, агрегування з групуванням і патерни, схожі на віконні,
що проходять через мільйони рядків, щоб підсумувати «усе». І вони з’являються в найгірший момент:
кінець місяця, кінець кварталу, ранок понеділка, коли трафік підіймається.
OLTP vs аналітика: це не лише «більші запити»
Продуктивність OLTP — це про затримку і конкуренцію. Продуктивність аналітики — це про пропускну спроможність і ширину каналу:
як швидко ви можете сканувати, декодувати, фільтрувати і агрегувати дані. Це різна фізика.
- Row store vs column store: MariaDB зберігає рядки. Аналітиці потрібні колонки. Сканування 2 колонок з 50 не повинно змушувати читати 50.
- Залежність від індексів: OLTP спирається на індекси. Аналітика часто віддає перевагу сканам з векторизованим виконанням і стисненим колонним зберіганням.
- Патерни конкуренції: OLTP — багато коротких запитів; аналітика — менше, довші запити, що займатимуть CPU, пам’ять і I/O.
- Реальність моделі даних: нормалізовані схеми хороші для записів; денормалізовані схеми швидші для агрегатів і join’ів під час запиту.
Справжня шкода: звіти не лише працюють повільно, вони дестабілізують базу
«Повільно» дратує. «Повільно і дестабілізуюче» — це інцидент. Запит звіту може:
- Витіснити гарячий OLTP набір з buffer pool.
- Забити диск читаннями (та undo/redo записами, якщо тимчасові таблиці зливаються на диск).
- Створити реплікаційну затримку (записи є, але репліка не встигає їх застосовувати).
- Спровокувати metadata locks у незручні моменти під час змін схеми.
- Викликати насичення CPU, внаслідок чого повільніє КОЖНИЙ запит, включно з тими, що важливі.
Якщо ви жили за принципом «просто додати індекс», ось підступ: індекси пришвидшують селективні запити.
Звіти часто не селективні. Або вони селективні за полем з низькою кардинальністю, що робить індекси менш корисними.
Або вони з’єднують великі таблиці так, що «правильного» індексу немає.
Жарт №1: Додавати індекси, щоб виправити звіти — це як додавати смуги на шосе, щоб виправити годину пік — вітаю, у вас тепер ширша пробка.
Цікаві факти і трохи історії (щоб ви перестали сперечатися з гравітацією)
- MariaDB відгалузилася від MySQL після побоювань щодо придбання Sun компанією Oracle; вона зберегла сумісність з MySQL, але розвивалася у механізмах зберігання й можливостях.
- ClickHouse походить з Yandex для веб-масштабної аналітики; його дизайн припускає великі скани і швидкі агрегати як норму, а не виняток.
- Колонне зберігання стало мейнстримом в аналітиці, бо читати менше байтів важливіше, ніж «мати правильний індекс», коли набори даних великі.
- Стиснення — це функція у колонних сховищах: краще стиснення = менше I/O, а менше I/O = більше швидкості. Це не лише економія диску.
- MergeTree (основна сімейство двигунів ClickHouse) оперує фонoвими злиттями та незмінними частинами — чудово для інжесту + читань, інші режими відмінні від B-tree.
- Materialized views в ClickHouse часто використовують як «передагрегування», патерн, популяризований OLAP-системами для стабільної латентності запитів.
- Зорова схема і денормалізовані таблиці фактів стали поширеними, бо джоїн нормалізованих транзакційних таблиць в аналітиці дорогий і крихкий.
- Реплікаційна затримка як симптом існувала ще до сучасних стеків аналітики: «запускати звіти на репліці» — хаґ з ранніх MySQL-часів, і він все ще кусає.
Що ClickHouse вміє добре (і в чому слабкий)
ClickHouse — це OLAP-база, створена для швидких зчитувань над великими наборами даних. Він читає колонні дані,
виконує запити векторизовано, агресивно стискає і може масштабуватися горизонтально.
Він призначений для «дай мені агрегати за місяць подій», а не для «оновлюй один рядок за первинним ключем 5000 разів на секунду».
Сильні сторони ClickHouse
- Швидкі скани й агрегати: group-by, таймбаки, приближені перцентилі, top-N списки.
- Високе стиснення: часто ви отримуєте дивовижно маленький обсяг для подієподібних даних.
- Дружній режим вставки: вставляйте батчами; нехай merges прибирають фрагменти пізніше.
- Опції передагрегації: materialized views можуть будувати підсумкові таблиці по мірі надходження даних.
- Розподілене виконання: масштабуйте читання і зберігання по шардам/реплікам при правильному дизайні.
Слабкі місця ClickHouse (де люди найчастіше роблять боляче собі)
- Оновлення/видалення — не OLTP: так, ви можете робити mutations, але це важко і не шлях за замовчуванням.
- Joins можуть бути дорогими, якщо поводитися як з нормалізованою OLTP-схемою і чекати чудес.
- Патерни eventual consistency: доведеться планувати пізні події, дедуплікацію й ідемпотентність.
- Операційні нюанси: merges, parts, дискові пороги і ліміти пам’яті — знання не опційне.
Основна рекомендація: зберігайте MariaDB для транзакцій і джерела істини.
Використовуйте ClickHouse для звітів, дашбордів і досліджень. Не змушуйте їх боротися за одні й ті ж ресурси.
Фреймворк рішення: лишити MariaDB, оптимізувати чи виносити
Не кожен повільний звіт вимагає нової бази. Деякі потребують кращого запиту, підсумкової таблиці,
або визнавання, що дашборду не потрібна оновленість що секунду. Але коли навантаження фундаментально аналітичне,
MariaDB продовжуватиме брати з вас «відсотки» у вигляді більших машин, більше реплік і більше інцидентів.
Коли залишатися в MariaDB (поки що)
- Набір даних невеликий (комфортно вміщується в пам’ять) і запити прості.
- Звіти можна перераховувати нічними пакетами без бізнес-болю.
- Більшість уповільнень від очевидних багів у запитах (відсутні предикати, N+1, випадкові cross join’и).
- У вас немає операційних ресурсів для ще однієї системи.
Коли виносити в ClickHouse
- Звіти регулярно сканують мільйони рядків і потребують інтерактивної латентності.
- Дашборди працюють постійно і конкурують з продукційним трафіком.
- Потрібна гнучка нарізка/фільтрація подієподібних даних (часові ряди, логи, кліки, замовлення, рухи інвентарю).
- «Запустити на репліці» викликає реплікаційну затримку або репліка вже стала критичною для продакшену.
Коли переосмислити продукт замість технічного рішення
Інколи чесне рішення не технічне. Це — управління очікуваннями.
Якщо фінанси хочуть «кохартний аналіз за весь час» на вимогу по п’яти роках даних, відповідь:
побудуйте правильний пайплайн і зберігання, і прийміть, що це коштує грошей і часу.
Швидкий план діагностики
Ви хочете знайти вузьке місце за хвилини, а не після тижня тюнінгу на емоціях. Ось послідовність, що працює в реальному житті.
Перше: MariaDB задихається через CPU, I/O, блоки чи пам’ять?
- CPU завантажено: ймовірні погані плани, великі join’и, відсутня селективність, важке сортування/групування.
- I/O-bound: промахи buffer pool, сканування таблиць, тимчасові таблиці на диск, погана локальність, багато випадкових читань.
- Блокування: metadata locks, довгі транзакції або звіти, що утримують блоки довше, ніж очікувалося.
- Тиск на пам’ять: сортування, тимчасові таблиці або занадто малий buffer pool, що спричиняє чурн.
Друге: чи справжня проблема — невідповідність типу навантаження?
- Запити вимагають сканування «більшості таблиці» навіть з індексами.
- Бізнес просить висококардинальні group-by і гнучкі фільтри.
- Звіти часті і виконуються в пікові години.
Третє: чи прихований проблемний компонент — пайплайн даних?
- Реплікаційна затримка робить «запуск на репліці» марним.
- CDC або ETL вводять дублі або події поза порядком.
- Зміни схеми ламають пайплайн, і ви дізнаєтесь про це тиждень потому.
Четверте: підтвердьте, вимірявши один репрезентативний запит end-to-end
Виберіть один болючий запит. Отримайте його план у MariaDB, запустіть з трасуванням і кількісно визначте rows examined, temp tables,
сортування і час очікування. Потім вирішіть: оптимізувати в MariaDB або припинити катувати її і виносити.
Цитата, щоб не обманювати себе: «Hope is not a strategy.» — Gene Kranz
Практичні завдання: команди, результати та рішення (12+)
Це не «іграшкові» команди. Це повсякденні кроки, які ви використовуєте, щоб вирішити, що робити далі.
Для кожного завдання: запустіть команду, інтерпретуйте результат і прийміть рішення.
Завдання 1: Знайти топ повільних запитів у MariaDB (slow query log)
cr0x@server:~$ sudo pt-query-digest /var/log/mysql/mariadb-slow.log | head -n 30
# 120s user time, 3s system time, 1.23M rss, 2.45G vsz
# Current date: Mon Dec 30 10:12:44 2025
# Hostname: db-primary
# Files: mariadb-slow.log
# Overall: 8.42k total, 97 unique, 0.23 QPS, 0.01x concurrency
# Time range: 2025-12-29T09:00:00 to 2025-12-30T09:00:00
# Attribute total min max avg 95% stddev median
# ============ ======= ======= ======= ======= ======= ======= =======
# Exec time 13200s 100ms 95s 1.6s 12s 4.3s 450ms
# Rows examine 2.1e11 0 9.2e8 2.5e7 1.1e8 6.3e7 8.2e6
# Query 1: 38% RPS, 61% time, 80% rows examined
Що це означає: Rows examined величезна відносно рядків, що повертаються. Класика — «аналітичний запит на OLTP-таблицях».
Рішення: Якщо найгірші — це групування/агрегування по великих діапазонах часу, плануйте винос.
Якщо це один запит з відсутнім предикатом — виправте запит спочатку.
Завдання 2: Перевірити поточні потоки MariaDB і чи вони зависають
cr0x@server:~$ mariadb -e "SHOW PROCESSLIST\G" | sed -n '1,60p'
*************************** 1. row ***************************
Id: 8123
User: report_user
Host: 10.2.4.19:52311
db: app
Command: Query
Time: 184
State: Sending data
Info: SELECT customer_id, count(*) FROM orders WHERE created_at >= '2025-10-01' GROUP BY customer_id
*************************** 2. row ***************************
Id: 8130
User: app_user
Host: 10.2.7.11:49821
db: app
Command: Query
Time: 2
State: Updating
Info: UPDATE inventory SET qty=qty-1 WHERE sku='X'
Що це означає: «Sending data» сотні секунд часто означає скан/агрегування, а не мережеву передачу.
Рішення: Якщо потоки звітів довготривалі і численні, обмежте їх, перемістіть на окрему систему або й те, й інше.
Завдання 3: Підтвердити, чи план запиту робить скан
cr0x@server:~$ mariadb -e "EXPLAIN SELECT customer_id, count(*) FROM orders WHERE created_at >= '2025-10-01' GROUP BY customer_id\G"
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: orders
type: range
possible_keys: idx_orders_created_at
key: idx_orders_created_at
rows: 48219321
Extra: Using index condition; Using temporary; Using filesort
Що це означає: Навіть з range-індексом ви скануєте ~48M рядків, потім тимчасові таблиці і filesort для group-by.
Рішення: Якщо «rows» — десятки мільйонів і звіт частий, припиніть оптимізувати по краях. Виносьте.
Завдання 4: Перевірити поведінку InnoDB buffer pool (чи трашиться він?)
cr0x@server:~$ mariadb -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';"
+---------------------------------------+------------+
| Variable_name | Value |
+---------------------------------------+------------+
| Innodb_buffer_pool_read_requests | 9923412331 |
| Innodb_buffer_pool_reads | 183492211 |
+---------------------------------------+------------+
Що це означає: Співвідношення вказує, що багато читань обслуговуються з пам’яті, але 183M фізичних читань — усе одно багато.
Слідкуйте за трендом під час вікон звітів.
Рішення: Якщо buffer pool reads підскочать під час звітів і затримка додатка зростає, ізолюйте аналітичне навантаження.
Завдання 5: Перевірити зливи тимчасових таблиць на диск (тихий вбивця)
cr0x@server:~$ mariadb -e "SHOW GLOBAL STATUS LIKE 'Created_tmp%tables';"
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Created_tmp_disk_tables | 421193 |
| Created_tmp_tables | 702881 |
+-------------------------+----------+
Що це означає: Тимчасові таблиці на диску дорогі: додатковий I/O, затримка, більше конкуренції.
Рішення: Якщо disk temp tables ростуть під час звітів, або переробіть запити, або припиніть виконувати їх на MariaDB.
Завдання 6: Виміряти реплікаційну затримку, якщо звіти на репліках
cr0x@server:~$ mariadb -h db-replica -e "SHOW SLAVE STATUS\G" | egrep 'Seconds_Behind_Master|Slave_IO_Running|Slave_SQL_Running'
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 1870
Що це означає: 31 хвилина позаду. Ваша «репліка для звітів» тепер — машина часу.
Рішення: Перестаньте вважати репліки аналітичними рушіями. Виносьте в ClickHouse або побудуйте окремі аналітичні репліки з іншими гарантіями.
Завдання 7: Визначити, чи хост I/O-bound (Linux)
cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (db-primary) 12/30/2025
avg-cpu: %user %nice %system %iowait %steal %idle
52.21 0.00 6.12 18.44 0.00 23.23
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz w/s wkB/s w_await %util
nvme0n1 2100.0 268800.0 0.0 0.00 8.10 128.0 980.0 105600.0 12.40 98.70
Що це означає: %util близько 99% і високий iowait: сховище насичене. Звіти читають занадто багато.
Рішення: Ви можете апгрейдити сховище, але це купівля часу. Краще: зменшити обсяг сканів, перемістивши аналітику в колонне зберігання.
Завдання 8: Перевірити розміри таблиць MariaDB і ріст (тиск по ємності)
cr0x@server:~$ mariadb -e "SELECT table_name, round((data_length+index_length)/1024/1024/1024,2) AS gb FROM information_schema.tables WHERE table_schema='app' ORDER BY (data_length+index_length) DESC LIMIT 10;"
+-------------------+-------+
| table_name | gb |
+-------------------+-------+
| events | 612.45 |
| orders | 189.12 |
| order_items | 141.77 |
| sessions | 88.04 |
+-------------------+-------+
Що це означає: Великі таблиці з мішаним OLTP + аналітичним використанням (як events) — перші кандидати на винос.
Рішення: Почніть з найбільших таблиць, що викликають скани; транзакційні таблиці залишайте в MariaDB.
Завдання 9: Бейслайн здоров’я сервера ClickHouse
cr0x@server:~$ clickhouse-client -q "SELECT version(), uptime(), round(memory_usage/1024/1024) AS mem_mb FROM system.metrics WHERE metric IN ('MemoryTracking')"
24.9.2.42
86400
512
Що це означає: Можна швидко відстежувати memory tracking і uptime; якщо це вже високе в прості, є проблема в конфігурації або навантаженні.
Рішення: Встановіть розумні ліміти пам’яті і розслідуйте запити, що втікають в пам’ять, перед тим як підключати дашборди.
Завдання 10: Перевірити parts/merges у ClickHouse (чи потопають ви в маленьких частинах?)
cr0x@server:~$ clickhouse-client -q "SELECT table, count() AS parts, sum(rows) AS rows, round(sum(bytes_on_disk)/1024/1024/1024,2) AS gb FROM system.parts WHERE active GROUP BY table ORDER BY parts DESC LIMIT 5"
events 18234 891233112 122.31
orders 1820 83211299 18.44
Що це означає: 18k parts для events натякає на занадто багато дрібних вставок або поганий партишинґ.
Рішення: Батчуйте вставки, налаштуйте партишинґ і забезпечте, щоб merges встигали; інакше латентність запитів буде нестабільною.
Завдання 11: Знайти повільні запити в ClickHouse (system.query_log)
cr0x@server:~$ clickhouse-client -q "SELECT query_duration_ms, read_rows, read_bytes, memory_usage, query FROM system.query_log WHERE type='QueryFinish' AND event_time > now()-3600 ORDER BY query_duration_ms DESC LIMIT 5"
12034 981233112 44120341212 2147483648 SELECT customer_id, count() FROM events WHERE ts > now()-86400 GROUP BY customer_id
Що це означає: read_rows/read_bytes показують, чи запит читає занадто багато; memory_usage натякає на великий стан group-by.
Рішення: Якщо читання величезні — перегляньте primary key/order by і партишинґ; якщо пам’ять велика — розгляньте передагрегацію.
Завдання 12: Перевірити диск водяні позначки і вільне місце на вузлах ClickHouse
cr0x@server:~$ df -h /var/lib/clickhouse
Filesystem Size Used Avail Use% Mounted on
/dev/md0 3.6T 3.1T 420G 89% /var/lib/clickhouse
Що це означає: ClickHouse потребує запаса місця для merges; близькість до повного диска робить merges повільними і призводить до дивних відмов.
Рішення: Тримайте значний запас вільного місця; якщо ви стабільно вище ~80–85%, плануйте ємність або політики життєвого циклу зараз.
Завдання 13: Перевірити CDC lag (відстеження позиції binlog через конектор)
cr0x@server:~$ sudo journalctl -u mariadb-to-clickhouse-cdc --since "10 min ago" | tail -n 8
Dec 30 10:01:11 cdc1 cdc[1842]: last_binlog=mysql-bin.003211:918233112
Dec 30 10:01:21 cdc1 cdc[1842]: last_binlog=mysql-bin.003211:918244901
Dec 30 10:01:31 cdc1 cdc[1842]: lag_seconds=3
Що це означає: Низький і стабільний лаг — це те, чого ви хочете. Спайки означають тиск на приймач або проблеми з конектором.
Рішення: Якщо лаг підскакує під час вікон звітів, ваш винос неповний: або дашборди все ще б’ють у MariaDB, або ClickHouse не встигає інжестити.
Завдання 14: Підтвердити, що ClickHouse дійсно використовує індекс (data skipping) через EXPLAIN
cr0x@server:~$ clickhouse-client -q "EXPLAIN indexes=1 SELECT count() FROM events WHERE ts >= now()-3600 AND customer_id=12345"
Indexes:
PrimaryKey
Keys:
ts
customer_id
Condition: (ts >= (now() - 3600)) AND (customer_id = 12345)
Що це означає: Вам потрібні умови, що узгоджуються з ORDER BY, щоб ClickHouse міг пропускати границі.
Рішення: Якщо ваші поширені фільтри не входять до primary key, ваша схема бореться з вашими запитами.
Проєкт виносу: потік даних, затримка та коректність
«Виносити аналітику» звучить чисто, поки ви не поставите набридливі питання: Наскільки свіжі мають бути дані?
Що робити, коли рядок оновлюється? Як моделювати видалення? Яке джерело істини?
Виберіть патерн виносу
- Batch ETL: нічні/годинні експорти з MariaDB у ClickHouse. Просто, дешево, не в реальному часі.
- CDC (change data capture): стрімінг змін binlog у ClickHouse безперервно. Більше компонентів, менша затримка.
- Dual-write: додаток пише в обидві MariaDB і ClickHouse. Швидкий шлях до жалю, якщо немає сильної дисципліни.
Для більшості компаній: batch ETL для небезпечних звітів, CDC для дашбордів, що керують операціями, і уникайте dual-write, якщо не любите дебагувати узгодженість о 2:00.
Визначайте «коректність» по-дорослому
Коректність аналітики рідко означає «точно як у OLTP у кожен мілісекунд». Зазвичай це:
- Дані повні до T мінус N хвилин.
- Пізні події обробляються (вікно backfill).
- Дедуплікація детермінована.
- Бізнес-метрики визначені раз і не винаходяться під кожен дашборд.
Як обробляти оновлення й видалення з MariaDB
В MariaDB оновлення рядка — буденність. В ClickHouse типово моделюють зміни в часі:
- Immutable events: append-only факти (замовлення створено, платіж підтверджено). Найкращий випадок.
- ReplacingMergeTree: зберігає останню версію за колонкою version; дублі зникають у результаті злиттів.
- CollapsingMergeTree: моделює знак (+1/-1) для семантики «вставка/видалення», підходить для деяких потоків подій.
- Mutations: важкі; використовувати рідко для справжніх корекцій, а не для рутинних оновлень.
Якщо ваша OLTP-таблиця — це «поточний стан» (наприклад, запас на складі), ви можете реплікувати її в ClickHouse,
але не очікуйте, що вона поводитиметься як транзакційно оновлювана таблиця. Для аналітики стан зазвичай виводять з подій.
Моделювання схем, яке не старіє
Найшвидший шлях зробити ClickHouse повільним — імпортувати нормалізовану MariaDB-схему «як є» і дивуватися.
Другий швидкий шлях — обрати ORDER BY, що не відповідає вашим реально використовуваним фільтрам.
Почніть з питань, а не з таблиць
Перерахуйте топ-10 запитів дашбордів. Реальних, не бажаних. Для кожного зафіксуйте:
- Фільтри по часу (остання година, останній день, довільний діапазон)
- Вимірювання з високою селективністю (customer_id, org_id, tenant_id)
- Групувальні виміри (country, plan, product_id)
- Очікувана кардинальність (скільки груп?)
- Частота оновлення і SLO
Стратегія order key: суперсила «пропускати дані»
Primary key ClickHouse (вираження ORDER BY в MergeTree) — не B-tree індекс.
Це те, як дані фізично сортуються, дозволяючи пропускати блоки, коли предикати збігаються з порядком.
Якщо кожен дашборд фільтрує за часом і орендарем, ваш order key часто починається з них:
(tenant_id, ts) або (ts, tenant_id). Що першим — залежить від патернів доступу.
- Переважно «один тенант, багато часових діапазонів»: (tenant_id, ts)
- Переважно «глобальні часові зрізи по всіх тенантах»: (ts, tenant_id)
Партишинґ: тримайте нудним
Партишинґ допомагає керувати життєвим циклом даних і обсягом злиттів. Надмірний партишинґ створює занадто багато частин і операційний біль.
Звичайний патерн: партиціювання по місяцю для подій, по дню для дуже великого обсягу, або по tenant тільки якщо ті дуже великі.
Materialized views: передагрегуйте для передбачуваних дашбордів
Скарга «дашборд повільний» часто означає «group-by за 90 днів повільний». Передагрегуйте гарячі метрики.
Побудуйте підсумкові таблиці, індексовані за вимірами, за якими ви фільтруєте. Тоді дашборди звертатимуться до підсумків, не до сирих фактів.
Жарт №2: Materialized views — це як приготування їжі на тиждень: витрачаєте трохи часу наперед, щоб не плакати над доставкою о 23:00.
Операції та надійність: як серйозно працювати з ClickHouse
Винос аналітики — це не «встановити ClickHouse і вібрувати». Ви впроваджуєте нову продакшен-систему.
Вона потребує моніторингу, бекапів, планування ємності, дисципліни зі змінами схеми і реакції на інциденти.
Що моніторити (мінімальна гігієна SRE)
- Затримка інжесту: від binlog MariaDB до доступності в ClickHouse.
- Беклог злиттів: кількість parts, merges у процесі, байти на злиття.
- Використання диску і вільне місце: merges потребують запасу; близькі до повного диски викликають каскадні збої.
- Перцентилі латентності запитів: для дашбордів і ad-hoc досліджень.
- Використання пам’яті і OOM kills: group-by може агресивно алокувати без лімітів.
- Здоров’я реплік: якщо використовуєте реплікацію, моніторте чергу реплікації і помилки.
Бекапи: вирішіть, що ви захищаєте
Дані ClickHouse часто є похідними. Це не означає, що їх не потрібно бекапити.
Це означає, що стратегія бекапу може відрізнятися:
- Якщо можна відновити з сирих логів/об’єктного сховища, бекапи — про швидше відновлення, а не про виживання системи.
- Якщо ClickHouse містить кураторовану бізнес-істину (таблиці метрик, скориговані дані), бекапте її як справді важливу — бо так воно і є.
Ізоляція ресурсів: не дайте дашбордам стати DoS
Використовуйте користувачів/профілі/квоти. Встановлюйте ліміти пам’яті на запит і на користувача. Використовуйте черги при потребі.
Мета: один важкий запит аналітика не повинен голодувати оперативні дашборди.
Зміни схеми: ставте їх як депл у продакшен
Зміни схеми в ClickHouse часто простіші, ніж OLTP-міграції, але вони все одно мають радіус ураження:
materialized views залежать від колонок; CDC-пайплайни залежать від схеми; дашборди залежать від семантики.
Плануйте зміни з урахуванням сумісності і версіонуйте трансформації.
Три корпоративні міні-історії (з болем)
Міні-історія 1: Інцидент від неправильної припущення
Середня B2B SaaS компанія мала знайому архітектуру: MariaDB primary, одна репліка маркована «reporting», і BI-інструмент,
що дивився на цю репліку. Всім було здається розумно. Записи залишалися на primary, читання — на репліці, мир на землі.
Неправильне припущення було тонким: вони вважали, що реплікаційна затримка буде «достатньо малою» для дашбордів, і якщо вона виросте,
це буде очевидно. Не було. BI інструмент кешував результати, користувачі експортували CSV, і ніхто не помічав, що «поточний місячний дохід»
іноді відставав на тридцять хвилин у пікові години. Дані були неправильні, але достатньо консистентні, щоб виглядати правдоподібно.
Потім відбувся реліз продукту. Обсяг записів підскочив, як і важкі звіти. Реплікаційна затримка зросла, і BI став повільнішим.
Хтось «виправив» це, підключивши BI до primary «на день». Той день включав багатогодинне сканування таблиці і кілька
дорогих group-by. Затримка оформлення замовлень зросла; додаток повторював запити; записи зросли ще більше; primary почав насичувати диск.
Інцидент не був катастрофою в один проміжок. Це було накопичення поганої архітектури: використання реплікації як аналітичної системи.
Постмортем дав звичний твіст: репліка вже була критичною для продакшену, бо годувала дашборди. Отже вона не була «прикрашенням».
Це була залежність без SLO і без запобіжників.
Виправлення було нудним і структурним: перемістити аналітику в ClickHouse, залишити репліку для failover, визначити очікування щодо свіжості і додати аларми затримки.
Реплікація повернулася до ролі, якої вона повинна була виконувати: інструмент стійкості, а не аналітика.
Міні-історія 2: Оптимізація, що повернулась бумерангом
Інша компанія вивела аналітику в ClickHouse і святкувала. Дашборди пішли від хвилин до секунд.
Потім вони «оптимізували» інжест, стрімінгуючи кожен рядок як одиночну вставку, бо «реального часу» звучало добре на зустрічах.
Через кілька тижнів латентність запитів стала непередбачуваною. Іноді швидко, іноді жахливо. Кластер ClickHouse виглядав здоровим, поки не ні.
Кількість parts росла. Merges працювали постійно. Диски завантажилися фоновою роботою. Кластер не падав; він був постійно незадоволений.
Бумеранг класичний: ClickHouse дружній до додавання, але він хоче батчі.
Одна-вставка-рядок створює багато дрібних parts, а багато дрібних parts створюють merge-тиск, який краде I/O у запитів.
Користувачі відчули це як «дашборди ненадійні», що навіть гірше за «повільні», бо викликає недовіру.
Відновлення не було магічним тюнінгом. Вони батчували вставки (за часом або за обсягом), зменшили гранулярність партицій,
і встановили операційні ліміти: порогові значення частин на партицію з алертами. Вони також погодилися, що «реальний час»
означає «в межах хвилини» для більшості метрик, а не «в межах секунди».
Це хороша наука: найшвидша система — та, яку ви не змушуєте робити патологічні речі.
Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію
Компанія, пов’язана з платежами, вела і MariaDB, і ClickHouse. Нічого яскравого.
У них був письмовий data contract: невеликий документ, що описував кожну таблицю метрик, поля джерела,
логіку трансформацій, «вікно свіжості» і тестовий запит для валідації кількостей рядків.
Кожна зміна схеми в MariaDB, що торкалася реплікованих таблиць, вимагала легкого рев’ю:
чи ламає це CDC? чи потрібна нова колонка в ClickHouse? чи потрібно оновити materialized view?
Процес не був улюбленим, але послідовним. Вони також запускали щоденну перевірку примирення:
порівнювали кількості і суми між MariaDB і ClickHouse для рухомого вікна часу.
Одного п’ятничного дня, здавалося б нешкідлива зміна додатку почала писати NULL у вимір, за яким групували дашборди.
MariaDB — байдуже. ClickHouse — байдуже. Але дашборд показав раптове падіння ключової метрики.
Перевірка примирення спіймала це за годину, бо зміниться кардинальність групування різко.
На виклик не потрібно було гадати. Вони відкотили зміну, заповнили відсутні виміри в ClickHouse з подій джерела,
і понеділковий виконавчий дашборд не перетворився на судову драму.
Нікому не дали трофей за цей процес. Ось чому він працював.
Типові помилки: симптом → корінь → виправлення
1) Симптом: «Звіти повільні» тільки в робочий час
Корінь: звіти конкурують з OLTP за CPU і I/O; чурн buffer pool підсилює ефект.
Виправлення: перемістіть дашборди в ClickHouse; якщо залишаєтесь в MariaDB, плануйте важкі звіти в не пікові години і додайте таймаути.
2) Симптом: репліка MariaDB відстає щоразу, коли запускається BI
Корінь: репліка читає багато і не встигає застосувати записи; або I/O насичено сканами.
Виправлення: припиніть використовувати репліку для аналітики; виносьте. Якщо тимчасово — додайте виділену «аналітичну репліку» з ізольованими ресурсами і прийміть, що це тимчасове рішення.
3) Симптом: дашборди в ClickHouse іноді швидкі, іноді повільні
Корінь: занадто багато дрібних parts і важкі merges; патологічний патерн інжеста.
Виправлення: батчуйте вставки; зменшіть кількість партицій; моніторте parts; забезпечте запас диску; обережно настройте merge-параметри.
4) Симптом: запит ClickHouse читає мільярди рядків для вузького фільтра
Корінь: ORDER BY не відповідає типічним предикатам; поганий data skipping.
Виправлення: перерахуйте order key; розгляньте проєкції або матеріалізовані rollup’и; не реплікуйте OLTP-схему без змін.
5) Симптом: дані в ClickHouse не збігаються з MariaDB
Корінь: CDC робить дублікати, події поза порядком, відсутні видалення/оновлення семантики.
Виправлення: використовуйте ідемпотентні ключі, колонки версій і патерни дедуплікації (наприклад, ReplacingMergeTree); впровадьте перевірки примирення і процеси backfill.
6) Симптом: ClickHouse раптово закінчує диск
Корінь: merges потребують тимчасового простору; не застосовано retention; припущення про стиснення впали через вибір типів даних.
Виправлення: впровадьте TTL/retention; тримайте диск нижче безпечних порогів; використовуйте правильні типи (LowCardinality де підходить), і не зберігайте JSON-блоби як основний план.
7) Симптом: запит MariaDB «оптимізували» новим індексом, але додаток став повільніший
Корінь: додатковий індекс збільшив write amplification; чурн кешу; оптимізатор обрав гірший план для OLTP-запитів.
Виправлення: відкотіть індекс; використовуйте покриваючі індекси лише там, де селективні; відокремте аналітичне навантаження замість додавання індексів.
Чек-листи / покроковий план
Покроково: правильно винести аналітику
-
Інвентарізуйте звітне навантаження.
Зберіть топ-20 запитів, їх частоту і дашборди/експорти, що їх запускають. -
Класифікуйте запити: OLTP-дружні vs скан-важкі аналітичні.
Якщо запит регулярно переглядає десятки мільйонів рядків — це аналітика. -
Визначте цілі свіжості.
«В межах 5 хвилин» — реальна вимога; «реальний час» — слово для зустрічей. -
Виберіть підхід інжесту.
Batch ETL для годинних/дневних звітів; CDC для near-real-time дашбордів. -
Проєктуйте таблиці ClickHouse з огляду на запити.
Виберіть партишинґ іORDER BYна основі патернів фільтрів. -
Почніть з одного домену.
Приклад: події/замовлення для аналітики, а не вся база. -
Побудуйте примирення.
Кількості рядків, суми і «відомо правильні» метрики по ковзаючих вікнах. Аларми при дрейфі. -
Переміщуйте дашборди поступово.
Shadow-run дашбордів проти ClickHouse і порівнюйте результати перед cutover. -
Тротлінг і захист.
Впровадьте таймаути і ліміти конкурентності для звітів у MariaDB під час міграції. -
Оперувати ClickHouse.
Моніторинг, бекапи, планування ємності та runbook’и перш ніж оголосити перемогу.
Чек-лист: що налаштувати в день один (мінімум)
- Увімкнений slow query log MariaDB і щоденний парсинг.
- Ретеншн логів запитів ClickHouse і дашборди для топ-запитів.
- Алерти по використанню диску з консервативними порогами.
- Метрика затримки інжесту + алерт.
- Моніторинг merges/parts.
- Політика, хто може запускати ad-hoc важкі запити і як їх обмежувати.
FAQ
1) Хіба не можна просто додати більше реплік MariaDB і запускати звіти там?
Можна, і багато команд так роблять. Це тимчасове рішення. Ви все одно використовуєте row-store OLTP-рушій для скан-важкої роботи,
і ви все одно боротиметеся з затримками, чурном кешу і дорогим залізом. Якщо звітування стратегічне — побудуйте аналітичну систему.
2) Чи є ClickHouse заміною для MariaDB?
Ні для OLTP. ClickHouse може зберігати багато даних і швидко відповідати на запити, але він не призначений для транзакційних оновлень,
строгих обмежень і частих точкових оновлень так, як MariaDB.
3) Яка найбільша помилка в схемі при переході на ClickHouse?
Імпортувати нормалізовані OLTP-таблиці і очікувати, що joins поведуться як у дата-віаргаузі. Проєктуйте таблиці фактів і обирайте ORDER BY
узгоджено з фільтрами. Зробіть ваші поширені запити дешевими за конструкцією.
4) Наскільки свіжими можуть бути дашборди в ClickHouse?
З CDC і розумним батчуванням «в межах хвилини» — звично. «В межах кількох секунд» можливе, але підвищує операційний ризик,
особливо якщо інжест перетворюється на фабрику дрібних частин.
5) Що з видаленнями і GDPR-стилем стирання?
Плануйте це заздалегідь. Для справжнього стирання може знадобитися цільова mutation або політики TTL залежно від моделі даних.
Якщо є юридичні вимоги до видалення, не нехтуйте ними — проєктуйте пайплайн і сховище з цим на увазі.
6) Чи потрібно передагреговувати все materialized views?
Ні. Передагрегуйте кілька метрик, що гарячі і дорогі, особливо тайли дашбордів з жорсткими латентнісними очікуваннями.
Зберігайте сирі факти для гнучкості, але не дозволяйте кожному дашборду перечитувати сирі дані для тих самих метрик.
7) Як не дати аналітикам «вибити» ClickHouse поганими запитами?
Використовуйте профілі/квоти, лімітуйте пам’ять на запит і надавайте кураторовані таблиці для досліджень.
І ще: навчайте людей спочатку фільтрувати за часом. Культура — це функція продуктивності.
8) Чи «запускати звіти на MariaDB після робочого часу» — довготривала стратегія?
Лише якщо бізнес приймає затримку і ріст набору даних залишається модератним. На практиці звітування розширюється,
«після робочого часу» зникає через різні часові пояси, і ви повертаєтесь сюди. Виносьте раніше, якщо звітування важливе.
9) Що робити, якщо наші «звіти» потребують точної транзакційної узгодженості?
Тоді визначте, які звіти дійсно цього потребують. Більшість — ні. Для декількох, що вимагають, тримайте їх в MariaDB або генеруйте з консистентного snapshot і прийміть затримку.
Не змушуйте кожний дашборд відповідати найжорсткішому вимогам.
10) Який найпростіший перший виграш з ClickHouse?
Винесіть таблиці типу подій (page views, audit, order lifecycle events) і побудуйте пару підсумкових таблиць для топ-дошбордів.
Ви відчуєте негайне полегшення для MariaDB і швидкі виграші по латентності дашбордів.
Практичні наступні кроки
Якщо звіти повільні, ставте це як продакшен-проблему, а не BI-незручність. Ваша OLTP база каже, що робить не ту роботу.
Виміряйте біль, визначте найгірших винуватців і вирішіть, чи оптимізуєте запит, чи змінюєте архітектуру.
Зробіть це далі, в такому порядку:
- Витягніть slow query log і відранжируйте за загальним часом і rows examined.
- Виберіть один репрезентативний «killer report» і підтвердіть, що це scan-heavy агрегат.
- Встановіть ціль свіжості і оберіть Batch ETL або CDC відповідно.
- Побудуйте ClickHouse fact table, спроєктовану навколо топ-фільтрів і group-by дашборда.
- Перемістіть один дашборд, валідуйте результати примиренням, потім ітеруйте.
- Додайте запобіжники (квоти, ліміти пам’яті, моніторинг) перед тим, як відкривати систему для всієї компанії.
Мета не в поклонінні ClickHouse. Мета — припинити карати MariaDB за те, що вона добре робить транзакції.
Дайте кожній системі її доріжку, і ви отримаєте швидші звіти, менше інцидентів і спокійніший on-call.