ClickHouse для просунутих користувачів у питаннях та відповідях

У квітні інженери Авіто збиралися на онлайн-посидіти з головним розробником ClickHouse Олексієм Міловидовим та Кирилом Шваковим, Golang-розробником з компанії Integros. Обговорювали, як ми використовуємо систему управління базами даних та які складнощі у нас виникають.

За мотивами зустрічі ми зібрали статтю з відповідями експертів на наші та глядацькі питання про бекапи, решардинг даних, зовнішні словники, Golang-драйвер та оновлення версій ClickHouse. Вона може бути корисна розробникам, які вже активно працюють із СУБД «Яндекса» та цікавляться її сьогоденням та майбутнім. За замовчуванням відповіді Олексія Міловидова, якщо не написано інше.

Обережно під катом багато тексту. Сподіваємося, що зміст із запитаннями допоможе вам зорієнтуватися.

ClickHouse для просунутих користувачів у питаннях та відповідях

Зміст

Якщо не хочеться читати текст, можна переглянути запис посиденьок на нашому ютуб-каналі. Таймкоди – у першому коментарі під відео.

ClickHouse постійно оновлюється, а наші дані ні. Що з цим робити?

ClickHouse постійно оновлюється, а наші дані, які були optimize final оброблені, не оновлюються і лежать у резервній копії.

Припустимо, у нас сталася якась проблема, і дані були втрачені. Ми вирішили відновитися, і виявилося, що старі партиції, які лежать на збереженні в серверах бекапів, дуже сильно розходяться з версією ClickHouse, що використовується на даний момент. Що робити в такій ситуації, і чи можлива вона?

Ситуація, коли ви відновили з бекапу дані у старому форматі, а на новій версії вони не підключаються, неможлива. Ми стежимо за тим, щоб формат даних у ClickHouse завжди залишався обернено сумісним. Це набагато важливіше, ніж зворотна сумісність по функціональності, якщо змінилася поведінка якоїсь функції, що рідко використовується. Дані, які зберігаються на диску, нова версія ClickHouse завжди має вміти читати. Це закон.

Які найкращі практики є на даний момент щодо резервного копіювання даних із ClickHouse?

Як робити резервні копії з урахуванням того, що у нас є операції optimize final, величезна база даних на терабайти, і дані, які оновлюються, припустимо, за останні три дні, а далі з ними ніяких процедур не відбувається?

Ми можемо зібрати власне рішення і на баші написати: збирай так і так ці резервні копії. Може, мити нічого не треба, і велосипед давно винайдений?

Для початку з приводу найкращих практик. Мої колеги завжди радять у відповідь на запитання про бекапи нагадувати про сервіс «Яндекс.Хмара», де це завдання вже вирішено. Тож користуйтеся ним, якщо є така можливість.

Повного рішення, повністю вбудованого в ClickHouse, для бекапів немає. Є деякі заготівлі, які можна використати. Щоб отримати повне рішення, доведеться або трохи вручну повозитися, або зробити обгортки у вигляді скриптів.

Почну з найпростіших рішень і закінчу найнавороченішими залежно від обсягу даних та розміру кластера. Чим більше кластер – тим складнішим стає рішення.

Якщо таблиця з даними займає лише кілька гігабайт, бекап можна зробити так:

  1. Зберегти визначення таблиць, тобто метадані show create table.
  2. Зробити дамп за допомогою ClickHouse клієнта вибрати * зі столу у файл. За промовчанням ви отримаєте файл у форматі TabSeparated. Якщо хочеться ефективніше — можна у форматі Native.

Якщо обсяг даних більше, то бекап займе більше часу та багато місця. Це називається логічний бекап, він не прив'язаний до формату даних ClickHouse. Якщо він є, то в крайньому випадку ви зможете взяти бекап і завантажити MySQL для відновлення.

Для більш просунутих випадків у ClickHouse вбудована можливість створити снапшот партій у локальній файловій системі. Ця можливість доступна у вигляді запиту alter table freeze partition. Або просто alter table freeze - Це снапшот усієї таблиці.

Снапшот буде створено консистентним для однієї таблиці одному шарді, тобто створити консистентний снапшот всього кластера в такий спосіб неможливо. Але для більшості завдань такої необхідності немає, і достатньо на кожному шарді виконати запит та отримати консистентний снапшот. Він створюється у вигляді хардлінків і тому не займає додаткового місця. Далі цей снапшот ви копіюєте на бекап-сервер або в сховище, яке використовуєте для бекапів.

Відновити такий бекап досить легко. Перше - створюєте таблиці за наявними визначеннями таблиць. Далі копіюєте збережені снапшоти партій у Directory-Detached для даних таблиць і виконуєте запит attach partition. Таке рішення цілком підходить для найсерйозніших обсягів даних.

Іноді потрібно щось ще крутіше - у тих випадках, коли у вас десятки або навіть сотні терабайт на кожному сервері та сотні серверів. Тут є рішення, яке я підглянув у колег із «Яндекс.Метрики». Я не рекомендував би його кожному — прочитайте і самі вирішите, підходить воно чи ні.

Спочатку потрібно створити кілька серверів із великими дисковими полицями. Далі на цих серверах підняти кілька ClickHouse серверів і налаштувати їх так, щоб вони працювали як ще одна репліка для тих самих шардів. І далі використовувати на цих серверах файлову систему або інструмент, який дозволяє створювати снапшоти. Тут є два варіанти. Перший варіант – це LVM снапшоти, другий варіант – ZFS на Linux.

Після цього кожен день потрібно створювати снапшот, він лежатиме і займатиме якесь місце. Звичайно, якщо дані змінюються, то з часом обсяг місця буде збільшуватися. Цей снапшот можна дістати будь-якої миті і відновити дані, ось таке дивне рішення. Плюс ще треба обмежити ці репліки у конфізі, щоб вони не намагалися ставати лідерами.

Чи можна буде організувати контрольоване відставання реплік у валах?

В цьому році ви плануєте робити вали в ClickHouse. Чи можна буде в них організувати контрольоване відставання реплік? Ми хотіли б з його допомогою убезпечити себе від негативних сценаріїв із альтерами та іншими змінами.

Чи можна зробити якісь roll back для альтерів? Наприклад, у існуючому валі взяти і сказати, що до цього моменту застосовуй зміни, а з цього моменту зміни перестань застосовувати?

Якщо в наш кластер прийшла команда і поламала його, то у нас є умовна репліка з відставанням у годину, де ми можемо сказати, що давайте саме її використовувати на даний момент, але останні десять хвилин змін у ній застосовувати не будемо?

Для початку про контрольоване відставання реплік. Такий запит від користувачів був, і ми створили issue на Гітхабі з проханням: "Якщо комусь це треба, ставте лайк, ставте серце". Ніхто не поставив, і цезакрили. Проте вже зараз можна отримати таку можливість, налаштувавши ClickHouse. Щоправда, лише починаючи з версії 20.3.

ClickHouse постійно на тлі злиття даних — мердж. Коли мердж вироблено, деякий набір шматків даних замінюється більший шматок. При цьому шматки даних, які були раніше, залишаються на диску протягом деякого часу.

По-перше, вони продовжують зберігатися до тих пір, поки є запити select, які їх використовують, щоб забезпечити неблокуючу роботу. Запити select спокійно читають із старих шматків.

По-друге, є ще й поріг у часі — старі шматки даних лежать на диску вісім хвилин. Ці вісім хвилин можна налаштувати та перетворити навіть в один день. Це коштуватиме місця на диску: в залежності від потоку даних вийде, що за останній день дані не те що подвоїться, їх може стати вп'ятеро більше. Але ви можете при серйозній проблемі зупинити ClickHouse сервер і з усім розібратися.

Тепер постає питання, як це захищає від альтерів. Тут варто подивитися глибше, тому що у старих версіях ClickHouse альтер працював таким чином, що просто безпосередньо змінював шматки. Є шматок даних із якимись файлами, і ми робимо, наприклад, alter drop column. Тоді цей стовпець фізично видаляється зі всіх шматків.

Але починаючи з версії 20.3 механізм альтерів був повністю змінений, і тепер шматки даних завжди іммутабельні. Вони не змінюються взагалі – альтери тепер працюють приблизно так само, як Мерджі. Замість міняти шматок на місці, ми створюємо новий. У новому шматку файли, які не змінилися, стають хардлінками, і, якщо ми видалили якийсь стовпець, він буде просто відсутній у новому шматку. Старий шматок за замовчуванням відійде через вісім хвилин, і тут можна підкрутити настройки, про які сказано вище.

