Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Poiché ClickHouse è un sistema specializzato, è importante tenere a mente le peculiarità della sua architettura durante il suo utilizzo. In questa presentazione, Aleksey parlerà di esempi comuni di errori nell'uso di ClickHouse che possono portare a prestazioni inefficaci. Attraverso esempi pratici, verrà mostrato come la scelta di un particolare schema di elaborazione dei dati possa influenzare le prestazioni in modo significativo.

Ciao a tutti! Mi chiamo Aleksey e mi occupo di ClickHouse.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Innanzitutto, voglio subito rassicurarvi: oggi non vi parlerò di cosa sia ClickHouse. Onestamente, mi ha stancato. Ogni volta ripeto la stessa cosa, e probabilmente tutti già lo sanno.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Invece, parlerò delle possibili insidie, ovvero di come si possa utilizzare ClickHouse in modo errato. In realtà, non c'è motivo di preoccuparsi, poiché sviluppiamo ClickHouse come un sistema semplice, comodo e funzionante 'out of the box'. È sufficiente installarlo e il gioco è fatto, senza problemi.

Tuttavia, è importante considerare che questo è un sistema specializzato e si può facilmente imbattersi in scenari di utilizzo insoliti che potrebbero portare il sistema fuori dalla sua zona di comfort.

Quindi, quali sono i problemi? Principalmente parlerò delle cose ovvie. È evidente per tutti, tutti capiscono e possono essere contenti di essere così intelligenti, mentre chi non capisce, scoprirà qualcosa di nuovo.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Il primo esempio semplice, che purtroppo si verifica spesso, è un gran numero di inserti con piccoli batch, ossia un gran numero di piccoli inserti.

Se consideriamo come ClickHouse esegue gli insert, puoi inviare un flusso di dati di un terabyte con una sola richiesta. Non è un problema.

E diamo un'occhiata a quale sarà una tipica prestazione. Ad esempio, abbiamo una tabella con dati da Yandex.Metrica. Hit. 105 colonne. 700 byte in forma non compressa. E inseriamo in modo appropriato batch di un milione di righe.

Inseriamo nella tabella MergeTree, otteniamo mezzo milione di righe al secondo. Ottimo. Nella tabella replicata – sarà un po' meno, circa 400.000 righe al secondo.

E se attiviamo l'inserimento con quorum, otteniamo un po' meno, ma comunque una prestazione decente, 250.000 righe al secondo. L'inserimento con quorum è una funzione non documentata in ClickHouse*.

* a partire dal 2020, già documentata.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Cosa succede se si lavora male? Inserendo una riga alla volta nella tabella MergeTree, otteniamo 59 righe al secondo. Questo è 10.000 volte più lento. In ReplicatedMergeTree – 6 righe al secondo. E se poi si attiva anche il quorum, si arriva a 2 righe al secondo. A mio avviso, è una situazione inaccettabile. Come può essere così lento? Ho persino una maglietta con scritto che ClickHouse non dovrebbe rallentare. Tuttavia, a volte succede.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

In realtà, è una nostra mancanza. Avremmo potuto far funzionare tutto correttamente, ma non l'abbiamo fatto. E non l'abbiamo fatto perché per il nostro scenario non era necessario. Avevamo già dei batch. I batch arrivavano a noi e tutto funzionava bene. Ma, naturalmente, possono esserci vari scenari. Ad esempio, quando hai un sacco di server su cui vengono generati dati. E questi inseriscono dati non così frequentemente, ma comunque ci sono inserimenti frequenti. E bisogna trovare un modo per evitarlo.

Dal punto di vista tecnico, la questione è che quando si esegue un inserimento in ClickHouse, i dati non vengono memorizzati in nessun memtable. Non abbiamo nemmeno un vero log structure MergeTree, ma semplicemente un MergeTree, poiché non ci sono log o memTable. I dati vengono scritti direttamente nel file system, già organizzati per colonne. E se avete 100 colonne, dovrete scrivere più di 200 file in una directory separata. Tutto ciò è piuttosto ingombrante.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

E sorge la domanda: «Come procedere correttamente?», se ci si trova nella situazione di dover comunque scrivere dati in ClickHouse.

Metodo 1. Questo è il metodo più semplice. Utilizzare un sistema di coda distribuito, come Kafka. Si estraggono semplicemente i dati da Kafka e si esegue un batch ogni secondo. E il tutto funzionerà correttamente, registrate e tutto procederà senza problemi.

I difetti sono che Kafka è un'altra ingombrante sistema distribuito. Capisco se nella vostra azienda già avete Kafka. È un bene, è conveniente. Ma se non lo avete, allora vale la pena riflettere più volte prima di introdurre un'altra piattaforma distribuita nel vostro progetto. Pertanto, è opportuno considerare delle alternative.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 2. Un'alternativa old-school piuttosto semplice. Avete un server che genera i vostri log. Questo server scrive semplicemente i log in un file. E ogni secondo, per esempio, rinominiamo quel file e ne apriamo uno nuovo. Un secondo script, sia tramite cron che come daemon, prende il file più vecchio e lo scrive in ClickHouse. Se scrivete i log ogni secondo, tutto funzionerà a meraviglia.

Ma il difetto di questo metodo è che se il server che genera i log scompare, anche i dati scompariranno.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 3. C'è un altro metodo interessante che non utilizza file temporanei. Ad esempio, avete un qualche tipo di sistema di pubblicità o un daemon interessante che genera dati. Potete accumulare un certo numero di dati direttamente nella RAM, nel buffer. E quando è passato un determinato periodo di tempo, mettete quel buffer da parte, ne create uno nuovo e in un thread separato inserite ciò che avete accumulato in ClickHouse.

