
Salve, habr.
Se qualcuno utilizza il sistema e ha riscontrato problemi di performance nel database (IO, spazio disco consumato), la probabilità che si sia considerato ClickHouse come alternativa dovrebbe tendere a uno. Questa affermazione implica che venga già utilizzata un'implementazione di terze parti come demone per la raccolta delle metriche, ad esempio o .
ClickHouse affronta bene i problemi descritti. Ad esempio, dopo il caricamento di 2TiB di dati da whisper, questi sono stati ridotti a 300GiB. Non mi soffermerò sui dettagli del confronto, ci sono già molte articoli su questo tema. Inoltre, fino a poco tempo fa, il nostro archivio ClickHouse non era perfetto.
Problemi di spazio utilizzato
A prima vista, tutto dovrebbe funzionare bene. Seguendo , creiamo una configurazione per lo schema di archiviazione delle metriche (di seguito retention), poi creiamo una tabella secondo le raccomandazioni del backend scelto per graphite-web: + o , a seconda dello stack utilizzato. E… si attiva una bomba a orologeria.
Per comprendere quale sia, è necessario sapere come funzionano le inserzioni e il successivo ciclo di vita dei dati nelle tabelle dei motori della famiglia *MergeTree ClickHouse (i diagrammi sono tratti da di Alexey Zatelepin):
- Viene inserito
un bloccodi dati. Nel nostro caso si tratta delle metriche pervenute.

- Ogni blocco di questo tipo viene ordinato secondo la chiave
ORDER BY, specificata durante la creazione della tabella. - Dopo l'ordinamento,
un pezzo(part) di dati viene scritto su disco.

- Il server monitora in background affinché tali pezzi non siano numerosi e avvia processi di
fusione(merge, cioè le fusioni).


- Il server smette di avviare fusioni autonomamente non appena i dati smettono di arrivare attivamente nella
partizione(partition), ma è possibile avviare il processo manualmente con il comandoOPTIMIZE. - Se nella partizione rimane solo un pezzo, non sarà possibile avviare la fusione con il comando normale, è necessario utilizzare
OPTIMIZE ... FINAL
Quindi, arrivano le prime metriche. E occupano uno spazio. Gli eventi successivi possono variare leggermente a seconda di molti fattori:
- La chiave di partizionamento può essere molto piccola (un giorno) o molto grande (alcuni mesi).
- La configurazione di retention può contenere più soglie significative per l'aggregazione dei dati all'interno della partizione attiva (dove vengono registrate le metriche), oppure no.
- Se ci sono troppi dati, i pezzi più vecchi, che a causa delle fusioni in background possono già essere enormi (in caso si scelga una chiave di partizionamento subottimale), non verranno fusi con i nuovi pezzi piccoli.
E tutto finisce sempre allo stesso modo. Lo spazio occupato dalle metriche in ClickHouse cresce solo se:
- non si applica
OPTIMIZE ... FINALmanualmente o - non si inseriscono dati in tutte le partizioni in modo permanente, in modo da avviare prima o poi una fusione in background.
Il secondo metodo sembra il più semplice da implementare e, quindi, è sbagliato e viene testato per primo.
Ho scritto uno script piuttosto semplice in python, che inviava metriche fittizie per ogni giorno degli ultimi 4 anni e veniva eseguito ogni ora tramite cron.
Poiché il lavoro dell'intero sistema ClickHouse DBMS si basa sul fatto che questa piattaforma affronterà ogni lavoro di routine, ma non si sa quando, non sono riuscito ad attendere il momento in cui i vecchi grandi pezzi inizieranno a unirsi con i nuovi più piccoli. È diventato chiaro che era necessario cercare un modo per automatizzare le ottimizzazioni forzate.

Informazioni nelle tabelle di sistema di ClickHouse
Esaminiamo la struttura della tabella . Queste forniscono informazioni complete su ciascun pezzo di tutte le tabelle sul server ClickHouse. Include anche le seguenti colonne:
- nome del database (
database); - nome della tabella (
table); - nome e ID della partizione (
partition&partition_id); - quando il pezzo è stato creato (
modification_time); - data minima e massima nel pezzo (la partizione avviene per giorno) (
min_date&max_date);
C'è anche una tabella , con i seguenti campi interessanti:
- nome del database (
Tables.database); - nome della tabella (
Tables.table); - età della metrica, quando deve essere applicata la successiva aggregazione (
age);
Quindi:
- Abbiamo una tabella dei pezzi e una tabella delle regole di aggregazione.
- Unendo le loro intersezioni, otteniamo tutte le tabelle *GraphiteMergeTree.
- Cerchiamo tutte le partizioni in cui:
- c'è più di un pezzo
- oppure è giunto il momento di applicare la seguente regola di aggregazione, e
modification_timeè più vecchio di quel momento.
Implementazione
Questa query
SELECT
concat(p.database, '.', p.table) AS table,
p.partition_id AS partition_id,
p.partition AS partition,
-- La "regola" più "antica" che può essere applicata per
-- la partizione, ma non in futuro, vedi (*)
max(g.age) AS age,
-- Numero di pezzi nella partizione
countDistinct(p.name) AS parts,
-- La metrica più vecchia della partizione è considerata 00:00:00 del giorno successivo
toDateTime(max(p.max_date + 1)) AS max_time,
-- Quando la partizione deve essere ottimizzata
max_time + age AS rollup_time,
-- Quando il pezzo più vecchio nella partizione è stato aggiornato
min(p.modification_time) AS modified_at
FROM system.parts AS p
INNER JOIN
(
-- Tutte le regole per tutte le tabelle *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
-- Solo pezzi attivi
p.active
-- (*) E solo righe in cui le regole di aggregazione devono già essere applicate
AND ((toDateTime(p.max_date + 1) + g.age) < now())
GROUP BY
table,
partition
HAVING
-- Solo partizioni più giovani del momento di ottimizzazione
(modified_at 1)
ORDER BY
table ASC,
partition ASC,
age ASCrestituisce ciascuna delle partizioni delle tabelle *GraphiteMergeTree, la cui fusione deve portare a un rilascio di spazio su disco. Resta solo da fare una cosa: eseguire una query su tutte queste partizioni. OPTIMIZE ... FINAL. Nella versione finale è stato anche considerato che non è necessario toccare le partizioni con scritture attive.
È proprio questo che fa il progetto . I miei ex colleghi di Yandex.Market lo hanno provato in produzione e i risultati possono essere visti qui sotto.

Se si avvia il programma su un server con ClickHouse, esso comincerà a funzionare in modalità daemon. Ogni ora verrà eseguita una query per verificare se ci sono nuove partizioni più vecchie di tre giorni che possono essere ottimizzate.
Nei prossimi piani c'è quello di fornire, almeno, pacchetti deb e, se possibile, anche rpm.
In conclusion
Negli ultimi 9 mesi, all'interno della mia azienda Ho trascorso molto tempo a lavorare con ClickHouse e graphite-web. È stata una buona esperienza, il risultato della quale ha reso possibile la rapida transizione da whisper a ClickHouse come sistema di archiviazione delle metriche. Spero che questo articolo rappresenti un inizio per un ciclo su quali miglioramenti abbiamo apportato in varie parti di questo stack e cosa faremo in futuro.
Per sviluppare la query sono stati spesi diversi litri di birra e giorni di amministrazione insieme a , per cui voglio esprimere la mia gratitudine. E anche per la revisione di questo articolo.
Fonte: habr.com