Те саме стосується альтерів типу мутацій. Коли ви робите alter delete або alter update, він змінює шматок, а створює новий. А потім видаляє старий.

Як бути, якщо структура таблиці змінилася?

Як підняти бекап, який був зроблений зі старою схемою? І друге питання про кейс із снапшотами та засобами файлової системи. Чи підходить тут Btrfs замість ZFS на Linux LVM?

Якщо ви робите attach partition партиції з іншою структурою, то ClickHouse вам скаже, що так не можна. Рішення таке. Перше – створити тимчасову таблицю типу MergeTree зі старою структурою, причепити туди дані за допомогою attach, зробити запит alter. Потім можна або скопіювати або перенести ці дані і зробити attach знову, або використовувати запит alter table move partition.

Тепер друге питання – чи можна використовувати Btrfs. Для початку, якщо у вас є LVM, достатньо LVM снапшотів, а файлова система може бути і ext4, це не має значення. З Btrts все залежить від вашого досвіду експлуатації. Це зріла файлова система, але все одно виникають деякі підозри щодо того, як все відпрацює на практиці у конкретному сценарії. Я б не радив це використовувати, якщо у вас немає Btrfs у продакшні.

Які нині найкращі практики у вирішальній рінг даних?

Питання про перешардування складне та багатогранне. Тут можна відповісти відразу кількома варіантами. Можна зайти з одного боку і сказати так - у ClickHouse вбудованої можливості перешардування немає. Але я боюся, що ця відповідь нікого не влаштує. Тому можна зайти з іншого боку і сказати, що в ClickHouse є багато способів перешардування даних.

Якщо закінчується місце на кластері або він не справляється з навантаженням, ви додаєте нові сервери. Але ці сервери за замовчуванням порожні, даних на них немає, ніякого навантаження не йде. Вам потрібно перекласти дані, щоб вони стали рівномірно розмазані за новим, збільшеним у розмірах кластером.

Перший спосіб, яким це можна зробити, - це скопіювати частину партицій на нові сервери за допомогою запиту alter table fetch partition. Наприклад, були у вас партиції по місяцях, і ви берете перший місяць 2017 року і копіюєте на новий сервер, потім третій місяць копіюєте на якийсь новий сервер. І так робите, поки не стане більш-менш поступово.

Перенесення можна здійснювати лише тих партицій, які змінюються під час запису. Для нових партицій запис доведеться відключити, тому що їх перенесення не атомарне. Інакше ви отримаєте дублікати або перепустки в даних. Проте цей спосіб практичний і працює досить ефективно. Через мережу передаються вже готові стислі партиції, тобто дані не перетискаються і не перекодуються.

Цей спосіб має один недолік, і він залежить від схеми шардування, чи закладалися ви на цю схему шардування, який у вас був ключ шардування. У вашому прикладі для випадку з метриками ключ шардування це хеш від шляху. Коли у вас йде select в Distributed таблицю, він іде відразу на всі шарди кластера і забирає звідти дані.

Це означає, що практично вам не має значення, які дані на якому шарді опинилися. Головне те, що дані по одному шляху виявляються на одному шарді, а ось яким саме, не є принциповим. У цьому випадку перенесення готових партицій чудово підходить, тому що при запитах select ви теж - що до перешардування, що після, схема значення особливо не має - отримуватимете повні дані.

Але бувають випадки і складніші. Якщо на рівні логіки програми ви закладаєтеся на спеціальну схему шардування, що цей клієнт розташований на такому шарді, і запит можна відправити відразу туди, а не в Distributed таблицю. Або ви використовуєте досить свіжу версію ClickHouse та увімкнули налаштування optimize skip unused shards. У цьому випадку під час запиту select вираз у секції де буде проаналізовано, і буде обчислено, на які шарди необхідно піти згідно зі схемою шардування. Це працює за умови, що дані розкладені саме відповідно до цієї схеми шардування. Якщо їх переклали вручну, відповідність може змінитися.

Отже, це спосіб номер один. І я чекаю вашої відповіді, підходить спосіб, чи йдемо далі.

Володимир Колобаєв, lead system administrator в Авіто: Олексій, той спосіб, який ви згадали не дуже добре лягає, коли треба розмазати навантаження навіть на читання Ми можемо взяти партію, яка місячна і можемо попередній місяць віднести на іншу ноду, але коли прийде запит за цими даними, ми навантажуватимемо тільки її. А хотілося б навантажувати весь кластер, тому що, в іншому випадку, у нас якийсь час все навантаження на читання оброблятиметься двома шардами.

Олексій Міловидов: Відповідь тут дивна — так, погано, але може й покатати. Поясню, як саме. Варто подивитися на сценарій навантаження, що йде за вашими даними. Якщо це дані моніторингу, то майже напевно можна сказати, що переважна більшість запитів іде за свіжими даними.

Ви поставили нові сервери, перенесли старі партиції, але змінили те, як записуються свіжі дані. І нові дані будуть розмащені по всьому кластеру. Таким чином, вже через п'ять хвилин запити за останні п'ять хвилин рівномірно навантажуватимуть кластер, через день запити за добу рівномірно навантажуватимуть кластер. А запити за попередній місяць, на жаль, підуть лише на частину серверів кластеру.

Але найчастіше у вас не буде запитів саме за лютий 2019 року. Швидше за все, якщо вже запити йдуть у 2019 році, то вони будуть за весь 2019 — за великий інтервал часу, а не за якийсь маленький діапазон. І такі запити також зможуть поступово навантажити кластер. Але в цілому ваше зауваження цілком вірно, що це таке рішення, яке не розмазує дані повністю рівномірно.

Я маю ще кілька пунктів для відповіді на запитання. Один з них про те, як спочатку зробити схему шардування такою, щоб від перешардування було менше болю. Це можливо не завжди.

Наприклад, у вас є дані моніторингу. Дані моніторингу зростають із трьох причин. Перша – накопичення історичних даних. Друга – зростання трафіку. І третя – збільшення кількості тих речей, що підпадають під моніторинг. З'являються нові мікросервіси та метрики, які потрібно зберігати.

Можливо, що з них найбільше зростання пов'язане саме з третьою причиною — збільшення використання моніторингу. І тут варто подивитися на характер навантаження, які основні запити на select. Основні запити на select, швидше за все, будуть йти деякою підмножиною метрик.

Наприклад, використання CPU на якихось серверах якимось сервісом. Виходить, що є деяке підмножина ключів, яким ви ці дані дістаєте. І сам запит за цими даними, швидше за все, досить простий і виконується за десятки мілісекунд. Використовується для сервісів моніторингу, дашбордів. Сподіваюся, я це правильно розумію.

Володимир Колобаєв: Справа в тому, що ми дуже часто апелюємо до історичних даних, оскільки ми в режимі реального часу порівнюємо поточне становище з історичним. І для нас важливо мати швидкий доступ до великого обсягу даних, і ClickHouse з цим чудово справляється.

Ви маєте рацію, більшість запитів на читання ми відчуваємо за останній день, як і будь-яка система моніторингу. Але при цьому на історичні дані навантаження теж досить велике. Вона в основному від системи аллертингу, яка кожні тридцять секунд ходить і каже ClickHouse: Віддай мені дані за останні шість тижнів. А тепер побудуй мені з них якусь ковзну середню, і давай порівняємо поточне значення з історичним».

Я хотів би сказати, що у нас для таких дуже свіжих запитів існує ще одна маленька табличка, в якій ми зберігаємо лише два дні даних, і основні запити летять до неї. У велику шардовану таблицю ми надсилаємо лише великі історичні запити.

Олексій Міловидов: На жаль, для вашого сценарію виходить погано, але я розповім опис двох поганих і складних схем шардування, які не потрібно використовувати, але які використовуються в сервісі моїх друзів.

Є основний кластер із подіями «Яндекс.Метрики». Події – це перегляди сторінок, кліки та переходи. Більшість запитів йде на конкретний веб-сайт. Ви відкриваєте сервіс «Яндекс.Метрика», у вас є сайт — avito.ru, заходьте на звіт, і йде запит на ваш сайт.

Але є й інші запити — аналітичні та глобальні, які роблять внутрішні аналітики. Про всяк випадок зазначу, що внутрішні аналітики роблять запити лише за сервісами «Яндекса». Проте навіть сервіси «Яндекса» займають істотну частку від усіх даних. Це запити не за конкретними лічильниками, а щодо ширшої фільтрації.