D'altra parte, i dati scompaiono anche con il kill -9. Se il tuo server si arresta, perderai questi dati. Inoltre, c'è il problema che, se non sei riuscito a scrivere nel database, i dati si accumuleranno nella RAM. E o finirà la RAM o semplicemente perderai i dati.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 4. Un altro modo interessante. Hai un processo server. E può inviare dati a ClickHouse immediatamente, ma farlo in un'unica connessione. Ad esempio, invia una richiesta http con transfer-encoding: chunked con insert. E genera chunk non troppo raramente, puoi inviare ogni riga, anche se ci sarà un overhead per il framing di questi dati.

Tuttavia, in questo caso i dati verranno inviati a ClickHouse subito. E ClickHouse li bufferizzerà da solo.

Ma sorgono anche problemi. Ora perderai dati, in particolare quando il tuo processo si arresta e, se il processo ClickHouse si arresta, perché sarà un insert incompleto. In ClickHouse gli insert sono atomici fino a una certa soglia specificata in numero di righe. In linea di principio, è un modo interessante. Anche questo può essere usato.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 5. Ecco un altro interessante approccio. Si tratta di un server sviluppato dalla community per l'elaborazione batch dei dati. Non l'ho esaminato personalmente, quindi non posso garantire nulla. Tuttavia, nemmeno per ClickHouse vengono fornite garanzie. Anche questo è open source, ma dall'altra parte, potreste essere abituati a determinati standard di qualità che ci impegniamo a garantire. Per questa soluzione, non lo so, andate su GitHub e guardate il codice. Potrebbe essere che abbiano scritto qualcosa di valido.

* a partire dal 2020, si dovrebbe anche considerare KittenHouse.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 6. Un altro modo è utilizzare le tabelle Buffer. I vantaggi di questo metodo sono che è molto semplice da iniziare a usare. Crei una tabella Buffer e ci inserisci i dati.

E il problema è che non viene risolto completamente. Se nella scrittura di tipo MergeTree devi raggruppare i dati in un solo batch al secondo, quando scrivi in una tabella buffer, devi raggruppare almeno fino a diverse migliaia al secondo. Se supera le 10.000 al secondo, sarà comunque problematico. E se si inseriscono batch, hai visto che si arriva a centinaia di migliaia di righe al secondo. E questo è già su dati piuttosto pesanti.

E anche le tabelle buffer non hanno log. E se c'è qualcosa che non va con il tuo server, i dati andranno persi.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

E come bonus, recentemente in ClickHouse abbiamo introdotto la possibilità di prelevare dati da Kafka. Esiste un motore di tabelle – Kafka. Basta crearne uno. E su di esso è possibile agganciare viste materializzate. In questo caso, estrarrà autonomamente i dati da Kafka e li inserirà nelle tabelle desiderate.

E ciò che è particolarmente gratificante di questa possibilità è che non l'abbiamo realizzata noi. È una funzionalità della community. E quando dico "funzionalità della community", lo dico senza alcun disprezzo. Abbiamo esaminato il codice, abbiamo fatto le revisioni, dovrebbe funzionare correttamente.

* alla fine del 2020, è stata introdotta un'analoga supporto per RabbitMQ.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Cosa potrebbe essere scomodo o inaspettato quando si inseriscono dati? Se si esegue una query insert values e in values si scrivono delle espressioni calcolate. Ad esempio, now() è un'espressione calcolata. In questo caso, ClickHouse deve avviare l'interprete di queste espressioni per ogni riga, e le prestazioni ne risentono notevolmente. È meglio evitarlo.

* Al momento, il problema è completamente risolto, non ci sono più regressioni delle prestazioni nell'uso delle espressioni in VALUES.

Un altro esempio di potenziali problemi si verifica quando i dati di un singolo batch appartengono a molteplici partizioni. Per impostazione predefinita, in ClickHouse le partizioni sono mensili. Se si inserisce un batch di un milione di righe e queste riguardano un arco di diversi anni, si troverà ad avere decine di partizioni. Questo equivale a batch di dimensioni significativamente più piccole, poiché all'interno vengono sempre suddivisi per partizioni.

* Recentemente, ClickHouse ha aggiunto in modalità sperimentale il supporto per il formato compatto di parti e parti in memoria con write-ahead log, il che risolve quasi completamente il problema.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ora esaminiamo il secondo tipo di problema: la tipizzazione dei dati.

La tipizzazione dei dati può essere stretta o basata su stringhe. La tipizzazione basata su stringhe è quando hai semplicemente dichiarato che tutti i tuoi campi sono di tipo string. È una cattiva pratica. Non dovresti farlo.

Vediamo come fare correttamente nei casi in cui vogliamo indicare che un certo campo è una stringa, lasciando a ClickHouse il compito di gestirlo, senza preoccuparci troppo. Tuttavia, vale la pena fare alcuni sforzi.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ad esempio, abbiamo un indirizzo IP. In un caso lo abbiamo salvato come una stringa, per esempio, 192.168.1.1. Nell'altro caso, sarà un numero di tipo UInt32*. 32 bit sono sufficienti per un indirizzo IPv4.

Prima di tutto, sorprendentemente, i dati verranno compressi in modo simile. Ci sarà una certa differenza, ovviamente, ma non così grande. Quindi non ci sono problemi particolari con l'input/output del disco.

Ma c'è una differenza significativa in termini di tempo di elaborazione della CPU e di tempo di esecuzione della query.

Calcoliamo il numero di indirizzi IP unici se memorizzati sotto forma di numeri. Si ottengono 137 milioni di righe al secondo. Se invece sono memorizzati come stringhe, si ottengono 37 milioni di righe al secondo. Non so perché ci sia questa coincidenza. Ho eseguito personalmente queste query. Tuttavia, è circa quattro volte più lento.

