Zdravím habr.
Pokud někdo provozuje systém
ClickHouse popsané problémy dobře řeší. Například po nasypání 2TiB dat z whisper se vejdou do 300GiB. Nebudu se srovnáním podrobně zdržovat, článků na toto téma je dost. Navíc donedávna nebylo s naším úložištěm ClickHouse vše dokonalé.
Problémy se spotřebovaným prostorem
Na první pohled by mělo vše dobře fungovat. Následující retention
), poté vytvořte tabulku podle doporučení vybraného backendu grafitu-webu:
Abyste pochopili, který z nich, musíte vědět, jak fungují vložky a další životní cestu dat v tabulkách rodinných motorů *MergeTree ClickHouse (schémata převzata z
- vložena
блок
data. V našem případě se jedná o metriky.
- Každý takový blok je před zápisem na disk roztříděn podle klíče.
ORDER BY
Zadané při vytvoření tabulky. - Po třídění,
кусок
(part
) se data zapisují na disk.
- Server sleduje na pozadí, aby takových kusů nebylo mnoho a spouští pozadí
слияния
(merge
, pak sloučit).
- Server přestane sám spouštět sloučení, jakmile data přestanou aktivně proudit
партицию
(partition
), ale proces můžete spustit ručně pomocí příkazuOPTIMIZE
. - Pokud v oddílu zbývá pouze jeden kus, pak nebudete moci spustit sloučení obvyklým příkazem, musíte použít
OPTIMIZE ... FINAL
Přicházejí tedy první metriky. A zabírají trochu místa. Následné události se mohou poněkud lišit v závislosti na mnoha faktorech:
- Klíč oddílu může být buď velmi malý (den) nebo velmi velký (několik měsíců).
- Konfigurace uchovávání může splňovat několik významných prahů agregace dat uvnitř aktivního oddílu (kde jsou zapsány metriky), nebo možná ne.
- Pokud existuje mnoho dat, pak se první části, které již mohou být obrovské kvůli slučování na pozadí (při výběru neoptimálního rozdělovacího klíče), samy nesloučí s čerstvými malými částmi.
A vždy to skončí stejně. Místo obsazené metrikami v ClickHouse roste pouze, pokud:
- neplatí
OPTIMIZE ... FINAL
ručně popř - nevkládejte data do všech oddílů průběžně, abyste dříve nebo později zahájili slučování na pozadí
Druhá metoda se zdá být nejsnáze implementovatelná, a proto je špatná a byla testována na prvním místě.
Napsal jsem poměrně jednoduchý python skript, který posílá fiktivní metriky pro každý den za poslední 4 roky a spouští každou hodinu s cronem.
Vzhledem k tomu, že veškerá práce ClickHouse DBMS je založena na tom, že tento systém dříve nebo později udělá veškerou práci na pozadí, ale není známo kdy, nestačil jsem se dočkat okamžiku, kdy se staré obrovské kusy rozhodnou začít slučovat s novými malými. Bylo jasné, že musíme najít způsob, jak automatizovat nucené optimalizace.
Informace v tabulkách systému ClickHouse
Pojďme se podívat na strukturu tabulky
- jméno databáze (
database
); - název tabulky (
table
); - název oddílu a ID (
partition
&partition_id
); - kdy dílo vzniklo (
modification_time
); - minimální a maximální datum v bloku (rozdělení je podle dne) (
min_date
&max_date
);
K dispozici je také stůl
- jméno databáze (
Tables.database
); - název tabulky (
Tables.table
); - stáří metriky, kdy má být použita další agregace (
age
);
Takže:
- Máme tabulku kousků a tabulku agregačních pravidel.
- Spojíme jejich průnik a získáme všechny tabulky *GraphiteMergeTree.
- Hledáme všechny oddíly, ve kterých:
- více než jeden kus
- nebo nastal čas použít další agregační pravidlo a
modification_time
starší než tento okamžik.
uskutečnění
Tento požadavek
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
vrátí každý z oddílů tabulky *GraphiteMergeTree, které by měly být sloučeny, aby se uvolnilo místo na disku. Zůstává pouze u malých věcí: projděte je všechny s žádostí OPTIMIZE ... FINAL
. Finální realizace počítala i s tím, že není potřeba sahat na přepážky s aktivním záznamem.
To je to, co projekt dělá.
Pokud program spustíte na serveru s ClickHouse, začne jednoduše pracovat v režimu démona. Jednou za hodinu se provede dotaz, který zkontroluje, zda existují nové oddíly starší než tři dny, které lze optimalizovat.
V blízké budoucnosti - poskytovat alespoň deb balíčky, a pokud možno - i rpm.
Místo závěru
Posledních 9+ měsíců jsem byl uvnitř své společnosti
Vývojem požadavku bylo vynaloženo několik litrů piva a admin dní
Zdroj: www.habr.com