Як організувати дані таким чином, щоб по одному лічильнику все працювало ефективно, і глобальні запити теж? Складність ще полягає в тому, що кількість запитів у ClickHouse на кластер "Метрики" - кілька тисяч на секунду. При цьому нетривіальних запитів, наприклад, кілька тисяч на секунду, один сервер ClickHouse не тягне.

Розмір кластера - шістсот із чимось серверів. Якщо над цим кластером просто натягнути Distributed таблицю і відправляти туди кілька тисяч запитів, стане ще гірше, ніж відправляти їх на один сервер. З іншого боку, варіант з тим, що дані розмазані поступово, а ми йдемо і запитуємо з усіх серверів, відразу відкидаємо.

Є варіант діаметрально протилежний. Уявіть, якщо ми дані шардуватимемо по сайтах, і запит для одного сайту піде на один шард. Тепер кластер цілком зможе витягнути десять тисяч запитів на секунду, але на одному шарді якийсь один запит працюватиме надто повільно. Він уже не масштабуватиметься за пропускною спроможністю. Особливо, якщо це сайт avito.ru. Я не розкрию секрету, якщо скажу, що Авіто - це один з найбільш відвідуваних сайтів у рунеті. І обробляти його на одному шарді було б безумством.

Тому схема шардування влаштована хитрішим чином. Весь кластер розбитий на кілька кластерків, які ми називаємо шарами. Усередині кожного кластеру від десятка до кількох десятків шардів. А лише таких кластерків тридцять дев'ять.

Як це все масштабується? Кількість кластерів не змінюється - як було кілька років тому тридцять дев'ять, так і лишилося. Але всередині кожного з них ми поступово збільшуємо кількість шардів у міру накопичення даних. І схема шардування в цілому така - розбиття на ці кластерки йде по веб-сайтах, і для того, щоб зрозуміти, який сайт на якому кластері, використовується взагалі окрема метабаза MySQL. Один сайт – на одному кластерці. А всередині нього шардування йде ідентифікаторами відвідувачів.

При записі ми їх розбиваємо залишком від поділу ідентифікатора відвідувача. Але при додаванні нового шарда схема шардування змінюється, ми продовжуємо розбивати, але за залишку від поділу інше число. Це означає, що один відвідувач вже таки розташований на декількох серверах, і закладатися на це не можна. Це зроблено виключно для того, щоб дані краще стискалися. А при запитах ми йдемо до Distributed таблиці, яка дивиться на кластерок і звертається до десятків серверів. Ось така безглузда схема.

Але моя розповідь буде неповною, якщо я не скажу, що від цієї схеми ми відмовилися. У новій схемі ми змінили всі дані скопіювали за допомогою clickhouse-copier.

У новій схемі всі сайти поділяються на дві категорії – великі та маленькі. Я не знаю, як там вибрано поріг, але в результаті вийшло так, що великі сайти записуються на один кластер, де 120 шардів по три репліки в кожному — тобто 360 серверів. І схема шардування така, що будь-який запит іде відразу на всі шарди. Якщо ви зараз в Яндекс.Метриці відкриєте будь-яку сторінку звіту для avito.ru, запит піде на 120 серверів. Великих сайтів у рунеті мало. І запитів виходить не тисяча за секунду, а навіть менше сотні. Все це спокійно прожовує Distributed таблиця, яку кожен із них обробляє 120 серверами.

А другий кластер – для маленьких сайтів. Тут схема шардування за ідентифікатором сайту, і кожен запит йде рівно на один шард.

У ClickHouse є утиліта clickhouse-copier. Можете про неї розповісти?

Відразу скажу, що це рішення є більш громіздким і дещо менш продуктивним. Перевага в тому, що вона розмазує дані повністю згідно з тією схемою, яку ви вкажете. Але недолік утиліти в тому, що вона зовсім не перешардує. Вона копіює дані з однієї схеми кластера до іншої схеми кластера.

Це означає, що для її роботи у вас має бути два кластери. Вони можуть бути розташовані на однакових серверах, проте дані не будуть переміщатися інкрементально, а будуть скопійовані.

Наприклад, було чотири сервери, стало вісім. Ви створюєте на всіх серверах нову Distributed таблицю, нові локальні таблиці та запускаєте clickhouse-copier, вказуючи в ньому схему роботи, що він повинен читати звідти, прийняти нову схему шардування та перекладати дані туди. І вам на старих серверах потрібно місця в півтора рази більше, ніж є зараз, тому що старі дані повинні залишатися на них, і зверху на них приїде половинка від цих же старих даних. Якщо ви заздалегідь подумали про те, що дані потрібно перешардувати і місце є, такий спосіб підійде.

Як усередині влаштований clickhouse-copier? Він розбиває всю роботу на набір завдань з обробки однієї партиції однієї таблиці однією шарді. Всі ці завдання можуть виконуватися паралельно, і clickhouse-copier може бути запущений на різних машинах у кількох примірниках, але те, що він робить для однієї партиції - це не що інше, як insert select. Дані читаються, розтискаються, перерозбиваються, потім знову стискаються, записуються кудись, пересортуються. Це важче рішення.

У вас була пілотна штука, яка називалася решардінг. Що з нею?

У вас була ще в 2017 році пілотна штука, яка називалася решардінг. Навіть є опція у ClickHouse. Я так розумію, це не злетіло. Можете розповісти, чому так вийшло? Начебто дуже актуально.

Вся проблема в тому, що при необхідності перешардувати дані на місці, потрібна дуже складна синхронізація для того, щоб зробити це атомарно. Коли ми почали дивитись на те, яким чином ця синхронізація влаштована, стало ясно, що є фундаментальні проблеми. І ці фундаментальні проблеми не лише теоретичні, а й одразу ж почали показувати себе на практиці у вигляді того, що можна пояснити дуже просто – нічого не працює.

Чи можна зливати всі частини даних до переміщення на повільні диски?

Питання TTL з опцією move to slow disk в контексті мерджей. Чи є спосіб, крім по cron, зливати всі частини в одну перед переміщенням на повільні диски?

Відповідь на запитання, чи можна якось автоматично склеювати всі шматки в один до їхнього перенесення — ні. Мені здається, у цьому немає потреби. Можна і не зливати всі частини в одну, а просто розраховувати на те, що вони переноситимуться на повільні диски автоматично.

У нас два критерії правил перенесення. Перший - у міру заповнення. Якщо на поточному рівні сховища менше відсотка вільного місця, ми вибираємо один шматок і переносимо його на більш повільне сховище. Точніше не повільніше, а наступне — як налаштуєте.

Другий критерій - за розміром. Він про перенесення великих шматків. Ви можете відрегулювати поріг вільного місця на швидкому диску, і дані будуть переноситися автоматично.

Як переїжджати на нові версії ClickHouse, якщо немає можливості заздалегідь перевірити сумісність?

Ця тема регулярно обговорюється у телеграм-чаті ClickHouse з урахуванням різних версій, та все ж. Наскільки безпечно оновлюватись з версії 19.11 на 19.16 та, наприклад, з 19.16 на 20.3. Як краще переїжджати на нові версії, не маючи нагоди заздалегідь перевірити сумісність у пісочниці?

Тут кілька «золотих» правил. Перше - читайте changelog. Він великий, але там є окремі пункти про несумісні зміни. Не варто ставитись до цих пунктів як до червоного прапора. Зазвичай це дрібні несумісності, які пов'язані з деякою крайовою функціональністю, яка, ймовірно, у вас не використовується.

Друге – якщо немає можливості перевірити сумісність у пісочниці, і ви хочете оновитися одразу у продакшні, рекомендація така – не треба цього робити. Спочатку створіть пісочницю та перевірте. Якщо тестового середовища немає, то у вас, швидше за все, не дуже велика компанія, а значить, можна скопіювати частину даних на свій ноутбук і на ньому переконатися, що все коректно працює. Можна навіть підняти кілька реплік локально своєю машиною. Або можна десь поряд підняти нову версію, і залити туди частину даних — тобто зробити імпровізоване тестове середовище.

Ще одне правило - не оновлюватися протягом тижня після виходу версії через вилов багів у продакшні і наступних швидких фіксів. Розберемося з нумерацією версій ClickHouse, щоб не заплутатися.

Є версія 20.3.4. Число 20 позначає рік випуску - 2020. З погляду того, що всередині, це ніякого значення не має, тому звертати увагу на це не будемо. Далі – 20.3. Другу цифру – у цьому випадку 3 – ми збільшуємо щоразу, коли випускаємо реліз із якоюсь новою функціональністю. Якщо ми хочемо додати до ClickHouse якусь можливість, ми повинні збільшити це число. Тобто у версії 20.4 ClickHouse працюватиме ще краще. Третя цифра – 20.3.4. Тут 4 - це кількість патч-релізів, в яких ми нових можливостей не додавали, але виправляли якісь баги. І 4 означає, що ми зробили це чотири рази.