Se consideriamo la differenza nello spazio su disco, c'è anche una variazione. La differenza è di circa un quarto, poiché ci sono molti indirizzi IP unici. Se qui ci fossero righe con un numero ridotto di valori diversi, esse si comprimerebbero facilmente secondo un dizionario, mantenendo un volume simile.

Una differenza quadrupla nel tempo non è da trascurare. Forse a voi non importa, ma quando vedo una differenza simile, mi rende triste.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Esaminiamo diversi casi.

1. Un caso in cui avete pochi valori unici diversi. In questo caso, utilizziamo una pratica semplice che probabilmente conoscete e potete applicare a qualsiasi DBMS. Questo ha senso non solo per ClickHouse. È sufficiente memorizzare nella base dati identificatori numerici. La conversione in stringhe e viceversa può avvenire sul lato della vostra applicazione.

Ad esempio, avete una regione e cercate di salvarla come stringa. Risultato: Mosca e MO. Quando vedo 'Mosca', penso che vada bene, ma quando appare anche MO, mi sembra davvero triste. Quanti byte sono?

Invece, salviamo semplicemente il numero Ulnt32 e 250. Abbiamo 250 in Yandex, ma potrebbe essere diverso da voi. A scanso di equivoci, vorrei sottolineare che ClickHouse ha una funzionalità integrata per lavorare con le geodatabase. Basta registrare un elenco con le regioni, incluso quello gerarchico, cioè ci sarà sia Mosca che MO, e tutto ciò di cui hai bisogno. E puoi convertire a livello di query.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Il secondo approccio è simile, ma già con supporto all'interno di ClickHouse. È il tipo di dato Enum. All'interno di Enum si definiscono tutti i valori necessari. Ad esempio, il tipo di dispositivo e poi scrivi: desktop, mobile, tablet, televisore. Solo 4 varianti.

Il problema è che è necessario alternare periodicamente. È stato aggiunto solo un'opzione. Facciamo un alter table. In realtà, l'alter table in ClickHouse è gratuito. È particolarmente gratuito per Enum, poiché i dati su disco non cambiano. Tuttavia, l'alter acquisisce un blocco sulla tabella e deve attendere che tutte le selezioni siano completate. Solo dopo l'alter verrà eseguito, cioè ci sono comunque alcuni inconvenienti.

* nelle versioni recenti di ClickHouse, l'ALTER è stato completamente non bloccante.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un'altra opzione piuttosto unica per ClickHouse è la connessione di dizionari esterni. Puoi scrivere numeri in ClickHouse e mantenere i tuoi dizionari in qualsiasi sistema ti sia comodo. Ad esempio, puoi utilizzare: MySQL, Mongo, Postgres. Puoi persino creare il tuo microservizio che fornisce questi dati tramite http. E a livello di ClickHouse scrivi una funzione che trasforma questi dati da numeri a stringhe.

Questo è un modo specializzato, ma molto efficace per eseguire un join con una tabella esterna. Esistono due varianti. In una di queste, i dati saranno completamente memorizzati nella cache, presenti interamente in memoria e aggiornati con una certa periodicità. Nell'altra variante, se i dati non possono essere caricati in memoria, è possibile memorizzarli parzialmente nella cache.

Ecco un esempio. C'è Yandex.Direct. Lì ci sono campagne pubblicitarie e banner. Ci sono probabilmente circa dieci milioni di campagne pubblicitarie. E più o meno si adattano in memoria. Ma ci sono miliardi di banner che non ci stanno. E utilizziamo un dizionario memorizzato nella cache da MySQL.

L'unico problema è che il dizionario memorizzato nella cache funzionerà bene solo se il tasso di hit è vicino al 100%. Se è inferiore, per l'elaborazione delle richieste di ogni blocco di dati sarà necessario recuperare effettivamente le chiavi mancanti e raccogliere i dati da MySQL. Per quanto riguarda ClickHouse, posso garantire che non rallenta, ma non parlerò di altri sistemi.

Come bonus, i dizionari sono un modo molto semplice per aggiornare retrospettivamente i dati in ClickHouse. Ovvero, se avevate un report sulle campagne pubblicitarie, un utente può semplicemente cambiare la campagna pubblicitaria e tutti i dati vecchi, in tutti i report, verranno aggiornati. Se scrivete direttamente nelle righe della tabella, non sarà possibile aggiornarle.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro modo, quando non sapete da dove prendere gli identificatori per le vostre righe, è semplicemente fare un hash. La soluzione più semplice è utilizzare un hash a 64 bit.

L'unico problema è che se l'hash è a 64 bit, ci saranno quasi sicuramente delle collisioni. Perché se ci sono miliardi di righe, la probabilità diventa significativa.

E non sarebbe una buona idea fare così per hashare i nomi delle campagne pubblicitarie. Se le campagne pubblicitarie di aziende diverse si confondono, il risultato sarà incomprensibile.

C'è un trucco semplice. Certo, non è molto adatto per dati seri, ma se non sono così critici, basta aggiungere l'identificatore del cliente alla chiave del dizionario. In questo modo, ci saranno collisioni, ma solo all'interno di un singolo cliente. Questo metodo è utilizzato per la mappa dei link in Yandex.Metrica. Abbiamo degli URL lì, memorizziamo gli hash. E sappiamo che ci sono collisioni, ovviamente. Ma quando la pagina viene visualizzata, la probabilità che su una stessa pagina un utente abbia degli URL sovrapposti che vengano notati è così bassa che si può trascurare.

