
Witaj, habr.
Jeśli ktoś wykorzystuje system i napotyka problemy z wydajnością magazynu (IO, zajmowane miejsce na dysku), to szansa, że rozważano ClickHouse jako alternatywę, powinna zmierzać do jedności. To stwierdzenie implikuje, że jako demon przyjmujący metryki już używana jest zewnętrzna implementacja, na przykład lub .
ClickHouse dobrze radzi sobie z opisanymi problemami. Na przykład, po przelaniu 2TiB danych z whisper, zmieściły się one w 300GiB. Nie będę się zatrzymywał na szczegółowej analizie porównawczej, materiałów na ten temat jest wystarczająco dużo. Co więcej, do niedawna nasze magazynowanie ClickHouse nie było idealne.
Problemy z zajmowanym miejscem
Na pierwszy rzut oka, wszystko powinno działać dobrze. Podążając , tworzymy konfigurację dla schemy przechowywania metryk (dalej retention), następnie tworzymy tabelę zgodnie z zaleceniami wybranego backendu dla graphite-web: + lub , w zależności od używanego stosu. I... włącza się bomba zegarowa.
Aby zrozumieć, jaka to bomba, trzeba znać, jak działają wstawienia i dalsza życiowa droga danych w tabelach silników z rodziny *MergeTree ClickHouse (diagramy pochodzą z Aleksieja Zatelepińa):
- Wstawiane są
blokidanych. W naszym przypadku to przyleciały metryki.

- Każdy taki blok przed zapisem na dysku jest sortowany zgodnie z kluczem
ORDER BY, podanym podczas tworzenia tabeli. - Po sortowaniu,
kawałek(part) danych jest zapisywany na dysku.