Не варто думати, що це щось жахливе. Зазвичай користувач може встановити найсвіжішу версію, і вона буде працювати без проблем з аптаймом на рік. Але уявіть, що в будь-якій функції для обробки бітмапів, яку було додано нашими китайськими товаришами, при передачі неправильних аргументів сервер падає. Ми повинні це виправити. Ми випустимо нову патч-версію, і ClickHouse стане стабільнішим.

Якщо у вас ClickHouse працює в продакшні, і виходить нова версія ClickHouse з додатковими фічами - наприклад, 20.4.1 - перша, не поспішайте ставити її в продакшн в перший же день. Навіщо вона взагалі потрібна? Якщо ви ще не використовуєте ClickHouse, ви можете її встановити, і, швидше за все, все буде добре. Але якщо ClickHouse вже стабільно працює, то слідкуйте за патчами та оновленнями – які проблеми ми виправляємо.

Кирило Шваков: Хочу доповнити трохи про тестові середовища. Всі дуже бояться тестових середовищ і чомусь вважають, що, якщо у вас дуже великий кластер ClickHouse, то і тестове середовище має бути не менше або хоча б удесятеро менше. Це зовсім негаразд.

Можу сказати на своєму прикладі. У мене є проект і там є ClickHouse. Наше тестове середовище саме для нього – це маленька віртуалка у Hetzner за двадцять євро, де абсолютно все розгорнуто. Щоб це робити, ми маємо повну автоматизацію в Ansible, і тому в принципі немає різниці, куди котити — на залізні сервери або просто розвернутися у віртуалках.

Що можна зробити? Було б непогано зробити в документації ClickHouse приклад, як розгорнути у себе невеликий кластер — у Docker, LXC, можливо, створити Ansible playbook, тому що у різних людей різні деплої. Це багато спростить. Коли ти береш і за п'ять хвилин розгортаєш кластер, набагато простіше намагатися чимось розібратися. Так набагато зручніше, тому що котити в прод версію, яку ти не перевірив, — це шлях у нікуди. Іноді це працює, інколи ж ні. І тому сподіватися на успіх погано.

Максим Котяков, senior backend engineer Авіто: Трохи доповню про тестові середовища із серії проблем великих компаній. У нас є повноцінний приймальний кластер ClickHouse, за схемами даних та налаштуваннями точна копія того, що є в продакшні. Цей кластер розгорнутий у досить жухлих контейнерах із мінімумом ресурсів. Ми пишемо туди деякий відсоток від продакшн-даних, благо є можливість відреплікувати в Кафці потік. Там все синхронізовано і відскейлено — і за потужностями, і за потоком, і, теоретично, за інших рівних має поводитися по метриках як продакшн. Все потенційно вибухонебезпечне спочатку котиться на цей стенд і кілька днів там наполягає до готовності. Але природно, це рішення дороге, важке та з ненульовими витратами на підтримку.

Олексій Міловидов: Розкажу, що є тестовим середовищем наших друзів з «Яндекс.Метрики». Один кластер був на 600 із чимось серверів, інший на 360, і є ще третій та кілька кластерів. Тестове середовище для одного з них - це просто дві шарди з двома репліками в кожному. Чому два шарди? Щоби був не один. І репліки теж щоби були. Просто деяка мінімальна кількість, яку можна собі дозволити.

Це тестове середовище дозволяє перевірити працездатність запитів і чи не зламалося щось по-великому. Але часто проблеми виникають зовсім іншого характеру, коли все працює, але є деякі невеликі зміни із навантаженням.

Наведу приклад. Вирішили встановити нову версію ClickHouse. Вона викладена на тестове середовище, пройдено автоматизовані тести в самій «Яндекс.Метриці», які порівнюють дані на старій версії та новій, проганяючи весь конвеєр. І, природно, зелені тести нашого CI. Інакше ми навіть не запропонували б цю версію.

Все чудово. Починаємо котити у продакшн. Мені надходить повідомлення, що на графіках навантаження зросло в кілька разів. Ми відкочуємо версію. Я дивлюся на графік і бачу: навантаження справді виросло в кілька разів під час викочування, і назад зменшилося, коли викотили. Потім ми почали відкочувати версію. І навантаження так само збільшилося і так само назад впало. Отже, висновок такий — навантаження виросло у зв'язку з викладкою, нічого дивного.

Далі було складно переконати колег таки встановити нову версію. Я говорю: «Все нормально, викочуйте. Тримайте кулачки, все працюватиме. Зараз навантаження виросло на графіках, але все нормально. Тримайтеся». Загалом, ми так зробили, і все версія викладена на продашкн. Але майже при кожній викладці виникають схожі проблеми.

Kill query має вбивати запити, але він цього не робить. Чому?

До мене прийшов користувач, якийсь аналітик, і створив запит, який поклав мій кластер ClickHouse. Якусь ноду чи кластер цілком — залежно від того, яку репліку чи шард запит потрапив. Я бачу, що всі ресурси CPU на цьому сервері в полку, все червоне. При цьому сам ClickHouse відповідає на запити. І я пишу: «Покажи мені, будь ласка, process list, який запит породив це божевілля».

Я знаходжу цей запит та пишу йому kill. І бачу, що нічого не відбувається. Мій сервер у полиці, ClickHouse далі віддає мені якісь команди, показує, що сервер живий, і все чудово. Але у мене деградація у всіх запитах користувача, починається деградація по запису в ClickHouse, і мій kill query не відпрацьовує. Чому? Я думав, що kill query має вбивати запити, а цього не відбувається.

Нині буде досить дивна відповідь. Справа в тому, що kill query не вбиває запитів.

Kill query виставляє маленький прапорець під назвою "я хочу, щоб цей запит був убитий". А сам запит під час обробки кожного блоку дивиться на цей прапорець. Якщо його виставлено, запит припиняє працювати. Виходить, що ніхто запиту не вбиває, він сам повинен все перевірити і зупинитися. І це має працювати у всіх випадках, коли запит перебуває у стані обробки блоків даних. Він опрацює наступний блок даних, перевірить прапорець та зупиниться.

Це не працює у випадках, коли запит заблоковано на якійсь операції. Щоправда, швидше за все, це не ваш випадок, тому що, за вашими словами, він використовує купу ресурсів сервера. Можливо, що це не працює у разі зовнішнього сортування та ще в деяких деталях. Але загалом такого не повинно бути, це баг. І єдине, що можу порадити, – оновлювати ClickHouse.

Як розрахувати час відповіді при навантаженні, що читає?

Є таблиця, в якій зберігаються агрегати по item – різні лічильники. Кількість рядків – приблизно сто мільйонів. Чи можна розраховувати на передбачуваний час відповіді, якщо налити 1K RPS по 1K item'ів?

Судячи з контексту, йдеться про навантаження, що читає, тому що на запис проблем ніяких — хоч тисячу, хоч сто тисяч, а іноді й кілька мільйонів рядків можна вставляти.

Запити, що читають, бувають різні. У select 1 ClickHouse може виконувати близько десятків тисяч запитів на секунду, тому навіть запити по одному ключу вже потребують деяких ресурсів. І такі точкові запити будуть складнішими, ніж у якихось key-value базах даних, тому що на кожне читання необхідно прочитати блок даних за індексом. Індекс у нас адресує не кожен запис, а кожен діапазон. Тобто доведеться почитати весь діапазон – це 8192 рядки за умовчанням. І доведеться розтиснути стислий блок даних із 64 Кб до 1 Мб. Зазвичай такі точкові запити займають від кількох мілісекунд. Але це найпростіший варіант.

Спробуємо зробити просту арифметику. Якщо помножити кілька мілісекунд на тисячу, вийде кілька секунд. Начебто тримати тисячу запитів за секунду не можна, але насправді можна, бо маємо кілька процесорних ядер. Так що в принципі 1000 RPS ClickHouse іноді може тримати, але на коротких запитах саме точкових.

Якщо потрібно масштабувати кластер ClickHouse за кількістю простих запитів, то я рекомендую найпростіше — збільшити кількість реплік та надсилати запити на випадкову репліку. Якщо одна репліка у вас тримає п'ятсот запитів за секунду, що цілком реально, то три репліки триматимуть півтори тисячі.