Come bonus, per molte operazioni è sufficiente avere solo gli hash e non è necessario memorizzare le stringhe in nessun altro posto.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro esempio, se le stringhe sono brevi, come i domini dei siti. Possono essere memorizzate così come sono. Oppure, ad esempio, la lingua del browser ru - 2 byte. Certo, dispiace un po' per i byte, ma non preoccuparti, 2 byte non sono un problema. Per favore, memorizza così come sono, non complicarti la vita.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro caso in cui, al contrario, ci sono molte stringhe e in esse molte uniche, e in più con un numero potenzialmente illimitato. Un esempio tipico sono le frasi di ricerca o gli URL. Le frasi di ricerca, anche a causa di errori di battitura. Vediamo quante frasi di ricerca uniche ci sono in un giorno. Risulta che sono quasi la metà di tutti gli eventi. E in questo caso potresti pensare di normalizzare i dati, contare gli identificatori e metterli in una tabella separata. Ma non è necessario farlo. Basta conservare queste stringhe così come sono.

È meglio – non inventare nulla, perché se conservi separatamente, sarà necessario fare un join. E questo join – nel migliore dei casi è accesso casuale in memoria, se ci sta dentro. Se non ci sta, ci saranno problemi.

E se i dati sono memorizzati in loco, vengono semplicemente letti nell'ordine corretto dal file system e tutto funziona bene.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Se hai URL o qualche altra stringa complessa e lunga, dovresti considerare che puoi calcolare un riassunto in anticipo e registrarlo in una colonna separata.

Per gli URL, ad esempio, è possibile memorizzare separatamente il dominio. E se hai veramente bisogno di un dominio, utilizza semplicemente questa colonna, mentre gli URL saranno conservati e non dovrai neppure toccarli.

Vediamo quale può essere la differenza. In ClickHouse c'è una funzione specializzata che calcola il dominio. È molto veloce, l'abbiamo ottimizzata. E, a essere onesti, non rispetta nemmeno gli RFC, ma per il resto calcola tutto ciò che ci serve.

In un caso, estrarremo semplicemente gli URL e calcoleremo il dominio. Questo richiede 166 millisecondi. Se invece prendiamo un dominio già pronto, impieghiamo solo 67 millisecondi, cioè quasi tre volte più veloce. E non è che abbiamo bisogno di fare calcoli complessi, ma semplicemente leggiamo meno dati.

Per qualche motivo, una delle query che è più lenta ha una velocità di trasferimento dei gigabyte al secondo superiore. Perché legge più gigabyte. Questi dati sono completamente superflui. La query sembra funzionare più velocemente, ma ci mette più tempo a completarsi.

Se si osserva la quantità di dati su disco, si può vedere che l'URL è di 126 megabyte, mentre il dominio è solo di 5 megabyte. Quindi è 25 volte meno. Tuttavia, la richiesta viene eseguita solo 4 volte più velocemente. Questo perché i dati sono caldi. Se fossero stati freddi, probabilmente sarebbe stato 25 volte più veloce a causa dell'input-output del disco.

A proposito, se si valuta quanto il dominio sia più piccolo dell'URL, risulta essere circa 4 volte. Eppure, i dati su disco occupano 25 volte meno spazio. Perché? A causa della compressione. Sia l'URL che il dominio vengono compressi. Ma spesso l'URL contiene un sacco di spazzatura.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Naturalmente, è importante utilizzare i tipi di dati corretti, che sono progettati specificamente per i valori necessari o che sono adatti. Se siete in IPv4, memorizzate come UInt32*. Se siete in IPv6, allora FixedString(16), perché l'indirizzo IPv6 è di 128 bit, cioè memorizzatelo direttamente in formato binario.

E cosa fare se a volte avete indirizzi IPv4 e altre volte IPv6? Sì, potete memorizzare entrambi. Una colonna per IPv4, l'altra per IPv6. Certamente, esiste la possibilità di mappare IPv4 in IPv6. Questo funzionerà, ma se avete bisogno spesso dell'indirizzo IPv4 nelle vostre richieste, sarebbe meglio metterlo in una colonna separata.

* ora in ClickHouse ci sono tipi di dati separati per IPv4 e IPv6, che memorizzano i dati in modo altrettanto efficiente come i numeri, ma li rappresentano in modo altrettanto conveniente come le stringhe.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

È anche importante notare che è consigliabile preprocessare i dati in anticipo. Ad esempio, se ricevete dei log grezzi. Potrebbe sembrare allettante inserire tutto direttamente in ClickHouse senza fare nulla, ma è comunque opportuno eseguire i calcoli necessari.

Prendiamo ad esempio la versione del browser. In un certo reparto vicino, al quale non voglio puntare il dito, la versione del browser viene memorizzata in questo modo, cioè come stringa: 12.3. E poi, per generare un rapporto, dividono questa stringa per un array, e prendono poi il primo elemento dell'array. Naturalmente, tutto rallenta. Ho chiesto perché stessero facendo in questo modo. Mi hanno risposto che non amano l'ottimizzazione prematura. E io non amo la pessimizzazione prematura.

In questo caso, sarebbe più corretto separare in 4 colonne. Non abbiate paura, perché questo è ClickHouse. ClickHouse è un database a colonne. E più piccole colonne ordinate ci sono, meglio è. Se avrete 5 BrowserVersion, fate 5 colonne. È normale.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ora consideriamo cosa fare se hai molte stringhe molto lunghe o array molto lunghi. Non è necessario memorizzarli in ClickHouse. Puoi invece salvare in ClickHouse solo un identificatore. E per queste stringhe lunghe, puoi conservarle in un altro sistema.

Ad esempio, in uno dei nostri servizi analitici ci sono alcuni parametri di eventi. E se sugli eventi arrivano molti parametri, noi salviamo semplicemente i primi 512 che incontriamo. Perché 512 non è un problema.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Se non sei sicuro dei tuoi tipi di dati, puoi anche registrare i dati in ClickHouse, ma in una tabella temporanea di tipo Log, progettata per i dati temporanei. Dopodiché, puoi analizzare la distribuzione dei valori, vedere cosa hai e definire i tipi corretti.