- Serwer w tle monitoruje, aby takich kawałków nie było za dużo, i uruchamia procesy
łączenia(merge, następnie mergi.


- Serwer przestaje samodzielnie uruchamiać mergi, gdy dane przestają aktywnie napływać do
partycji(partition), ale można uruchomić proces ręcznie komendąOPTIMIZE. - Jeśli w partycji pozostał tylko jeden kawałek, to nie będzie możliwe uruchomienie mergi zwykłą komendą, należy użyć
OPTIMIZE ... FINAL
Tak więc, przychodzą pierwsze metryki. I zajmują pewne miejsce. Kolejne zdarzenia mogą się nieco różnić w zależności od wielu czynników:
- Klucz partycjonowania może być zarówno bardzo mały (dzień), jak i bardzo duży (kilka miesięcy).
- Konfiguracja retention może obejmować kilka znaczących progów agregacji danych w aktywnej partycji (gdzie zapisywane są metryki), ale nie musi.
- Jeśli danych jest bardzo dużo, to najwcześniejsze kawałki, które ze względu na tło merge mogą być już ogromne (przy wyborze nieoptymalnego klucza partycjonowania), nie będą się same łączyć z nowymi małymi kawałkami.
I zawsze kończy się to tak samo. Miejsce zajmowane przez metryki w ClickHouse tylko rośnie, jeśli:
- nie stosuję
OPTIMIZE ... FINALręcznie lub - nie wstawiam danych do wszystkich partycji na stałe, aby prędzej czy później uruchomić tło merge
Drugi sposób wydaje się najprostszy do wdrożenia i dlatego jest błędny i był testowany w pierwszej kolejności.
Napisałem wystarczająco prosty skrypt w pythonie, który wysyłał fikcyjne metryki dla każdego dnia przez ostatnie 4 lata i był uruchamiany co godzinę przez cron.
Ponieważ cała praca ClickHouse DBMS oparta jest na tym, że ten system prędzej czy później wykona całą pracę w tle, ale nie wiadomo kiedy, nie udało mi się doczekać momentu, kiedy stare ogromne kawałki będą mogły zacząć łączyć się z nowymi małymi. Zrozumiałem, że muszę znaleźć sposób na zautomatyzowanie wymuszonej optymalizacji.

Informacje w tabelach systemowych ClickHouse
Przyjrzyjmy się strukturze tabeli . To wyczerpujące informacje o każdym kawałku wszystkich tabel na serwerze ClickHouse. Zawiera między innymi następujące kolumny:
- nazwa bazy danych (
database); - nazwa tabeli (
stół); - nazwa i ID partycji (
partition&partition_id); - kiedy kawałek został stworzony (
modification_time); - minimalna i maksymalna data w kawałku (partycjonowanie odbywa się według dni) (
min_date&max_date);
Jest też tabela , z następującymi interesującymi polami:
- nazwa bazy danych (
Tables.database); - nazwa tabeli (
Tables.table); - wiek metryki, kiedy ma być zastosowana następna agregacja (
age);
A zatem:
- Mamy tabelę kawałków i tabelę reguł agregacji.
- Łączymy ich przekrój i otrzymujemy wszystkie tabele *GraphiteMergeTree.
- Szukamy wszystkich partycji, w których:
- więcej niż jeden kawałek
- lub nadszedł moment, aby zastosować następną regułę agregacji, i
modification_timejest starsze od tego momentu.
Realizacja
To zapytanie
WYBIERZ
concat(p.database, '.', p.table) AS table,
p.partition_id AS partition_id,
p.partition AS partition,
-- Najstarsza "zasada", która może być stosowana do
-- partycji, ale nie w przyszłości, zobacz (*)
max(g.age) AS age,
-- Liczba części w partycji
countDistinct(p.name) AS parts,
-- Najstarsza metryka w partycji uznawana jest za 00:00:00 następnego dnia
toDateTime(max(p.max_date + 1)) AS max_time,
-- Kiedy partycja powinna być zoptymalizowana
max_time + age AS rollup_time,
-- Kiedy najstarszy fragment w partycji był aktualizowany
min(p.modification_time) AS modified_at
Z system.parts AS p
INNER JOIN
(
-- Wszystkie zasady dla wszystkich tabel *GraphiteMergeTree
WYBIERZ
Tables.database AS database,
Tables.table AS table,
age
Z system.graphite_retentions
ARRAY JOIN Tables
GRUPUJ PO
database,
table,
age
) AS g ON
(p.table = g.table)
I (p.database = g.database)
GDZIE
-- Tylko aktywne fragmenty
p.active
-- (*) I tylko wiersze, gdzie zasady agregacji już powinny być stosowane
I ((toDateTime(p.max_date + 1) + g.age) < now())
GRUPUJ PO
table,
partition
HAVING
-- Tylko partycje, które są młodsze od momentu optymalizacji
(modified_at 1)
ZAMÓW
table ASC,
partition ASC,
age ASCzwraca każdą z partycji tabel *GraphiteMergeTree, których połączenie powinno prowadzić do uwolnienia miejsca na dysku. Pozostaje jedynie kwestia: przejść przez nie wszystkie za pomocą zapytania OPTIMIZE ... FINAL. W finalnej wersji uwzględniono również to, że partycje z aktywnym zapisem nie muszą być ruszane.
Właśnie to czyni projekt . Byli koledzy z Yandex.Market przetestowali go w produkcji, efekty pracy można zobaczyć poniżej.

Jeśli uruchomisz program na serwerze z ClickHouse, po prostu zacznie działać w trybie demona. Co godzinę będzie wykonywane zapytanie, sprawdzając, czy nie pojawiły się nowe partycje starsze niż trzy dni, które można zoptymalizować.
W najbliższych planach — dostarczenie przynajmniej pakietów deb, a jeśli to możliwe — także rpm.
Zamiast zakończenia
W ciągu ostatnich 9 miesięcy spędziłem dużo czasu w mojej firmie mieszając ClickHouse z graphite-web. To było dobre doświadczenie, którego wynikiem może być szybkie przejście z whisper na ClickHouse jako magazyn metryk. Mam nadzieję, że ten artykuł będzie czymś w rodzaju początku cyklu o tym, jakie ulepszenia wnieśliśmy w różne części tego stosu i co będzie zrobione w przyszłości.
Na opracowanie zapytania poświęcono kilka litrów piwa i dni administracyjne wspólnie z , za co pragnę wyrazić mu swoją wdzięczność. A także za recenzję tego artykułu.
Źródło: habr.com