Іноді, звичайно, можна і ClickHouse налаштувати на максимальну кількість точкових читань. Що для цього потрібно? Перше – зменшити гранульованість індексу. У цьому зменшувати її слід до одиниці, та якщо з розрахунку, що кількість записів в індексі буде кілька мільйонів чи десятків мільйонів на сервер. Якщо в таблиці сто мільйонів рядків, то як гранульованість можна виставити 64.

Можна зменшити розмір стисненого блоку. Для цього є налаштування min compress block size, max compress block size. Їх можна зменшити, переналити дані, і тоді точкові запити будуть швидшими. Але все одно ClickHouse - це не key-value база даних. Велика кількість невеликих запитів - це антипаттерн навантаження.

Кирило Шваков: Дамо пораду на випадок, якщо там звичайні екаунтери. Це досить стандартна ситуація, коли в ClickHouse зберігають якийсь лічильник. У мене є користувач, він із такої країни, ще якесь третє поле, і потрібно інкрементно щось збільшувати. Берете MySQL, робите унікальний ключ - у MySQL він duplicate key, а в PostgreSQL він conflict - і плюсиком додаєте. Це буде працювати значно краще.

Коли у вас небагато даних, сенсу використовувати ClickHouse особливо немає. Є звичайні бази даних і вони добре з цим справляються.

Що підтюнити в ClickHouse, щоб більше даних було в кеші?

Уявимо ситуацію — на серверах є 256 Гб ОЗУ, у щоденній рутині ClickHouse бере приблизно 60—80 Гб, у піку — до 130. Що можна включити та підтюнити, щоб більше даних було в кеші і, відповідно, було менше походів на диск?

Як правило, page cache операційної системи добре справляється із цим завданням. Якщо ви відкриваєте просто топ, дивіться там cached або free – там теж написано, скільки закешовано – можна помітити, що вся вільна пам'ять використана під кеш. І ці дані під час читання читатимуться не з диска, а з оперативної пам'яті. При цьому можу сказати, що кеш використовується ефективно, тому що кешуються саме стислі дані.

Тим не менш, якщо ви хочете прискорити деякі прості запити ще більше, можна включити всередині ClickHouse кеш в розтиснутих даних. Це називається uncompressed cache. У конфігураційному файлі config.xml виставляєте uncompressed cache size в потрібне вам значення - я раджу не більше половини вільної оперативної пам'яті, тому що інше піде під page cache.

Крім того, є два налаштування рівня запиту. Перше налаштування use uncompressed cache - Включає його використання. Її рекомендується включати для всіх запитів, крім важких, які можуть всі дані прочитати і цей кеш вимити. І друге налаштування - це щось на зразок максимальної кількості рядків для використання кешу. Вона автоматично обмежує великі запити, щоб вони були повз кеш.

Як можна настроїти storage_configuration для зберігання в оперативній пам'яті?

У новій документації ClickHouse я прочитав розділ, пов'язаний з data storage. В описі є приклад із fast SSD.

Цікаво, як можна конфігурувати те саме з volume hot memory. І ще одне питання. Як працює select з такою організацією даних, чи зчитуватиме він весь набір або тільки той, що лежить на диску, і чи стискаються ці дані в пам'яті? І як відпрацьовує секція prewhere на такій організації даних?

Ця настройка впливає на зберігання шматків даних, і їх формат не змінюється.
Давайте розглянемо докладніше.

Налаштувати зберігання даних в оперативній пам'яті можна. Все, що конфігурується для диска, - це його шлях. Ви створюєте tmpfs розділ, який підмонтований якимось шляхом у файловій системі. Вказуєте цей шлях як шлях для зберігання даних для гарячого розділу, туди починають надходити і записуватися шматки даних, все добре.

Але я не рекомендую так робити через низьку надійність, хоча якщо у вас мінімум три репліки в різних дата-центрах, то можна. Якщо що, дані буде відновлено. Уявімо, що сервер раптом вимкнули та включили назад. Розділ підмонтувався знову, але там порожнеча. ClickHouse сервер при старті бачить, що у нього ці шматки відсутні, хоча згідно метаданим ZooKeeper вони повинні бути. Він дивиться, на яких репліках вони є, запитує їх і завантажує. Таким чином, дані будуть відновлені.

У цьому сенсі зберігання даних в оперативній пам'яті принципово не відрізняється від їх зберігання на диску, тому що при записі даних на диск вони теж спочатку потрапляють у page cache і фізично записуються відкладено. Це залежить від варіанта монтування файлової системи. Але про всяк випадок скажу, що ClickHouse не робить fsync при insert.

При цьому дані в оперативній пам'яті зберігаються в такому ж форматі, що і на диску. Запит select так само вибирає шматки, які необхідно читати, в шматках вибирає необхідні діапазони даних, і читає їх. І prewhere працює абсолютно так само, незалежно від того, були дані в оперативній пам'яті або на диску.

До якої кількості унікальних значень ефективний Low Cardinality?

Low Cardinality хитро влаштований. Він складає словники даних, але вони локальні. По-перше, словники свої для кожного шматка, по-друге, навіть усередині одного шматка вони можуть бути різними для кожного діапазону. Коли кількість унікальних значень досягає порогової кількості - на мою думку, один мільйон - словник просто відкладається, і створюється новий.

Відповідь загалом: для кожного локального діапазону, скажімо, для кожного дня, десь до мільйона унікальних значень Low Cardinality ефективна. Після цього буде просто fallback, при якому буде використовуватися багато різних словників, а не один. Працювати воно буде приблизно так само, як звичайний стовпець типу string, можливо, дещо менш ефективно, але серйозної деградації продуктивності не станеться.

Які найкращі практики з повнотекстового пошуку за таблицею з п'ятьма мільярдами рядків?

Існують різні варіанти відповіді. Перший – сказати, що ClickHouse – це не система для повнотекстового пошуку. Для цього є спеціальні системи, наприклад, Elasticsearch и Сфінкс. Тим не менш, я все частіше зустрічаю людей, які кажуть, що переходять з Elasticsearch на ClickHouse.

Чому таке відбувається? Вони пояснюють це тим, що Elasticsearch перестає на деяких обсягах справлятися з навантаженням, починаючи з побудови індексів. Індекси стають занадто громіздкими, і якщо просто перекласти дані в ClickHouse, вийде, що вони зберігаються в кілька разів ефективніше за обсягом. При цьому пошукові запити найчастіше були не такі, що потрібно знайти у всьому обсязі даних якусь фразу з урахуванням морфології, а зовсім інші. Наприклад, знайти за останні кілька годин у логах за якоюсь підпослідовністю байт.

У цьому випадку ви у ClickHouse створюєте індекс, першим полем у якому буде дата з часом. І найбільше відсікання даних буде саме по діапазону дат. Усередині обраного діапазону дат вже, як правило, можна здійснити повнотекстовий пошук навіть за допомогою like брутфорс-методом. Оператор like у ClickHouse – це найефективніший оператор like, який ви можете знайти. Якщо знайдете краще, скажіть мені.

Але все одно like це full scan. І full scan може бути повільним не лише з CPU, а й по диску. Якщо раптом у вас один терабайт даних на добу і ви за добу шукаєте якесь слово, то доведеться відсканувати терабайт. А він, напевно, на звичайних жорстких дисках, і в результаті вони будуть завантажені так, що ви на цей сервер по SSH не зайдете.

І тут я готовий запропонувати ще один маленький трюк. Він із розряду експериментальних — може спрацює, а може ні. У ClickHouse є повнотекстові індекси у вигляді триграмних Блум-фільтрів. Наші колеги з компанії Arenadata вже випробували ці індекси, і вони часто працюють саме так, як призначено.

Для того щоб правильно їх використовувати, слід добре розуміти, як саме вони працюють: що собою являє триграмний Блум-фільтр і як вибрати його розмір. Можу сказати, що вони допоможуть для запитів з якихось рідкісних фраз, підрядків, які рідко зустрічаються в даних. У цьому випадку за індексами буде обрано піддіапазони, і буде прочитано менше даних.

Нещодавно в ClickHouse з'явилися ще більш сучасні функції для повнотекстового пошуку. Це, по-перше, пошук відразу купи підрядок за один прохід, у тому числі варіанти з урахуванням регістру, без урахування регістру, з підтримкою UTF-8 або тільки для ASCII. Вибирайте найефективніший, який вам потрібний.

З'явився пошук кількох регулярних висловів за один прохід. Вам не потрібно писати X like один підрядок or X like інший підрядок. Відразу пишете, і все виконується максимально ефективно.