* ora in ClickHouse c'è il tipo di dati LowCardinality che consente di memorizzare efficacemente le stringhe con minori oneri operativi.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ora consideriamo un altro caso interessante. A volte le cose funzionano in modo strano per alcune persone. Entro e vedo questo. E mi immagino subito che sia stato fatto da un amministratore molto esperto e intelligente, con una vasta esperienza nella configurazione di MySQL versione 3.23.

Qui vediamo mille tabelle, ognuna delle quali contiene un resto di una divisione di qualcosa di incomprensibile per mille.

In linea di principio, rispetto l'esperienza altrui e comprendo anche quanto possa essere stata sofferta questa esperienza.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

E le ragioni sono più o meno chiare. Sono stereotipi vecchi che possono essersi accumulati lavorando con altri sistemi. Ad esempio, nelle tabelle MyISAM non esiste una chiave primaria cluster. E questo modo di suddividere i dati potrebbe essere un tentativo disperato di ottenere la stessa funzionalità.

Un'altra ragione è che eseguire operazioni come alter su tabelle grandi è complicato. Tutto verrà bloccato. Anche se nelle versioni moderne di MySQL questo problema non è più così serio.

Oppure, ad esempio, il microsharding, ma ne parleremo tra poco.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

In ClickHouse non è necessario fare così, perché, in primo luogo, la chiave primaria è cluster e i dati sono ordinati in base alla chiave primaria.

A volte mi chiedono: «Come cambia la performance delle query di intervallo in ClickHouse in base alle dimensioni della tabella?». Rispondo che non cambia affatto. Ad esempio, se avete una tabella con un miliardo di righe e leggete un intervallo di un milione di righe, va tutto bene. Se nella tabella ci sono un trilione di righe e leggete un milione di righe, sarà praticamente lo stesso.

In secondo luogo, non servono cose come le partizioni manuali. Se andate a vedere cosa c'è nel file system, noterete che la tabella è un elemento piuttosto robusto. All'interno ci sono strutture simili a partizioni. Cioè, ClickHouse fa tutto per voi e non dovete preoccuparvi.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Alter in ClickHouse è gratuito, se si tratta di alterare colonne aggiungendo/rimuovendo.

E non ha senso creare tabelle piccole, perché se avete dieci righe o diecimila righe nella tabella, non fa alcuna differenza. ClickHouse è un sistema che ottimizza il throughput, non la latenza, quindi elaborare dieci righe non ha senso.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

È meglio utilizzare una grande tabella. Liberatevi dei vecchi stereotipi, andrà tutto bene.

Come bonus, nella nostra ultima versione è stata introdotta la possibilità di creare una chiave di partizionamento personalizzata per eseguire varie operazioni di manutenzione su singole partizioni.

Ad esempio, potresti avere molte piccole tabelle, magari quando hai bisogno di elaborare dei dati intermedi. Ricevi dei chunk e devi eseguire una trasformazione su di essi prima di scriverli nella tabella finale. Per questo caso esiste un ottimo motore di tabella: StripeLog. È simile a TinyLog, solo migliore.

* ora in ClickHouse c'è anche la funzione tabellare input.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro antipattern è il micro-sharding. Ad esempio, hai bisogno di sharding per i dati e hai 5 server, ma domani ne avrai 6. Ti chiedi come riequilibrare questi dati. E invece di suddividerli in 5 shard, ne crei 1.000. E poi assegni ciascuno di questi micro-shard a un server separato. Così, su un server, ad esempio, avrai 200 ClickHouse, con istanze separate su porte diverse o diversi database.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ma in ClickHouse non è molto efficace. Questo perché anche un solo instance di ClickHouse cerca di utilizzare tutte le risorse disponibili del server per elaborare una singola richiesta. Ad esempio, se hai un server con 56 core di CPU, eseguendo una richiesta che impiega un secondo, utilizzerà tutti e 56 i core. Se hai 200 ClickHouse su un unico server, avrai 10.000 thread attivi. In generale, le prestazioni ne risentiranno notevolmente.

Un'altra ragione è che la distribuzione del lavoro tra questi instances sarà disomogenea. Alcuni termineranno prima, altri dopo. Se tutto avvenisse all'interno di un unico instance, ClickHouse sarebbe in grado di gestire correttamente la distribuzione dei dati tra i thread.

Un'ulteriore ragione è che si verificherà interazione tra i processi tramite TCP. I dati dovranno essere serializzati e deserializzati e questo comporta un numero enorme di micro-shard. Questo metodo risulta semplicemente inefficiente.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro antipattern, anche se è difficile definirlo tale, è la presenza di un numero elevato di pre-aggregazioni.

In generale, la preaggregazione è utile. Se avevate un miliardo di righe e le avete aggregate a 1.000 righe, ora la query si esegue istantaneamente. Tutto fantastico. È possibile farlo. A tal fine, anche in ClickHouse esiste un tipo di tabella chiamato AggregatingMergeTree, che esegue l'aggregazione incrementale durante l'inserimento dei dati.

Tuttavia, ci sono casi in cui si pensa di aggregare i dati in un modo, e poi in un altro dipartimento, purtroppo non voglio dire quale, utilizzano tabelle SummingMergeTree per la somma in base alla chiave primaria, usando come chiave primaria circa 20 colonne diverse. Per ragioni di riservatezza, ho cambiato i nomi di alcune colonne, ma l'idea è proprio quella.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

E sorgono problemi. In primo luogo, la quantità di dati non diminuisce di molto. Ad esempio, potrebbe ridursi di tre volte. Tre volte sarebbe un buon compromesso per concedersi opportunità illimitate nell'analisi, che si ottengono se i dati non sono aggregati. Se i dati sono aggregati, invece di analisi si ottiene solo una misera statistica.

