ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках

ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках

Вітаю, habr.

Калі нехта эксплуатуе сістэму graphite-web і сутыкнуўся з праблемай прадукцыйнасці сховішча шаптаць (IO, спажыванае дыскавая прастора), то шанец таго, што быў кінуты погляд на ClickHouse у якасці замены, павінен імкнуцца да адзінкі. Дадзенае сцвярджэнне мае на ўвазе тое, што ў якасці прымаючага метрыкі дэмана ўжо выкарыстоўваецца іншая рэалізацыя, напрыклад carbonwriter або go-carbon.

ClickHouse добра вырашае апісаныя праблемы. Да прыкладу, пасля пераліўкі 2TiB дадзеных з whisper, яны змясціліся ў 300GiB. Падрабязна на параўнанні я спыняцца не буду, артыкулаў на гэтую тэму хапае. Да таго ж, да нядаўняга часу з нашым ClickHouse сховішчам было не ўсё ідэальна.

Праблемы з спажываным месцам

На першы погляд усё павінна працаваць добра. Прытрымліваючыся дакументацыі, ствараем канфіг для схемы захоўвання метрык (далей retention), затым ствараем табліцу паводле рэкамендацыі абранага бэкенда для graphite-web: carbon-clickhouse+graphite-clickhouse або graphouse, у залежнасці ад таго, які стэк выкарыстоўваецца. І… уключаецца бомба запаволенага дзеяння.

Для таго, каб зразумець, якая, трэба ведаць, як працуюць устаўкі і далейшы жыццёвы шлях дадзеных у табліцах рухавікоў сямейства *MergeTree ClickHouse (дыяграмы ўзяты з прэзентацыі Аляксея Зацялепіна):

  • Устаўляецца блок дадзеных. У нашым выпадку гэта прыляцелі метрыкі.
    ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках
  • Кожны такі блок перад запісам на дыск сартуецца паводле ключа. ORDER BY, паказанаму пры стварэнні табліцы.
  • Пасля сартавання, кусок (part) дадзеных запісваецца на дыск.
    ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках
  • Сервер сочыць у фоне, каб такіх кавалкаў было не шмат, і запускае фонавыя. слияния (merge, далей мержы).
    ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках
    ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках
  • Сервер перастае запускаць мержы самастойна, як толькі дадзеныя перастаюць актыўна паступаць у партицию (partition), але можна запусціць працэс уручную камандай OPTIMIZE.
  • Калі ў партіціі застаўся толькі адзін кавалак, то запусціць мерж звычайнай камандай не атрымаецца, неабходна выкарыстоўваць OPTIMIZE ... FINAL

Такім чынам, паступаюць першыя метрыкі. І яны займаюць нейкую прастору. Наступныя падзеі могуць некалькі вар'іравацца ў залежнасці ад шматлікіх фактараў:

  • Ключ партыцыянавання можа быць як вельмі маленькім (дзень), так і вельмі вялікім (некалькі месяцаў).
  • Канфіг retention можа змяшчаць некалькі значных парогаў агрэгацыі дадзеных усярэдзіне актыўнай партіціі (куды ідзе запіс метрык), а можа і не.
  • Калі дадзеных вельмі шмат, то самыя раннія кавалкі, якія з-за фонавых мержаў могуць ужо быць вялізнымі (пры выбары неаптымальнага ключа партыцыянавання), не будуць мержаться самі са свежымі маленькімі кавалкамі.

І заканчваецца заўсёды ўсё аднолькава. Месца, займанае метрыкамі ў ClickHouse толькі расце, калі:

  • не прымяняць OPTIMIZE ... FINAL уручную або
  • не ўстаўляць дадзеныя ва ўсе партіціі на пастаяннай аснове, каб рана ці позна запусціць фонавы мерж

Другі спосаб здаецца найболей простым у рэалізацыі і, значыць, ён няправільны і быў апрабаваны ў першую чаргу.
Я напісаў дастаткова просты скрыпт на python, які адпраўляў фіктыўныя метрыкі для кожнага дня за мінулыя 4 гады і запускаўся кожную гадзіну кронам.
Бо ўся праца ClickHouse DBMS пабудавана на тым, што гэтая сістэма рана ці позна зробіць усю фонавую працу, але невядома калі, то дачакацца моманту, калі старыя велізарныя кавалкі зробяць ласку пачаць мерж з новымі маленькімі, мне не атрымалася. Стала зразумела, што трэба шукаць спосаб аўтаматызаваць прымусовыя аптымізацыі.

ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках

Інфармацыя ў сістэмных табліцах ClickHouse