Третє – тепер є наближений пошук регекспів та наближений пошук підрядків. Якщо хтось написав слово з друкарською помилкою, воно шукатиметься за максимальною відповідністю.

Як краще організувати доступ до ClickHouse для великої кількості користувачів?

Розкажіть, як краще організувати доступ для великої кількості споживачів та аналітиків. Як сформувати чергу, пріоритизувати запити max concurrent queries і якими інструментами?

Якщо кластер досить великий, то добрим рішенням буде підняти ще два сервери, які стануть точкою входу для аналітиків. Тобто не пускати аналітиків на конкретні шарди кластера, а просто створити два порожні сервери, без даних, і на них уже налаштувати права доступу. При цьому параметри користувачів при розподілених запитах передаються на віддалені сервери. Тобто, ви налаштовуєте все на цих двох серверах, і налаштування мають ефект на всьому кластері.

В принципі ці сервери без даних, але обсяг оперативної пам'яті на них дуже важливий для виконання запитів. Диск також може використовуватися для тимчасових даних, якщо увімкнено зовнішню агрегацію або зовнішнє сортування.

Важливо подивитися на налаштування, пов'язані з усіма можливими лімітами. Якщо я зараз зайду на кластер «Яндекс.Метрики» як аналітик і запитаю запит select count from hits, то мені відразу видадуть виняток, що виконати запит я не можу. Максимальна кількість рядків, яку я дозволю сканувати, — це сто мільярдів, а всього на кластері їх п'ятдесят трильйонів в одній таблиці. Це перше обмеження.

Допустимо, я приберу обмеження за кількістю рядків і виконаю запит повторно. Тоді я побачу наступний виняток - включено налаштування force index by date. Я не можу виконати запит, якщо я не вказав діапазон дат. Не потрібно розраховувати на те, що аналітики вказуватимуть його вручну. Типовий випадок - написаний діапазон дат, where event date between тиждень. А потім просто не там вказали дужку і замість and вийшов or — or URL match. Якщо обмеження немає, воно піде сканувати стовпець URL-адреси і витратить просто тонну ресурсів.

Крім того, у ClickHouse є два налаштування пріоритетів. На жаль, вони дуже примітивні. Одна називається просто пріоритет. Якщо priority ≠ 0, і виконуються запити з якимось пріоритетом, але при цьому виконується запит з пріоритетом, який має значення менше, що означає вищий пріоритет, то запит зі значенням пріоритету більше, що означає нижчий пріоритет, просто зупиняється і не працюватиме взагалі за цей час.

Це дуже грубе налаштування, і воно не підходить для тих випадків, коли на кластері є постійне навантаження. Але якщо у вас короткі, імпульсні запити важливі, а в основному кластер простоює, таке налаштування підійде.

Наступне налаштування пріоритетів називається OS thread priority. Вона просто виставляє всім потоків виконання запиту величину nice для шедулера Linux. Працює вона так собі, але таки працює. Якщо виставити найменше значення nice - воно найбільше за величиною, і значить найнижчий пріоритет - а у запитів з високим пріоритетом виставити -19, то CPU споживатиме низькопріоритетні запити приблизно в чотири рази менше, ніж високопріоритетні.

Ще потрібно налаштувати максимальний час виконання запиту, скажімо, п'ять хвилин. Мінімальну швидкість виконання запиту – це найкрутіше. Ця настройка є давно, і вона потрібна, щоб не просто стверджувати, що ClickHouse не гальмує, а щоб це форсувати.

Уявіть, ви налаштовуєте: якщо якийсь запит обробляє менше одного мільйона рядків на секунду, то так робити не можна. Це ганьбить наше добре ім'я, нашу добру базу даних. Давайте це просто заборонимо. Там насправді два налаштування. Одна називається min execution speed — у рядках за секунду, а друга називається timeout before checking min execution speed — за умовчанням п'ятнадцять секунд. Тобто п'ятнадцять секунд можна, а потім, якщо повільно, просто кинути виняток — перервати запит.

Ще потрібно налаштувати квоти. У ClickHouse є вбудована можливість квот, яка вважає споживання ресурсів. Але, на жаль, не залізних ресурсів типу CPU, дисків, а логічних – кількість оброблених запитів, рядків та прочитаних байт. І можна налаштувати, наприклад, максимум сто запитів протягом п'яти хвилин та тисячу запитів на годину.

Чому це важливо? Тому що частина запитів аналітики будуть виконувати вручну безпосередньо з ClickHouse клієнта. І все буде добре. Але якщо у вас в компанії просунуті аналітики, вони напишуть скрипт, а в скрипті може бути помилка. І ця помилка призведе до того, що запит виконається у нескінченному циклі. Від цього треба захиститися.

Чи можна надати результати одного запиту десяти клієнтам?

У нас є кілька користувачів, які люблять приходити з дуже великими запитами в той самий момент часу. Запит великий, виконується в принципі швидко, але через те, що таких запитів одночасно багато, стає дуже боляче. Чи можна той самий запит, який прилетів десять разів поспіль, виконати один раз, а результат його віддати десяти клієнтам?

Проблема в тому, що саме результатів кешу чи кешу проміжних даних ми не маємо. Є page cache операційної системи, яка дозволить не читати дані з диска повторно, але, на жаль, дані все одно розтискатимуться, десеріалізуватимуться і оброблятимуться повторно.

Хотілося б якось цього уникнути, або кешуючи проміжні дані, або вибудувавши схожі запити в якусь чергу і додавши кеш результатів. Зараз у нас є в розробці один pull request, який додає кеш запитів, але тільки для підзапитів у секції in і join — рішення неповноцінне.

Тим не менш, у нас також виникає така ситуація. Особливо канонічний приклад – це запити із пагінацією. Є звіт, у ньому є кілька сторінок, і йде запит limit 10. Потім те саме, але limit 10,10. Потім ще наступна сторінка. І питається, навіщо ми щоразу це все рахуємо? Але зараз рішення немає, і уникнути цього неможливо.

Є альтернативне рішення, яке ставиться сайдкаром поряд із ClickHouse. ClickHouse Proxy.

Кирило Шваков: У ClickHouse Proxy є вбудований рейт лімітер та вбудований кеш результатів. Там зроблено дуже багато налаштувань, тому що вирішувалося подібне завдання. Proxy дозволяє лімітувати запити, вибудовуючи їх у чергу, і налаштовувати, скільки часу живе кеш запитів. Якщо запити були дійсно однакові, Proxy віддасть їх багато разів, а сходить у ClickHouse лише один раз.

У Nginx теж є кеш у безкоштовній версії, і це теж працюватиме. Nginx навіть має налаштування, що, якщо запити прийшли одночасно, він буде гальмувати інші, поки один не виконається. Але саме в ClickHouse Proxy налаштування зроблено набагато краще. Він робився саме під ClickHouse саме під ці запити, тому більше підходить. Та й ставиться просто.

Як бути з асинхронними операціями та матеріалізованими уявленнями?

Є така проблема, що операції з реплейсинг-движком асинхронні - спочатку записуються дані, потім відбувається їхнє схлопування. Якщо під табличкою живе матеріалізована табличка з якимись агрегатами, то дублі до неї запишуться. І якщо немає якоїсь складної логіки, дані будуть задубльовані. Що можна із цим зробити?

Є очевидне рішення – реалізувати тригер на певний клас матв'юх при асинхронній операції схлопування. Чи є якісь «срібні кулі», плани щодо реалізації подібних функціональностей?

Варто дати раду тому, як працює дедуплікація. Те, про що я зараз розповім, не стосується питання, але про всяк випадок варто про це згадати.

При вставці в таблицю, що реплікується, є дедуплікація цілком вставлених блоків. Якщо ви повторно вставили один і той же блок, що містить таку ж кількість тих самих рядків у тому самому порядку, то дані дедуплікуються. Ви отримаєте “Ok” у відповідь на insert, але фактично буде записана одна пачка даних, і вона не буде продубльована.

Це потрібно для певності. Якщо під час вставки ви отримали “Ok”, ваші дані вставлені. Якщо ви отримали помилку від ClickHouse, то вони не вставлені, і потрібно вставку повторити. Але якщо під час вставки розірвалося з'єднання, ви не знаєте, вставлені дані чи ні. Єдиний варіант – це повторити вставку знову. Якщо дані насправді були вставлені, і ви їх вставили повторно, є дедукція блоків. Вона потрібна, щоб уникнути дублікатів.

І важливо ще, як вона працює для матеріалізованих уявлень. Якщо дані були дедупліковані при вставці в основну таблицю, то матеріалізоване подання вони теж не підуть.

Тепер щодо питання. У вас складніша ситуація, тому що ви записуєте дублікати окремих рядків. Тобто не пачка цілком задубльована, а саме конкретні рядки, і вони у фоні схлопуються. Справді, дані будуть схлопываться в основній таблиці, а в матеріалізоване уявлення підуть не схлопнуті, і за мерджах нічого з матеріалізованими уявленнями не відбудеться. Тому що матеріалізована вистава — це не що інше, як тригер на insert. За інших операцій нічого додатково з ним не відбувається.

І я тут порадувати не можу. Потрібно лише шукати конкретне рішення для цього випадку. Наприклад, чи можна в матеріалізованому уявленні теж зробити його реплейсинг, і спосіб дедуплікації, можливо, так само працюватиме. Але, на жаль, не завжди. Якщо воно агрегує, то не вийде.

Кирило Шваков: У нас теж костилебудування свого часу ще те було. Була проблема, що є покази реклами, і є деякі дані, які ми можемо показувати в реалтаймі, – це просто покази. Вони рідко коли дублюються, але якщо таке відбувається, ми їх потім все одно зхлопнемо. І були речі, які дублювати не можна — кліки та вся ця історія. Але показувати також їх хотілося практично відразу.

Як було зроблено матеріалізовані уявлення? Були уявлення, куди пишеться напряму — йде запис у сирі дані, і пишеться у хмури. Там колись дані не дуже правильні, вони дублюються і так далі. І є друга частина таблиці, де вони так само виглядають, як і матеріалізовані уявлення, тобто за структурою вони абсолютно однакові. Коли ми перераховуємо дані, дораховуємо дані без дублікатів, пишемо в ті таблиці.

Ми ходили через API – у ClickHouse руками це працювати не буде. І API дивиться: коли я маю дату останнього додавання до таблиці, де гарантовано вже правильні дані, пораховані, і він робить запит до однієї таблиці та до іншої таблиці. З одного запит вибирає до певної кількості часу, і з іншого добирає те, що ще не пораховано. І це працює, але не засобами одного ClickHouse.

Якщо у вас API якесь є – для аналітиків, для користувачів – то, в принципі, це варіант. Ви завжди дораховуєте, завжди перераховуєте. Це можна робити раз на добу або ще в якийсь час. Ви самі собі вибираєте спектр, який вам не потрібний і не критичний.

У ClickHouse багато логів. Як я можу бачити все, що відбувається з сервером у моменті?

У ClickHouse дуже багато різних логів, і ця кількість підвищується. У нових версіях якісь із них навіть за замовчуванням включені, у старих версіях їх треба включати під час оновлення. Проте їх все більше і більше. Хотілося б бачити зрештою, що відбувається зараз у мене з сервером, можливо, на якомусь зведеному дашборді.

Чи немає у вас у команді ClickHouse, чи у командах ваших друзів, які підтримують якийсь функціонал готових дашбордів, які б ці логи відображали у вигляді вже готового продукту? Зрештою, просто так логи дивитися в ClickHouse - це чудово. Але дуже класно, якби це було вже у вигляді дашборду. Я б від цього кайфанув.

Дашборди є, щоправда, вони не стандартизовані. У нас в компанії десь 60 команд використовують ClickHouse, і найдивніше, що у багатьох з них дашборди, які вони самі собі зробили, і трохи інші. Деякі команди використовують внутрішню інсталяцію «Яндекс.Хмари». Там є деякі готові звіти, хоч і не всі необхідні. В інших своє.

У моїх колег з «Метрики» є свій дашборд у Grafana, а у мене є свій за їхнім кластером. Я там дивлюся штуки на кшталт кеш хіт для кешу засічок. І навіть ще складніше те, що ми використовуємо різні інструменти. Свій дашборд я створював на дуже старому інструменті, що називається Graphite-web. Він зовсім негарний. І я так досі ним і користуюся, хоча Grafana, напевно, була б зручнішою та красивішою.

Базова річ у дашбордах однакова. Це системні метрики за кластером: CPU, пам'ять, диск, мережа. Інші – кількість одночасних запитів, кількість одночасних мерджів, кількість запитів за секунду, максимальна кількість шматків для партицій таблиць MergeTree, лаг реплікації, розмір черги реплікації, кількість вставлених рядків за секунду, кількість вставлених блоків за секунду. Це все, що виходить не з ліг, а з метрик.

Володимир Колобаєв: Олексію, я хотів би трохи скоригувати. Є Grafana. Grafana має datasource, яким є ClickHouse. Тобто я можу з Grafana робити запити одразу в ClickHouse. У ClickHouse є таблиця з логами, вона у всіх однакова. Я хочу в результаті Grafana звертатися до цієї таблиці логів і бачити ті запити, які прикладає мій сервер. Було б чудово мати такий дашборд.

Я його навелесипедив сам. Але в мене виникає питання — якщо воно все стандартизоване, і Grafana використовується всіма поспіль, чому в «Яндексі» немає такого офіційного дашборду?

Кирило Шваков: Насправді datasource, який до ClickHouse, зараз підтримує Altinity. І я просто хочу дати вектор, куди копати та кого штовхати. Можна спитати з них, тому що «Яндекс» все-таки робить ClickHouse, а не історію навколо нього. Altinity - це основна компанія, яка зараз просуває ClickHouse. Вони його не покинуть, а підтримуватимуть. Тому що в принципі, щоб завантажити дашборд на сайт Grafana, потрібно лише зареєструватися та залити його — особливих проблем немає.

Олексій Міловидов: За останній рік у ClickHouse додалося багато можливостей щодо профілювання запитів. Є метрики для кожного запиту щодо використання ресурсів. А нещодавно доданий ще більш низькорівневий профайлер запитів, щоб дивитися, де запит проводить кожну мілісекунду. Але щоб скористатися цією функціональністю, мені доводиться відкрити консольний клієнт та набрати запит, який я постійно забуваю. Я його кудись зберіг і завжди забуваю, куди саме.

Я хотів би, щоб був інструмент, у якому просто написано — ось ваші важкі запити, згруповані за класами запитів. Натиснув на якийсь, і мені б сказали, що він важкий тому. Нині такого рішення немає. І справді досить дивно, що коли люди мене запитують: «Скажіть, є якісь готові дашборди для Grafana?», я кажу: «Зайдіть на сайт Grafana, там ком'юніті “Дашборди”, і там є дашборд від Дімка, є дашборд від Костяна. Що це таке, я не знаю, я сам не скористався».

Як впливати на Мерджі, щоб сервер не падав в OOM?

У мене є таблиця, в таблиці всього одна партиція, вона ReplacingMergeTree. Я в ній пишу дані протягом чотирьох років. Мені треба було зробити в неї альтер і видалити деякі дані.

Я це зробив, і в ході обробки цього запиту з'їли всю пам'ять на всіх серверах кластера, і всі сервери кластера дружно пішли в OOM. Потім вони всі разом встали, почали виконувати мердж цієї ж операції, цього блоку даних, і знову впали в OOM. Потім вони знову встали і впали. І ця штука не припинялася.

Потім виявилося, що це справді баг, який хлопці пофіксували. Це дуже здорово, дякую. Але осад залишився. І тепер, коли я думаю про те, що треба робити якийсь мердж у таблиці, у мене постає питання — а чому я не можу взяти і на ці мерджі якимось чином вплинути? Наприклад, обмежити їх за кількістю необхідної оперативної пам'яті, або в принципі за їх кількістю, яка оброблятиме цю таблицю.

У мене є таблиця, яка називається «Метрики», обробляй мені її, будь ласка, у два потоки. Не треба плодити десять чи п'ять мерджів паралельно, зробиш у два. Я думаю, що о другій у мене пам'яті вистачить, а на те, щоб десять обробляти, може не вистачити. Чому страх залишається? Тому що таблиця росте, і колись я зіткнуся з ситуацією, що в принципі вже не через баг, а через те, що дані будуть змінюватися в такій великій кількості, що мені просто пам'яті на сервері не вистачить. І тоді сервер падатиме в OOM при мерджі. Причому мутацію я можу скасувати, а Мердж вже немає.

Ви знаєте, при мерджах сервер не падатиме в OOM, тому що при мерджі використовується кількість оперативної пам'яті тільки на один маленький діапазон даних. Отже, все буде добре незалежно від обсягу даних.

Володимир Колобаєв: Добре. Тут момент такий, що після того, як зробили баг фікс, я скачав собі нову версію, і на іншій таблиці, більш маленькій, де багато партицій, зробив подібну операцію. І в ході Мердж на сервері було спалено близько 100 Гб оперативної пам'яті. У мене було 150 зайнято, 100 з'їло і залишилося вікно на 50 Гб, тому я в OOM не впав.

Що мене зараз захищає від того, щоб я не падав в OOM, якщо він дійсно споживає по 100 Гб оперативної пам'яті? Як бути у ситуації, якщо раптом оперативна пам'ять на мерджах закінчиться?

Олексій Міловидов: Є така проблема, що витрата оперативної пам'яті саме на Мерджі не обмежується. І друга проблема полягає в тому, що якщо якийсь мердж був призначений, то його необхідно виконати, тому що він записаний у лог реплікації. Лог реплікації - це дії, які потрібні, щоб привести репліку в консистентний стан. Якщо не зробити ручні маніпуляції, які цей лог реплікації відкотить назад, Мердж доведеться, так чи інакше, виконати.

Звичайно, було б не зайвим мати обмеження щодо оперативної оперативності, яке «про всяк випадок» захищає саме від OOM. Воно не допоможе мерджу виконатися, він починатиметься знову, доходитиме до якогось порога, кидатиме виняток, а потім знову починатиметься — нічого хорошого з цього не вийде. Але в принципі обмеження це запровадити було б корисно.

Як відбуватиметься розробка Golang-драйвера для ClickHouse?

Golang-драйвер, який написав Кирило Шваков, зараз офіційно начебто підтримується командою ClickHouse. Він у репозиторії ClickHouse, він тепер великий та справжній.

Невелика ремарка. Є чудове та всіма улюблене сховище нормальних форм нескінченного порядку – це Vertica. Вони також мають свій офіційний python-драйвер, який підтримується розробниками Vertica. І кілька разів було таке, що версії сховища та версії драйвера дуже круто роз'їжджалися, і драйвер у якийсь момент переставав працювати. І другий момент. Підтримка цього офіційного драйвера, здається мені, ведеться системі «Ніпель» - ти пишеш їм issue, і воно висить вічно.

У мене два запитання. Зараз Golang-драйвер Кирила - це майже дефолтний спосіб спілкуватися з Golang з ClickHouse. Хіба хтось через http інтерфейс комунікує досі, бо йому так подобається. Як відбуватиметься розробка цього драйвера? Чи синхронізуватиметься вона з якимись breaking changes у самому сховищі? І який порядок розгляду є?

Кирило Шваков: Перше — як усе влаштовано бюрократично. Цей момент не обговорювався, тому відповісти мені нема чого.

Щоб відповісти на питання про це, потрібна невелика історія драйвера. Я працював у компанії, в якій було багато даних. Це була рекламна крутила з величезною кількістю подій, які потрібно було десь зберігати. І в якийсь момент з'явився ClickHouse. Ми налили туди дані, і спочатку все було добре, а потім ClickHouse впав. На той момент ми вирішили, що воно нам не потрібне.

Через рік ми повернулися до ідеї використання ClickHouse і нам потрібно було якось туди писати дані. Вступна була така - залізо дуже слабке, ресурсів мало. Але ми завжди так працювали, і тому подивилися на нативний протокол.

Оскільки ми працювали на Go, було зрозуміло, що потрібний драйвер на Go. Я робив його практично фултайм – це було моє робоче завдання. До якогось моменту ми його довели, і в принципі ніхто не припускав, що хтось, крім нас, його використовуватиме. Потім прийшов CloudFlare рівно з такою ж проблемою, і в якийсь час ми з ними працювали дуже рівно, тому що у них були ті самі завдання. Причому ми це робили і в ClickHouse сам, і драйвер.

Раніше я просто перестав їм займатися, тому що моя активність з точки зору ClickHouse і з роботою трохи змінилася. Тому це не закриваються. Періодично до репозиторію комітять люди, яким самі щось потрібно. Тоді я дивлюся pull request і іноді навіть щось сам правлю, але це трапляється рідко.

До драйвера хочеться повернутися. Кілька років тому, коли вся ця справа починалася, ClickHouse був також інший і з іншими можливостями. Зараз є розуміння, як переробити драйвер, щоб було добре. Якщо це станеться, то версія 2 буде в будь-якому випадку несумісна через накопичені милиці.

Як цю справу організувати, я не знаю. В мене самого часу не так багато. Якщо якісь люди допилюватимуть драйвер, я зможу їм допомагати і розповідати, що робити. Але саме активна участь «Яндекса» у розвитку проекту поки що ніяк не обговорювалася.

Олексій Міловидов: Насправді поки що немає жодної бюрократії щодо цих драйверів. Єдине — вони винесені до офіційної організації, тобто цей драйвер визнано офіційним рішенням за умовчанням для Go. Є якісь інші драйвери, але вони йдуть окремо.

У нас усередині жодної розробки для цих драйверів немає. Питання — чи зможемо ми найняти окрему людину, не конкретно цей драйвер, але в розвиток всіх комьюнити-драйверов, чи зможемо знайти когось зовні.

Зовнішній словник не піднімається після перезавантаження з увімкненим налаштуванням lazy_load. Що робити?

У нас включено налаштування lazy_load, і після перезавантаження сервера словник сам не піднімається. Він піднімається лише після того, як користувач звернеться до цього словника. І за першого звернення видає помилку. Чи можна якось автоматично завантажувати словники засобами ClickHouse, чи потрібно самим завжди контролювати їх готовність, щоб користувачі не отримували помилок?

Можливо, у нас стара версія ClickHouse, тому словник автоматично не завантажувався. Можливо таке?

По-перше, словники можна форсовано завантажити за допомогою запиту system reload dictionaries. По-друге, з приводу помилки — якщо словник уже завантажений, то запити працюватимуть за даними, які були завантажені. Якщо словник ще не був завантажений, він завантажуватиметься прямо під час запиту.

Для важких словників це дуже зручно. Наприклад, потрібно притягнути мільйон рядків із MySQL. Хтось робить простий select, але цей select буде чекати на цей мільйон рядків. Тут є два рішення. Перше – вимкнути lazy_load. Друге — коли сервер піднімається, перед тим, як увімкнути на нього навантаження, зробити system reload dictionary або просто виконати запит, який використовує словник. Тоді словник буде завантажено. Потрібно самим контролювати доступність словників із включеною настройкою lazy_load, тому що автоматично ClickHouse їх не підтягує.

На останнє запитання відповідь — чи версія стара, чи треба налагоджувати.

Як бути з тим, що system reload dictionaries не підвантажує жоден з множини словників, якщо хоча б один з них падає з помилкою?

Є ще питання щодо system reload dictionaries. У нас два словники – один не провантажується, другий провантажується. System reload dictionaries у разі не підвантажує жоден словник, і доводиться точково підвантажувати конкретний за його назвою з допомогою system reload dictionary. Це також пов'язано із версією ClickHouse?

Хочу порадувати. Ця поведінка змінювалася. Отже, якщо ви оновите ClickHouse, воно теж зміниться. Якщо вас не влаштовує поточна поведінка system reload dictionaries, оновіться, і сподіватимемося, що воно зміниться на краще.

Чи є спосіб конфігурувати реквізити в конфізі ClickHouse, але не світити їх при помилках?

Наступне питання про помилки, пов'язані зі словником, зокрема реквізити. Ми прописали реквізити підключення в конфізі ClickHouse до словника, і при помилці ми ці реквізити та пароль отримуємо у відповідь.

Ми вирішили цю помилку винесенням реквізитів у конфіг драйвера ODBC. Чи є якийсь спосіб налаштувати реквізити в конфізі ClickHouse, але не світити ці реквізити при помилках?

Тут рішення дійсно - вказувати ці credentials в odbc.ini, а в самому ClickHouse вказувати лише ODBC Data Source Name. Для решти джерел словників такого не буде — ні для словника з MySQL, ні для решти ви не повинні бачити пароль під час повідомлення про помилку. Для ODBC я теж подивлюся — якщо таке є, треба просто прибрати.

Бонус: фони для Зуму з посиденьок

По кліку на картинку для найзавзятіших читачів відкриються бонусні фони з посиденьок. Гасимо пожежу разом з маскотами технологій Авіто, радимося з колегами з кімнати сісадміна або комп'ютерного клубу олдскульного і проводимо дейлі під мостом на тлі графіті.

ClickHouse для просунутих користувачів у питаннях та відповідях

Джерело: habr.com

Додати коментар або відгук