E cosa infastidisce di più? Il fatto che queste persone del dipartimento vicino camminano e a volte chiedono di aggiungere un'altra colonna alla chiave primaria. Cioè, abbiamo aggregato i dati in questo modo, e ora vogliamo qualcosa di più. Ma in ClickHouse non esiste il comando alter per la chiave primaria. Quindi bisogna scrivere qualche script in C++. E non mi piacciono gli script, anche se sono in C++.

E se si guarda a cosa è stato creato ClickHouse, i dati non aggregati sono proprio lo scenario per cui è nato. Se usi ClickHouse per dati non aggregati, stai facendo tutto correttamente. Se stai aggregando, allora a volte è giustificabile.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro caso interessante sono le query in ciclo infinito. A volte entro in un server di produzione e guardo la lista dei processi. E ogni volta scopro che sta succedendo qualcosa di terribile.

Ad esempio, qualcosa del genere. Qui è subito chiaro che tutto poteva essere eseguito in una sola query. Basta scrivere lì url in e la lista.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Perché ci sono molte richieste in un ciclo infinito - è un problema? Se l'indice non viene utilizzato, avrai molte passate sugli stessi dati. Ma se l'indice viene utilizzato, ad esempio, hai una chiave primaria su ru e scrivi url = qualcosa. E pensi che verrà letta in modo mirato una sola url dalla tabella, va tutto bene. Ma in realtà non è così. Perché ClickHouse fa tutto per pacchetti.

Quando deve leggere un certo intervallo di dati, legge un po' di più, perché l'indice in ClickHouse è sparso. Questo indice non consente di trovare una singola riga nella tabella, solo un intervallo. E i dati vengono compressi in blocchi. Per leggere una riga, devi prendere un intero blocco e decomprimerlo. E se esegui molte richieste, ci saranno molte sovrapposizioni e una gran quantità di lavoro verrà svolta ripetutamente.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

E come bonus, possiamo notare che in ClickHouse non bisogna temere di passare anche megabyte e anche centinaia di megabyte nella sezione IN. Ricordo dalla nostra esperienza che se in MySQL passiamo un sacco di valori nella sezione IN, ad esempio, 100 megabyte di numeri, MySQL consuma 10 gigabyte di memoria e poi non succede più nulla, tutto funziona male.

Il secondo punto è che in ClickHouse, se le vostre query usano un indice, non è mai più lento di una scansione completa, cioè se è necessario leggere quasi tutta la tabella, procederà in modo sequenziale e leggerà l'intera tabella. Insomma, si gestirà da solo.

Tuttavia, ci sono alcune difficoltà. Ad esempio, la sezione IN con una subquery non utilizza l'indice. Ma questo è un nostro problema e dobbiamo risolverlo. Non c'è nulla di fondamentale qui. Ci metteremo a ripararlo.

E un'altra cosa interessante è che se avete una query molto lunga e si sta effettuando un'elaborazione distribuita delle query, questa query molto lunga verrà inviata a ciascun server senza compressione. Ad esempio, 100 megabyte e 500 server. E, di conseguenza, verranno trasferiti 50 gigabyte attraverso la rete. Sarà trasmesso e poi tutto verrà eseguito con successo.

* già in uso; tutto sistemato come promesso.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

È un caso piuttosto comune quando le richieste provengono da un'API. Ad esempio, avete creato un vostro servizio. E se il vostro servizio è richiesto, avete aperto un'API e già dopo due giorni vedete che sta succedendo qualcosa di strano. Tutto è sovraccarico e pervengono richieste anomale che non avrebbero mai dovuto esserci.

E la soluzione è una sola. Se hai aperto un'API, sarà necessario limitarla. Ad esempio, puoi introdurre delle quote. Non ci sono altre opzioni ragionevoli. Altrimenti, verrà subito scritto uno script e avrai problemi.

In ClickHouse c'è una funzionalità speciale: il conteggio delle quote. È possibile anche passare la tua chiave di quota. Questo è, ad esempio, un identificatore interno dell'utente. E le quote verranno calcolate in modo indipendente per ciascuno di essi.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ora c'è un'altra cosa interessante. Questa è la replicazione a comando manuale.

Conosco molti casi in cui, nonostante ClickHouse abbia un supporto incorporato per la replicazione, le persone replicano ClickHouse manualmente.

Qual è il principio? Avete una pipeline di elaborazione dei dati. E funziona in modo indipendente, ad esempio, in diversi data center. Registrate gli stessi dati nello stesso modo in ClickHouse, in un certo senso. Tuttavia, la pratica mostra che i dati finiranno comunque per divergere a causa di alcune peculiarità nel vostro codice. Spero che non sia il vostro caso.

E periodicamente voi dovrete comunque sincronizzare manualmente. Ad esempio, una volta al mese gli amministratori eseguono rsync.

In realtà, è molto più semplice utilizzare la replica integrata in ClickHouse. Ma ci possono essere alcune controindicazioni, perché per questo è necessario utilizzare ZooKeeper. Non dirò nulla di negativo su ZooKeeper, in linea di principio, il sistema funziona, ma a volte le persone non lo usano a causa della java-fobia, perché ClickHouse è un ottimo sistema, scritto in C++, che può essere utilizzato e funzionerà alla grande. E ZooKeeper è in java. E in qualche modo non si ha voglia di guardarci, ma a quel punto potete usare la replicazione manuale.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

ClickHouse è un sistema pratico. Tiene conto delle vostre esigenze. Se avete una replica a comando manuale, potete creare una tabella Distributed che monitori le vostre repliche manuali e gestisca automaticamente il failover. Esiste persino un'opzione speciale che consente di evitare i problemi, anche se le vostre repliche si discostano sistematicamente.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Possono sorgere problemi se utilizzate motori di tabella primitivi. ClickHouse è un costruttore dotato di una varietà di motori di tabella. In tutti i casi seri, come indicato nella documentazione, utilizzate le tabelle della famiglia MergeTree. Tutti gli altri sono per casi speciali o per test.