Зірнем на структуру табліцы system.parts. Гэта вычарпальная інфармацыя аб кожным кавалку ўсіх табліц на серверы ClickHouse. Змяшчае, у тым ліку, наступныя слупкі:

  • імя БД (database);
  • імя табліцы (table);
  • імя і ВД партыцыі (partition & partition_id);
  • калі кавалак быў створаны (modification_time);
  • мінімальная і максімальная дата ў кавалку (партыцыянаванне ідзе па днях) (min_date & max_date);

Таксама ёсць табліца system.graphite_retentions, з наступнымі цікавымі палямі:

  • імя БД (Tables.database);
  • імя табліцы (Tables.table);
  • узрост метрыкі, калі павінна быць прыменена наступная агрэгацыя (age);

Такім чынам:

  1. У нас ёсць табліца кавалкаў і табліца правілаў агрэгацыі.
  2. Аб'ядноўваем іх скрыжаванне і атрымліваем усе табліцы GraphiteMergeTree.
  3. Шукаем усе партыцыі, у якіх:
    • больш за адзін кавалка
    • ці надышоў момант прымяніць наступнае правіла агрэгацыі, і modification_time старэйшыя за гэты момант.

Рэалізацыя

Дадзены запыт

SELECT
    concat(p.database, '.', p.table) AS table,
    p.partition_id AS partition_id,
    p.partition AS partition,
    -- Самое "старое" правило, которое может быть применено для
    -- партиции, но не в будущем, см (*)
    max(g.age) AS age,
    -- Количество кусков в партиции
    countDistinct(p.name) AS parts,
    -- За самую старшую метрику в партиции принимается 00:00:00 следующего дня
    toDateTime(max(p.max_date + 1)) AS max_time,
    -- Когда партиция должна быть оптимизированна
    max_time + age AS rollup_time,
    -- Когда самый старый кусок в партиции был обновлён
    min(p.modification_time) AS modified_at
FROM system.parts AS p
INNER JOIN
(
    -- Все правила для всех таблиц *GraphiteMergeTree
    SELECT
        Tables.database AS database,
        Tables.table AS table,
        age
    FROM system.graphite_retentions
    ARRAY JOIN Tables
    GROUP BY
        database,
        table,
        age
) AS g ON
    (p.table = g.table)
    AND (p.database = g.database)
WHERE
    -- Только активные куски
    p.active
    -- (*) И только строки, где правила аггрегации уже должны быть применены
    AND ((toDateTime(p.max_date + 1) + g.age) < now())
GROUP BY
    table,
    partition
HAVING
    -- Только партиции, которые младше момента оптимизации
    (modified_at < rollup_time)
    -- Или с несколькими кусками
    OR (parts > 1)
ORDER BY
    table ASC,
    partition ASC,
    age ASC

вяртае кожную з партыцый табліц * GraphiteMergeTree, мерж якіх павінен прывесці да вызвалення дыскавай прасторы. Засталася толькі справа за малым: прайсціся па іх усім з запытам OPTIMIZE ... FINAL. У фінальнай рэалізацыі таксама ўлічаны той момант, што партыцыі з актыўным запісам чапаць няма неабходнасці.

Менавіта гэта і робіць праект graphite-ch-optimizer. Былыя калегі з Яндекс.Маркет апрабавалі яго ў продзе, вынік працы можна бачыць ніжэй.

ClickHouse + Graphite: як значна паменшыць спажыванае месца на дысках

Калі запусціць праграму на серверы з ClickHouse, то яна проста пачне працаваць у рэжыме дэмана. Раз у гадзіну будзе выконвацца запыт, правяраючы, ці не з'явіліся новыя партыцыі старэйшыя за тры суткі, якія можна аптымізаваць.

У бліжэйшых планах – падаць, прынамсі, deb пакеты, а па магчымасці – яшчэ і rpm.

замест заключэння

За мінулыя 9 з лішнім месяцаў я ўсярэдзіне сваёй кампаніі innogames правёў шмат часу, варыўшыся на стыку ClickHouse і graphite-web. Гэта быў добры досвед, вынікам якога стаў магчымы хуткі пераход з whisper на ClickHouse у якасці сховішчы метрык. Спадзяюся, што гэты артыкул - нешта накшталт пачатку цыклу аб тым, якія паляпшэнні былі ўнесены намі ў розныя часткі гэтага стэка, і што будзе зроблена ў будучыні.

На распрацоўку запыту было патрачана некалькі літраў піва і адміна-дзён сумесна з v0devil, за што я хачу выказаць яму сваю падзяку. А таксама за рэцэнзаванне гэтага артыкула.

Старонка праекту на github

Крыніца: habr.com

Дадаць каментар