Nella tabella MergeTree non è necessario avere una data e un orario. Potete comunque utilizzarla. Se non avete data e ora, specificate che il default è il 2000. Questo funzionerà senza richiedere risorse.

Nella nuova versione del server, è anche possibile specificare un partizionamento personalizzato senza una chiave di partizione. Sarà la stessa cosa.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

D'altra parte, è possibile utilizzare semplici motori di tabelle. Ad esempio, caricare i dati una volta e vedere, ruotare e rimuovere. Puoi utilizzare Log.

Oppure memorizzare piccole quantità per l'elaborazione intermedia – questo è StripeLog o TinyLog.

Memory può essere utilizzato se si tratta di una piccola quantità di dati e vuoi semplicemente ruotare qualcosa in RAM.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

ClickHouse non ama molto i dati eccessivamente normalizzati.

Ecco un esempio tipico. Ci sono un'enorme quantità di URL. Li hai messi in una tabella separata. Poi hai deciso di fare un JOIN con loro, ma questo non funzionerà, di norma, perché ClickHouse supporta solo Hash JOIN. Se la memoria non è sufficiente per un gran numero di dati con cui fare un join, il JOIN non potrà essere eseguito*.

Se i dati hanno una grande cardinalità, non preoccuparti, conservali in forma denormalizzata, gli URL direttamente inplace nella tabella principale.

* ora in ClickHouse ci sono anche merge join e funzionano in condizioni in cui i dati intermedi non possono essere contenuti in memoria. Ma questo non è efficiente e la raccomandazione rimane valida.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro paio di esempi, ma ho già dei dubbi se siano anti-modelli o meno.

In ClickHouse c'è un noto svantaggio. Non supporta gli aggiornamenti*. In un certo senso, questo è anche un bene. Se hai dei dati importanti, come la contabilità, nessuno potrà modificarli, perché non ci sono aggiornamenti.

* da tempo è stata aggiunta la supporto per update e delete in modalità batch.

Tuttavia, ci sono alcuni metodi speciali che consentono di effettuare aggiornamenti in background. Ad esempio, le tabelle di tipo ReplaceMergeTree. Esse eseguono aggiornamenti durante le fusioni in background. Puoi forzarlo usando il comando optimize table. Ma non farlo troppo spesso, perché questo comporterebbe una riscrittura completa della partizione.

I JOIN distribuiti in ClickHouse sono anche gestiti male dal pianificatore di query.

È problematico, ma a volte va bene.

Usare ClickHouse solo per leggere dati tramite select*.

Non consiglierei di utilizzare ClickHouse per operazioni di calcolo impegnative. Ma non è proprio così, perché stiamo già deviando da questa raccomandazione. Recentemente abbiamo aggiunto la possibilità di applicare modelli di machine learning in ClickHouse – Catboost. E questo mi preoccupa, perché penso: “Che orrore. Quanti cicli per byte ci sono?!”. Mi dispiace molto sprecare cicli per byte.

Uso efficace di ClickHouse. Alexey Milovidov (Yandex)

Ma non preoccupatevi, installate ClickHouse, andrà tutto bene. Se avete bisogno, abbiamo una comunità. A proposito, la comunità siete voi. E se avete qualche problema, potete almeno entrare nella nostra chat e, spero, riceverete aiuto.

Domande

Grazie per la presentazione! Dove posso segnalare un malfunzionamento di ClickHouse?

Potete segnalarmelo direttamente adesso.

Ho appena iniziato a usare ClickHouse. Ho subito fatto cadere l'interfaccia cli.

Vi è andata bene.

Poco dopo ho fatto cadere il server con una piccola select.

Siete davvero dotati.

Ho aperto un bug su GitHub, ma è stato ignorato.

Vedremo.

Aleksey mi ha attirato nella presentazione con la promessa di spiegarmi come compressate i dati.

È molto semplice.

L'ho capito solo ieri. Serve più specificità.

Non ci sono trucchi terribili. Si tratta semplicemente di compressione a blocchi. Per impostazione predefinita, si usa LZ4, ma si può attivare ZSTD*. I blocchi variano da 64 kilobyte a 1 megabyte.

* ci sono anche codec di compressione specializzati supportati che possono essere usati in combinazione con altri algoritmi.

I dati nei blocchi sono semplicemente grezzi?

Non proprio grezzi. Ci sono array. Se hai una colonna numerica, i numeri sono riposti consecutivamente in un array.

Capito.

Alexey, un esempio simile a uniqExact con gli indirizzi IP, cioè, che uniqExact viene calcolato più lentamente per le stringhe rispetto ai numeri, e così via. E se applicassimo un trucco e facessimo il casting al momento della lettura? Cioè, avete detto che su disco non ci sono grandi differenze. Se leggiamo le stringhe da disco, facciamo il casting, allora i nostri aggregati saranno più veloci o no? O comunque ne guadagneremo solo in modo trascurabile? Mi sembra che l'abbiate testato, ma per qualche motivo non lo avete indicato nel benchmark.

Penso che sarà più lento rispetto a senza casting. In questo caso, l'indirizzo IP deve essere analizzato dalla stringa. Certo, in ClickHouse l'analisi degli indirizzi IP è anche ottimizzata. Abbiamo fatto del nostro meglio, ma lì avete i numeri scritti in forma decimale. È molto scomodo. D'altra parte, la funzione uniqExact funzionerà più lentamente sulle stringhe non solo perché sono stringhe, ma anche perché viene scelta un'altra specializzazione dell'algoritmo. Le stringhe vengono semplicemente elaborate in modo diverso.

E se prendiamo un tipo di dato più primitivo? Ad esempio, abbiamo registrato un user id, che abbiamo in, l'abbiamo scritto come stringa e poi effettuato il casting, sarà più divertente o no?

Ho dei dubbi. Penso che sia ancora più triste, perché analizzare numeri è una questione seria. Mi sembra che quel collega avesse persino fatto una presentazione su quanto sia difficile analizzare i numeri in forma di diecimila, o forse no.

Alexey, grazie mille per la presentazione! E grazie ancora per ClickHouse! Ho una domanda riguardo ai piani. C'è in programma una funzionalità per aggiornare i dizionari in modo parziale?

Cioè, un riavvio parziale?

Sì, sì. Tipo la possibilità di impostare un campo MySQL, cioè aggiornare dopo, per caricare solo quei dati, se il dizionario è molto grande.

Una funzionalità molto interessante. E mi sembra che qualcuno l'avesse proposta nella nostra chat. Potresti essere stato tu.

Non credo di essere stato io.

Ottimo, adesso sembra che ci siano due richieste. E si può iniziare a lavorarci con calma. Ma voglio avvertirti che questa funzionalità è piuttosto semplice da implementare. Cioè, in teoria, dovresti solo scrivere il numero di versione nella tabella e poi scrivere: versione inferiore a tale. E questo significa che probabilmente suggeriremo di farlo agli appassionati. Sei un appassionato?

Sì, ma purtroppo non in C++.

I tuoi colleghi sanno scrivere in C++?

Troverò qualcuno.

Ottimo.

* la possibilità è stata aggiunta due mesi dopo la presentazione – è stata sviluppata dall’autore della domanda e inviata pull request.

Grazie!

Buongiorno! Grazie per la presentazione! Hai menzionato che ClickHouse utilizza molto bene tutte le risorse disponibili. E il relatore accanto a Luxoft parlava della sua soluzione per Posta Russia. Ha detto che a loro è piaciuto molto ClickHouse, ma non lo hanno utilizzato al posto del loro principale concorrente proprio perché consumava tutta la CPU. E non sono riusciti a integrarlo nella loro architettura, nel loro ZooKeeper con i container. C'è un modo per limitare ClickHouse in modo che non consumi tutto ciò che gli diventa disponibile?

Sì, è possibile e molto facile. Se vuoi che consumi meno core, basta scrivere set max_threads = 1. E tutto, eseguirà la query su un solo core. Inoltre, puoi specificare impostazioni diverse per diversi utenti. Quindi nessun problema. E fai sapere ai colleghi di Luxoft che non è corretto che non abbiano trovato questa impostazione nella documentazione.

Ciao Alexey! Vorrei chiederti qualcosa. Ho sentito più volte che molti iniziano a usare ClickHouse come archivio per i log. Durante la presentazione hai detto di non farlo, ossia che non è necessario memorizzare stringhe lunghe. Qual è la tua opinione al riguardo?

Innanzitutto, i log di solito non sono stringhe lunghe. Ci sono, ovviamente, delle eccezioni. Ad esempio, un servizio scritto in Java può generare un'eccezione che viene registrata. E questo può avvenire in un ciclo infinito, riempiendo lo spazio su disco rigido. La soluzione è molto semplice. Se le stringhe sono molto lunghe, tagliale. E cosa significa lunghe? Decine di kilobyte - è un problema.

* nelle versioni recenti di ClickHouse, è attivata la "granularità adattiva dell'indice", che risolve gran parte del problema della memorizzazione delle stringhe lunghe.

E un kilobyte è normale?

Normale.

Ciao! Grazie per la presentazione! Ho già chiesto questo in chat, ma non ricordo se ho ricevuto risposta. È previsto un ampliamento della sezione WITH nel modo del CTE?

Per il momento no. La sezione WITH è piuttosto insignificante. È una funzionalità secondaria.

Ho capito. Grazie!

Grazie per la relazione! È molto interessante! Domanda globale. È in programma di implementare, forse in forma di placeholder, una modifica per l'eliminazione dei dati?

Assolutamente. Questo è il nostro primo compito nella nostra lista. Stiamo attualmente pianificando come fare tutto correttamente. E bisogna iniziare a premere sulla tastiera*.

* abbiamo premuto i tasti della tastiera e abbiamo fatto tutto.

Questo influenzerà in qualche modo le prestazioni del sistema? L'inserimento sarà veloce come adesso?

È possibile che le operazioni di delete e update siano molto pesanti, ma questo non influenzerà le prestazioni delle operazioni di select e insert.

E un'altra piccola domanda. Nella presentazione hai parlato della chiave primaria. Di conseguenza, abbiamo una partizione che per impostazione predefinita è mensile, giusto? E quando impostiamo un intervallo di date che rientra in un mese, leggiamo solo quella partizione, corretto?

Sì.

Ho una domanda. Se non possiamo identificare una chiave primaria, è corretto utilizzare il campo "Data" per ridurre al minimo la ristrutturazione dei dati in background, in modo che siano organizzati in modo più ordinato? Se non avete richieste in intervallo e non potete scegliere alcuna chiave primaria, ha senso inserire la data nella chiave primaria?

Sì.

Forse ha senso utilizzare nella chiave primaria un campo per il quale i dati si comprimerebbero meglio se sono ordinati secondo quel campo. Ad esempio, l'ID utente. Un utente visita lo stesso sito. In questo caso, inserite l'ID utente e l'orario. Così facendo, i dati si comprimeranno meglio. Per quanto riguarda la data, se non avete e non avete mai richieste in intervallo per date, allora non è necessario includere la data nella chiave primaria.

Bene, grazie mille!

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster