Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Poiché ClickHouse è un sistema specializzato, quando lo si utilizza è importante tenere conto delle caratteristiche della sua architettura. In questo rapporto, Alexey parlerà di esempi di errori comuni durante l'utilizzo di ClickHouse, che possono portare a un lavoro inefficace. Esempi pratici mostreranno come la scelta dell'uno o dell'altro schema di elaborazione dei dati possa modificare le prestazioni per ordini di grandezza.

Ciao a tutti! Mi chiamo Alexey, creo ClickHouse.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Innanzitutto mi affretto a farti piacere subito, oggi non ti dirò cos'è ClickHouse. Ad essere sincero, ne sono stanco. Ogni volta che ti dico di cosa si tratta. E probabilmente lo sanno già tutti.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ti dirò invece quali sono i possibili errori, cioè come puoi utilizzare ClickHouse in modo errato. In effetti, non c'è bisogno di avere paura, perché stiamo sviluppando ClickHouse come un sistema semplice, conveniente e funzionante fuori dagli schemi. L'ho installato, nessun problema.

Ma devi comunque tenere presente che questo sistema è specializzato e puoi facilmente imbatterti in un caso d'uso insolito che porterà questo sistema fuori dalla sua zona di comfort.

Allora, che tipo di rastrello c'è? Per lo più parlerò di cose ovvie. Tutto è ovvio per tutti, tutti capiscono tutto e possono essere contenti di essere così intelligenti, e chi non capisce imparerà qualcosa di nuovo.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Il primo e più semplice esempio, che purtroppo si verifica spesso, è quello di un gran numero di inserti con piccoli lotti, cioè un gran numero di piccoli inserti.

Se consideriamo come ClickHouse esegue l'inserimento, puoi inviare almeno un terabyte di dati in una richiesta. Non è un problema.

E vediamo quale sarebbe la performance tipica. Ad esempio, abbiamo una tabella dai dati Yandex.Metrica. Colpi. 105 alcune colonne. 700 byte non compressi. E lo inseriremo bene in lotti di un milione di righe.

Inseriamo MergeTree nella tabella, risulta mezzo milione di righe al secondo. Grande. In una tabella replicata sarà leggermente più piccola, circa 400 righe al secondo.

E se abiliti l'inserimento del quorum, ottieni prestazioni leggermente inferiori, ma comunque decenti, 250 termini al secondo. L'inserimento del quorum è una funzionalità non documentata in ClickHouse*.

*a partire dal 2020, già documentato.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Cosa succede se fai qualcosa di brutto? Inseriamo una riga nella tabella MergeTree e otteniamo 59 righe al secondo. È 10 volte più lento. In ReplicatedMergeTree – 000 righe al secondo. E se il quorum è attivato, risultano 6 righe al secondo. Secondo me, questa è una specie di schifezza assoluta. Come puoi rallentare in quel modo? Ho anche scritto sulla mia maglietta che ClickHouse non dovrebbe rallentare. Ma tuttavia a volte succede.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

In effetti, questo è il nostro difetto. Avremmo potuto facilmente far funzionare tutto bene, ma non lo abbiamo fatto. E non l’abbiamo fatto perché la nostra sceneggiatura non lo richiedeva. Avevamo già dei macellai. Abbiamo appena ricevuto i lotti al nostro ingresso e nessun problema. Lo inseriamo e tutto funziona bene. Ma, ovviamente, tutti i tipi di scenari sono possibili. Ad esempio, quando hai un gruppo di server su cui vengono generati i dati. E non inseriscono i dati così spesso, ma finiscono comunque con inserimenti frequenti. E dobbiamo in qualche modo evitarlo.

Da un punto di vista tecnico il punto è che quando fai un inserimento in ClickHouse i dati non finiscono in nessuna memtable. Non abbiamo nemmeno una vera e propria struttura di log MergeTree, ma solo un MergeTree, perché non esiste né un log né una memTable. Scriviamo semplicemente immediatamente i dati nel file system, già organizzati in colonne. E se hai 100 colonne, sarà necessario scrivere più di 200 file in una directory separata. Tutto questo è molto ingombrante.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E sorge la domanda: "Come farlo nel modo giusto?" Se la situazione è tale che è ancora necessario registrare in qualche modo i dati in ClickHouse.

Metodo 1. Questo è il modo più semplice. Utilizzare una sorta di coda distribuita. Ad esempio, Kafka. Basta estrarre i dati da Kafka e raggrupparli una volta al secondo. E andrà tutto bene, registri, tutto funziona bene.

Gli svantaggi sono che Kafka è un altro sistema distribuito ingombrante. Capisco anche se hai già Kafka nella tua azienda. È buono, è conveniente. Ma se non esiste, dovresti pensarci tre volte prima di trascinare un altro sistema distribuito nel tuo progetto. E quindi vale la pena considerare delle alternative.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 2. Questa è un'alternativa vecchia scuola e allo stesso tempo molto semplice. Hai una sorta di server che genera i tuoi log. E scrive semplicemente i tuoi log in un file. E una volta al secondo, ad esempio, rinominiamo questo file e ne strappiamo uno nuovo. E uno script separato, tramite cron o qualche demone, prende il file più vecchio e lo scrive su ClickHouse. Se registri i log una volta al secondo, andrà tutto bene.

Ma lo svantaggio di questo metodo è che se il tuo server su cui vengono generati i log scompare da qualche parte, anche i dati scompariranno.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 3. Esiste un altro metodo interessante, che non richiede alcun file temporaneo. Ad esempio, hai una sorta di filatore pubblicitario o qualche altro demone interessante che genera dati. E puoi accumulare un sacco di dati direttamente nella RAM, nel buffer. E quando è trascorso abbastanza tempo, metti da parte questo buffer, ne crei uno nuovo e in un thread separato inserisci ciò che è già accumulato in ClickHouse.

D'altro canto i dati scompaiono anche con kill -9. Se il tuo server si blocca, perderai questi dati. E un altro problema è che se non riesci a scrivere nel database, i tuoi dati si accumuleranno nella RAM. E o la RAM si esaurirà o perderai semplicemente i dati.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 4. Un altro metodo interessante. Hai qualche tipo di processo server. E può inviare immediatamente i dati a ClickHouse, ma lo fa in un'unica connessione. Ad esempio, ho inviato una richiesta http con codifica di trasferimento: Chunked con insert. E genera blocchi non troppo raramente, puoi inviare ogni riga, anche se ci sarà un sovraccarico per inquadrare questi dati.

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

Ma sorgono anche problemi. Ora perderai dati, anche quando il tuo processo viene terminato e se il processo ClickHouse viene terminato, perché sarà un inserimento incompleto. E in ClickHouse gli inserti sono atomici fino a una certa soglia specificata nella dimensione delle righe. In linea di principio, questo è un modo interessante. Può anche essere utilizzato.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 5. Ecco un altro metodo interessante. Si tratta di una sorta di server sviluppato dalla comunità per l'invio in batch dei dati. Non l'ho guardato personalmente, quindi non posso garantire nulla. Tuttavia, non viene fornita alcuna garanzia per ClickHouse stessa. Anche questo è open source, ma d'altra parte potresti essere abituato ad alcuni standard di qualità che cerchiamo di fornire. Ma per questa cosa, non lo so, vai su GitHub, guarda il codice. Forse hanno scritto qualcosa di normale.

*a partire dal 2020, va aggiunto anche questo KittenHouse.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Metodo 6. Un altro metodo consiste nell'utilizzare le tabelle Buffer. Il vantaggio di questo metodo è che è molto facile iniziare a usarlo. Crea una tabella Buffer e inseriscila al suo interno.

Lo svantaggio è che il problema non è completamente risolto. Se, in una velocità come MergeTree, devi raggruppare i dati in un batch al secondo, in una velocità in una tabella buffer devi raggruppare almeno fino a diverse migliaia al secondo. Se sono più di 10 al secondo, sarà comunque negativo. E se lo inserisci in batch, hai visto che risultano essere centomila righe al secondo. E questo è già su dati abbastanza pesanti.

E anche le tabelle buffer non hanno un registro. E se c'è qualcosa che non va nel tuo server, i dati andranno persi.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E come bonus, recentemente abbiamo avuto l'opportunità su ClickHouse di recuperare dati da Kafka. C'è un motore da tavolo: Kafka. Tu semplicemente crei. E puoi appendervi rappresentazioni materializzate. In questo caso, estrarrà lui stesso i dati da Kafka e li inserirà nelle tabelle che ti servono.

E ciò che è particolarmente piacevole di questa opportunità è che non siamo stati noi a realizzarla. Questa è una funzionalità della comunità. E quando dico “caratteristica della comunità”, lo intendo senza alcun disprezzo. Abbiamo letto il codice, fatto una revisione, dovrebbe funzionare bene.

* a partire dal 2020 è apparso un supporto simile per RabbitMQ.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Cos'altro potrebbe risultare scomodo o inaspettato durante l'inserimento dei dati? Se effettui una richiesta di inserimento di valori e scrivi alcune espressioni calcolate in valori. Ad esempio, anche now() è un'espressione calcolata. E in questo caso, ClickHouse è costretta a lanciare l'interprete di queste espressioni su ogni riga e le prestazioni diminuiranno di ordini di grandezza. È meglio evitarlo.

* al momento il problema è completamente risolto, non c'è più alcuna regressione delle prestazioni quando si utilizzano le espressioni in VALUES.

Un altro esempio è quando potrebbero verificarsi dei problemi quando si hanno dati su un batch che appartiene a un gruppo di partizioni. Per impostazione predefinita, le partizioni ClickHouse sono mensili. E se inserisci un batch di un milione di righe e ci sono dati per diversi anni, lì avrai diverse dozzine di partizioni. E questo equivale al fatto che ci saranno lotti di dimensioni diverse decine di volte più piccole, perché al loro interno vengono sempre prima divisi in partizioni.

* Recentemente, in modalità sperimentale, ClickHouse ha aggiunto il supporto per il formato compatto di blocchi e blocchi nella RAM con registro write-ahead, che risolve quasi completamente il problema.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Consideriamo ora il secondo tipo di problema: la digitazione dei dati.

La digitazione dei dati può essere rigorosa o stringa. String è quando l'hai appena preso e hai dichiarato che tutti i tuoi campi sono di tipo stringa. Questo fa schifo. Non è necessario farlo.

Scopriamo come farlo correttamente in quei casi in cui vuoi dire che abbiamo un campo, una stringa e lasciare che ClickHouse lo capisca da solo, e non mi preoccuperò. Ma vale comunque la pena fare qualche sforzo.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

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

In primo luogo, stranamente, i dati verranno compressi in modo approssimativamente uguale. Ci sarà una differenza, ovviamente, ma non così grande. Quindi non ci sono problemi particolari con l'I/O del disco.

Esiste però una notevole differenza tra il tempo del processore e il tempo di esecuzione della query.

Contiamo il numero di indirizzi IP univoci se sono memorizzati come numeri. Ciò equivale a 137 milioni di linee al secondo. Se lo stesso è sotto forma di stringhe, allora 37 milioni di righe al secondo. Non so perché sia ​​accaduta questa coincidenza. Ho eseguito personalmente queste richieste. Ma ancora circa 4 volte più lento.

E se calcoli la differenza nello spazio su disco, c'è anche una differenza. E la differenza è di circa un quarto, perché ci sono molti indirizzi IP univoci. E se ci fossero righe con un numero limitato di significati diversi, sarebbero facilmente compresse secondo il dizionario approssimativamente nello stesso volume.

E la quadrupla differenza oraria non si trova sulla strada. Forse non te ne frega niente, ovviamente, ma quando vedo una tale differenza, mi rattrista.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Diamo un'occhiata a diversi casi.

1. Un caso in cui hai pochi valori univoci diversi. In questo caso, utilizziamo una pratica semplice che probabilmente conosci e che puoi utilizzare per qualsiasi DBMS. Tutto questo ha senso non solo per ClickHouse. Basta scrivere identificatori numerici nel database. E puoi convertirli in stringhe e viceversa sul lato della tua applicazione.

Ad esempio, hai una regione. E stai provando a salvarlo come una stringa. E lì sarà scritto: Mosca e la Regione di Mosca. E quando vedo che c'è scritto "Mosca", non è niente, ma quando è Mosca, in qualche modo diventa completamente triste. Questo è il numero di byte.

Invece, scriviamo semplicemente il numero Ulnt32 e 250. In Yandex ne abbiamo 250, ma il tuo potrebbe essere diverso. Per ogni evenienza, dirò che ClickHouse ha la capacità integrata di lavorare con una geobase. Scrivi semplicemente una directory con le regioni, inclusa una gerarchica, ad es. ci sarà Mosca, la Regione di Mosca e tutto ciò di cui hai bisogno. E puoi convertire a livello di richiesta.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

La seconda opzione è più o meno la stessa, ma con il supporto all'interno di ClickHouse. Questo è il tipo di dati Enum. Scrivi semplicemente tutti i valori che ti servono all'interno di Enum. Ad esempio, digita il tipo di dispositivo e scrivi lì: desktop, mobile, tablet, TV. Ci sono 4 opzioni in totale.

Lo svantaggio è che è necessario cambiarlo periodicamente. È stata aggiunta solo un'opzione. Facciamo alterare la tabella. In effetti, la tabella di modifica in ClickHouse è gratuita. Particolarmente gratuito per Enum perché i dati su disco non cambiano. Tuttavia, alter acquisisce un lock* sulla tabella e deve attendere finché tutte le selezioni non vengono eseguite. E solo dopo questa modifica verrà eseguita, cioè ci saranno ancora alcuni inconvenienti.

*nelle ultime versioni di ClickHouse, ALTER è reso completamente non bloccante.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un'altra opzione piuttosto unica per ClickHouse è il collegamento di dizionari esterni. Puoi scrivere numeri in ClickHouse e conservare le tue directory in qualsiasi sistema conveniente per te. Ad esempio, puoi utilizzare: MySQL, Mongo, Postgres. Puoi persino creare il tuo microservizio che invierà questi dati tramite http. E a livello ClickHouse, scrivi una funzione che convertirà questi dati da numeri a stringhe.

Questo è un modo specializzato ma molto efficiente per eseguire un join su una tabella esterna. E ci sono due opzioni. In una forma di realizzazione, questi dati saranno completamente memorizzati nella cache, completamente presenti nella RAM e aggiornati con una certa frequenza. E in un'altra opzione, se questi dati non rientrano nella RAM, puoi memorizzarli parzialmente nella cache.

Ecco un esempio. C'è Yandex.Direct. E c'è una società pubblicitaria e banner. Probabilmente ci sono circa decine di milioni di società pubblicitarie. E si adattano più o meno alla RAM. E ci sono miliardi di banner, non ci stanno. E utilizziamo un dizionario memorizzato nella cache di MySQL.

L'unico problema è che il dizionario memorizzato nella cache funzionerà correttamente se il tasso di successo è vicino al 100%. Se è più piccolo, durante l'elaborazione delle query per ciascun batch di dati, dovrai effettivamente prendere le chiavi mancanti e andare a recuperare i dati da MySQL. Per quanto riguarda ClickHouse, posso comunque garantirlo: sì, non rallenta, non parlerò di altri sistemi.

E come bonus, i dizionari rappresentano un modo molto semplice per aggiornare retroattivamente i dati in ClickHouse. Cioè, avevi un rapporto sulle società pubblicitarie, l'utente ha semplicemente cambiato la società pubblicitaria e in tutti i vecchi dati, in tutti i rapporti, anche questi dati sono cambiati. Se scrivi le righe direttamente nella tabella, sarà impossibile aggiornarle.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro modo quando non sai dove trovare gli identificatori per le tue stringhe. puoi semplicemente cancellarlo. Inoltre, l'opzione più semplice è acquisire un hash a 64 bit.

L'unico problema è che se l'hash è a 64 bit, quasi sicuramente si verificheranno delle collisioni. Perché se ci sono un miliardo di linee lì, la probabilità diventa già evidente.

E non sarebbe molto utile infangare in questo modo i nomi delle società pubblicitarie. Se si confondono le campagne pubblicitarie di diverse aziende, risulterà qualcosa di incomprensibile.

E c'è un semplice trucco. È vero, anche per dati seri non è molto adatto, ma se qualcosa non è molto serio, aggiungi semplicemente l'identificatore del client alla chiave del dizionario. E poi avrai delle collisioni, ma solo all'interno di un cliente. E utilizziamo questo metodo per le mappe dei collegamenti in Yandex.Metrica. Abbiamo gli URL lì, memorizziamo gli hash. E sappiamo che, ovviamente, ci sono collisioni. Ma quando la pagina viene visualizzata, si può trascurare la probabilità che su una pagina di un utente alcuni URL siano attaccati insieme e questo venga notato.

Come bonus, per molte operazioni gli hash sono sufficienti e le stringhe stesse non devono essere archiviate da nessuna parte.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro esempio è se le stringhe sono brevi, ad esempio i domini dei siti Web. Possono essere conservati così come sono. Oppure, ad esempio, la lingua del browser ru è di 2 byte. Certo, mi dispiace davvero per i byte, ma non preoccuparti, 2 byte non sono un peccato. Per favore, tienilo così com'è, non preoccuparti.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro caso è quando, al contrario, ci sono molte linee e ce ne sono molte uniche in esse, e anche il set è potenzialmente illimitato. Un tipico esempio sono le frasi di ricerca o gli URL. Frasi di ricerca, inclusi errori di battitura. Vediamo quante frasi di ricerca uniche ci sono al giorno. E si scopre che sono quasi la metà di tutti gli eventi. E in questo caso, potresti pensare di dover normalizzare i dati, contare gli identificatori e inserirli in una tabella separata. Ma non è necessario farlo. Mantieni semplicemente queste righe così come sono.

È meglio non inventare nulla, perché se lo memorizzi separatamente dovrai fare un join. E questa unione è, nella migliore delle ipotesi, un accesso casuale alla memoria, se ci sta ancora nella memoria. Se non si adatta, allora ci saranno problemi.

E se i dati vengono archiviati sul posto, vengono semplicemente letti nell'ordine corretto dal file system e tutto va bene.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Se disponi di URL o di altre stringhe lunghe e complesse, vale la pena considerare che puoi calcolare in anticipo una sorta di estratto e scriverlo in una colonna separata.

Per gli URL, ad esempio, puoi memorizzare il dominio separatamente. E se hai davvero bisogno di un dominio, usa semplicemente questa colonna e gli URL si troveranno lì e non li toccherai nemmeno.

Vediamo qual è la differenza. ClickHouse ha una funzione specializzata che calcola il dominio. È molto veloce, lo abbiamo ottimizzato. E, a dire il vero, non è nemmeno conforme alla RFC, ma considera comunque tutto ciò di cui abbiamo bisogno.

E in un caso otterremo semplicemente gli URL e calcoleremo il dominio. Ciò equivale a 166 millisecondi. E se prendi un dominio già pronto, risulta essere solo 67 millisecondi, ovvero quasi tre volte più veloce. Ed è più veloce non perché dobbiamo fare qualche calcolo, ma perché leggiamo meno dati.

Ecco perché una richiesta, che è più lenta, ha una velocità maggiore di gigabyte al secondo. Perché legge più gigabyte. Si tratta di dati completamente inutili. La richiesta sembra essere eseguita più velocemente, ma richiede più tempo per essere completata.

E se guardi la quantità di dati sul disco, risulta che l'URL è di 126 megabyte e il dominio è di soli 5 megabyte. Risulta 25 volte meno. Tuttavia, la richiesta viene eseguita solo 4 volte più velocemente. Ma è perché i dati sono scottanti. E se facesse freddo, sarebbe probabilmente 25 volte più veloce grazie all'I/O del disco.

A proposito, se calcoli quanto è più piccolo un dominio rispetto a un URL, risulta essere circa 4 volte più piccolo, ma per qualche motivo i dati occupano 25 volte meno sul disco. Perché? A causa della compressione. E l'URL è compresso e il dominio è compresso. Ma spesso l'URL contiene un mucchio di spazzatura.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E, naturalmente, vale la pena utilizzare i tipi di dati giusti, progettati specificamente per i valori desiderati o adatti. Se utilizzi IPv4, archivia UInt32*. Se IPv6, allora FixedString(16), perché l'indirizzo IPv6 è a 128 bit, ovvero memorizzato direttamente in formato binario.

Ma cosa succede se a volte hai indirizzi IPv4 e talvolta IPv6? Sì, puoi memorizzarli entrambi. Una colonna per IPv4, un'altra per IPv6. Naturalmente c'è un'opzione per visualizzare IPv4 in IPv6. Funzionerà anche questo, ma se hai spesso bisogno di un indirizzo IPv4 nelle richieste, sarebbe carino inserirlo in una colonna separata.

* ClickHouse ora dispone di tipi di dati IPv4 e IPv6 separati che archiviano i dati con la stessa efficienza dei numeri, ma li rappresentano con la stessa comodità delle stringhe.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

È anche importante notare che vale la pena preelaborare i dati in anticipo. Ad esempio, ricevi alcuni log grezzi. E forse non dovresti semplicemente inserirli subito in ClickHouse, anche se la tentazione di non fare nulla è molto forte e tutto funzionerà. Ma vale comunque la pena di fare i calcoli possibili.

Ad esempio, la versione del browser. In qualche dipartimento vicino, su cui non voglio puntare il dito, la versione del browser è memorizzata così, cioè come una stringa: 12.3. E poi, per fare un rapporto, prendono questa stringa e la dividono in un array, e poi nel primo elemento dell'array. Naturalmente tutto rallenta. Ho chiesto perché lo fanno. Mi hanno detto che a loro non piace l’ottimizzazione prematura. E non mi piace la pessimizzazione prematura.

Quindi in questo caso sarebbe più corretto dividere in 4 colonne. Non aver paura qui, perché questa è ClickHouse. ClickHouse è un database a colonne. E più le colonnette sono ordinate, meglio è. Ci saranno 5 versioni del browser, crea 5 colonne. Questo va bene.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ora vediamo cosa fare se hai molte stringhe molto lunghe e array molto lunghi. Non è necessario che siano archiviati in ClickHouse. Invece, puoi solo memorizzare un identificatore in ClickHouse. E inserisci queste lunghe file in qualche altro sistema.

Ad esempio, uno dei nostri servizi di analisi presenta alcuni parametri di eventi. E se ci sono molti parametri per gli eventi, salviamo semplicemente i primi 512 che incontriamo, perché 512 non è un peccato.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E se non puoi decidere il tipo di dati, puoi anche registrare i dati in ClickHouse, ma in una tabella temporanea del tipo Log, speciale per i dati temporanei. Successivamente, puoi analizzare quale distribuzione di valori hai lì, cosa c'è in generale e creare i tipi corretti.

*ClickHouse ora ha un tipo di dati Cardinalità bassa che consente di archiviare le stringhe in modo efficiente con meno sforzo.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Consideriamo ora un altro caso interessante. A volte le cose funzionano in modo strano per le persone. Entro e vedo questo. E sembra immediatamente che ciò sia stato fatto da un amministratore molto esperto e intelligente che ha una vasta esperienza nella configurazione di MySQL versione 3.23.

Qui vediamo mille tabelle, ciascuna delle quali registra il resto della divisione dividendo chissà cosa per mille.

In linea di principio rispetto l’esperienza degli altri, compresa la comprensione della sofferenza che può derivare da questa esperienza.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

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

Un altro motivo è che è difficile eseguire operazioni di modifica su tabelle di grandi dimensioni. Tutto sarà bloccato. Sebbene nelle versioni moderne di MySQL questo problema non sia più così grave.

O, ad esempio, il microsharding, ma ne parleremo più avanti.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Non è necessario farlo in ClickHouse, perché, in primo luogo, la chiave primaria è raggruppata, i dati sono ordinati in base alla chiave primaria.

E a volte le persone mi chiedono: "Come variano le prestazioni delle query di intervallo in ClickHouse a seconda della dimensione della tabella?" Io dico che non cambia affatto. Ad esempio, hai una tabella con un miliardo di righe e leggi un intervallo di un milione di righe. Va tutto bene. Se ci sono trilioni di righe in una tabella e ne leggi un milione, sarà quasi la stessa cosa.

E, in secondo luogo, non sono necessarie tutte le cose come le partizioni manuali. Se entri e guardi cosa c'è nel file system, vedrai che la tabella è un grosso problema. E all'interno c'è qualcosa come le partizioni. Cioè, ClickHouse fa tutto per te e non devi soffrire.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

La modifica in ClickHouse è gratuita se si modifica la colonna aggiungi/elimina.

E non dovresti creare tabelle piccole, perché se hai 10 o 10 righe in una tabella, non ha alcuna importanza. ClickHouse è un sistema che ottimizza il throughput, non la latenza, quindi non ha senso elaborare 000 righe.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

È corretto utilizzare una tabella di grandi dimensioni. Sbarazzati dei vecchi stereotipi, andrà tutto bene.

E come bonus, nell'ultima versione ora abbiamo la possibilità di creare una chiave di partizionamento arbitraria per eseguire tutti i tipi di operazioni di manutenzione sulle singole partizioni.

Ad esempio, hai bisogno di molte piccole tabelle, ad esempio, quando è necessario elaborare alcuni dati intermedi, ricevi blocchi ed è necessario eseguire una trasformazione su di essi prima di scrivere nella tabella finale. In questo caso esiste un meraviglioso motore di tabelle: StripeLog. È un po' come TinyLog, solo meglio.

* ora ha anche ClickHouse input della funzione tabella.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro antipattern è il microsharding. Ad esempio, devi partizionare i dati e hai 5 server e domani ci saranno 6 server. E pensi a come riequilibrare questi dati. E invece non ti spezzi in 5 frammenti, ma in 1 frammenti. E poi mappi ciascuno di questi microshard su un server separato. E otterrai, ad esempio, 000 ClickHouse su un server. Istanze separate su porte separate o database separati.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ma questo non va molto bene in ClickHouse. Perché anche un'istanza di ClickHouse tenta di utilizzare tutte le risorse del server disponibili per elaborare una richiesta. Cioè, hai una sorta di server e ha, ad esempio, 56 core del processore. Stai eseguendo una query che richiede un secondo e utilizzerà 56 core. E se hai posizionato 200 ClickHouse su un server, si scopre che verranno avviati 10 thread. In generale, tutto andrà molto male.

Un altro motivo è che la distribuzione del lavoro tra queste istanze non sarà uniforme. Alcuni finiranno prima, altri finiranno più tardi. Se tutto ciò accadesse in un'unica istanza, ClickHouse stessa capirebbe come distribuire correttamente i dati tra i thread.

E un altro motivo è che avrai la comunicazione tra processori tramite TCP. I dati dovranno essere serializzati, deserializzati e si tratta di un numero enorme di microshard. Semplicemente non funzionerà in modo efficace.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro antipattern, anche se difficilmente può essere definito un antipattern. Si tratta di una grande quantità di pre-aggregazione.

In generale, la preaggregazione è buona. Avevi un miliardo di righe, le hai aggregate e sono diventate 1 righe e ora la query viene eseguita immediatamente. È tutto bellissimo. Puoi farlo. E per questo, anche ClickHouse dispone di un tipo di tabella speciale, AggregatingMergeTree, che esegue l'aggregazione incrementale man mano che i dati vengono inseriti.

Ma ci sono momenti in cui pensi che aggregheremo dati come questi e aggregheremo dati come questi. E in qualche dipartimento vicino, non voglio nemmeno dire quale, usano le tabelle SummingMergeTree per riepilogare in base alla chiave primaria e circa 20 colonne vengono utilizzate come chiave primaria. Per ogni evenienza, ho cambiato i nomi di alcune colonne per motivi di segretezza, ma praticamente è tutto.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E sorgono tali problemi. Innanzitutto, il volume dei tuoi dati non diminuisce troppo. Ad esempio, diminuisce di tre volte. Tre volte sarebbe un buon prezzo per permettersi le capacità di analisi illimitate che si ottengono se i dati non vengono aggregati. Se i dati sono aggregati, invece di analisi si ottengono solo statistiche pietose.

E cosa c'è di così speciale? Il fatto è che queste persone del dipartimento vicino a volte vanno e chiedono di aggiungere un'altra colonna alla chiave primaria. Cioè, abbiamo aggregato i dati in questo modo, ma ora ne vogliamo un po' di più. Ma ClickHouse non ha una chiave primaria alternativa. Pertanto, dobbiamo scrivere alcuni script in C++. E non mi piacciono gli script, anche se sono in C++.

E se guardi per cosa è stato creato ClickHouse, allora i dati non aggregati sono esattamente lo scenario per cui è nato. Se utilizzi ClickHouse per dati non aggregati, lo stai facendo bene. Se si aggrega, questo a volte è perdonabile.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Un altro caso interessante sono le query in un ciclo infinito. A volte vado su qualche server di produzione e guardo show processlist lì. E ogni volta scopro che sta succedendo qualcosa di terribile.

Ad esempio, così. È subito chiaro che tutto potrebbe essere fatto in una sola richiesta. Basta scrivere l'URL e l'elenco lì.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Perché molte di queste query in un ciclo infinito sono dannose? Se non viene utilizzato un indice, si avranno molti passaggi sugli stessi dati. Ma se viene utilizzato l'indice, ad esempio, hai una chiave primaria per ru e scrivi url = qualcosa lì. E pensi che se viene letto un solo URL dalla tabella, tutto andrà bene. Ma in realtà no. Perché ClickHouse fa tutto in batch.

Quando ha bisogno di leggere una certa serie di dati, legge un po' di più, perché l'indice in ClickHouse è scarso. Questo indice non consente di trovare una singola riga nella tabella, ma solo un intervallo di qualche tipo. E i dati sono compressi in blocchi. Per leggere una riga, devi prendere l'intero blocco e aprirlo. E se stai facendo un sacco di query, ci saranno molte sovrapposizioni e avrai molto lavoro da fare ancora e ancora.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E come bonus, puoi notare che in ClickHouse non dovresti aver paura di trasferire anche megabyte e persino centinaia di megabyte nella sezione IN. Ricordo dalla nostra pratica che se in MySQL trasferiamo un gruppo di valori nella sezione IN, ad esempio, trasferiamo lì 100 megabyte di alcuni numeri, allora MySQL consuma 10 gigabyte di memoria e non gli succede nient'altro, tutto funziona male.

E il secondo è che in ClickHouse, se le tue query utilizzano un indice, non è sempre più lento di una scansione completa, ad es. se devi leggere quasi l'intera tabella, andrà in sequenza e leggerà l'intera tabella. In generale, lo capirà da solo.

Tuttavia ci sono alcune difficoltà. Ad esempio, il fatto che IN con una sottoquery non utilizzi l'indice. Ma questo è il nostro problema e dobbiamo risolverlo. Non c'è nulla di fondamentale qui. Lo sistemeremo*.

E un'altra cosa interessante è che se hai una richiesta molto lunga ed è in corso l'elaborazione della richiesta distribuita, questa richiesta molto lunga verrà inviata a ciascun server senza compressione. Ad esempio, 100 megabyte e 500 server. E, di conseguenza, avrai 50 gigabyte trasferiti sulla rete. Verrà trasmesso e poi tutto sarà completato con successo.

* già in uso; Tutto è stato risolto come promesso.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

E un caso abbastanza comune è quando le richieste provengono dall'API. Ad esempio, hai creato una sorta di servizio. E se qualcuno ha bisogno del tuo servizio, apri l'API e letteralmente due giorni dopo vedi che sta accadendo qualcosa di incomprensibile. Tutto è sovraccarico e stanno arrivando richieste terribili che non sarebbero mai dovute accadere.

E c'è solo una soluzione. Se hai aperto l'API, dovrai tagliarla. Ad esempio, introdurre una sorta di quote. Non ci sono altre opzioni normali. Altrimenti scriveranno immediatamente una sceneggiatura e ci saranno problemi.

E ClickHouse ha una caratteristica speciale: il calcolo della quota. Inoltre, puoi trasferire la tua chiave di quota. Questo è, ad esempio, l'ID utente interno. E le quote verranno calcolate indipendentemente per ciascuno di essi.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ora un'altra cosa interessante. Questa è la replica manuale.

Conosco molti casi in cui, nonostante ClickHouse abbia il supporto di replica integrato, le persone replicano ClickHouse manualmente.

Qual è il principio? Hai una pipeline di elaborazione dati. E funziona in modo indipendente, ad esempio, in diversi data center. Scrivi gli stessi dati nello stesso modo in ClickHouse. È vero, la pratica dimostra che i dati continueranno a divergere a causa di alcune funzionalità del codice. Spero che sia nel tuo.

E di tanto in tanto dovrai comunque sincronizzare manualmente. Ad esempio, una volta al mese gli amministratori eseguono la sincronizzazione.

In effetti, è molto più semplice utilizzare la replica integrata in ClickHouse. Ma potrebbero esserci alcune controindicazioni, perché per questo è necessario utilizzare ZooKeeper. Non dirò niente di negativo su ZooKeeper, in linea di principio il sistema funziona, ma capita che le persone non lo usino a causa della javafobia, perché ClickHouse è un ottimo sistema, scritto in C++, che puoi usare e andrà tutto bene. E ZooKeeper è in Java. E in qualche modo non vuoi nemmeno guardare, ma puoi utilizzare la replica manuale.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

ClickHouse è un sistema pratico. Tiene conto delle tue esigenze. Se disponi di una replica manuale, puoi creare una tabella distribuita che esamini le tue repliche manuali ed esegua un failover tra di esse. E c'è anche un'opzione speciale che ti permette di evitare i flop, anche se le tue linee divergono sistematicamente.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ulteriori problemi potrebbero sorgere se si utilizzano motori di tabella primitivi. ClickHouse è un costruttore che ha un sacco di diversi motori di tabelle. Per tutti i casi gravi, come scritto nella documentazione, utilizzare le tabelle della famiglia MergeTree. E tutto il resto è così, per casi singoli o per test.

In una tabella MergeTree non è necessario avere data e ora. Puoi ancora usarlo. Se non sono presenti data e ora, scrivi che il valore predefinito è 2000. Funzionerà e non richiederà risorse.

E nella nuova versione del server puoi anche specificare di avere un partizionamento personalizzato senza una chiave di partizione. Sarà lo stesso.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

D'altra parte, puoi utilizzare motori di tabella primitivi. Ad esempio, inserisci i dati una volta e guarda, ruota e cancella. Puoi usare Log.

Oppure archiviare piccoli volumi per l'elaborazione intermedia è StripeLog o TinyLog.

La memoria può essere utilizzata se la quantità di dati è piccola e puoi semplicemente giocherellare con qualcosa nella RAM.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

A ClickHouse non piacciono molto i dati rinormalizzati.

Ecco un tipico esempio. Si tratta di un numero enorme di URL. Li metti nella tabella successiva. E poi hanno deciso di eseguire JOIN con loro, ma di regola non funzionerà, perché ClickHouse supporta solo Hash JOIN. Se non c'è abbastanza RAM per molti dati da connettere, JOIN non funzionerà*.

Se i dati hanno cardinalità elevata, non preoccuparti, archiviali in una forma denormalizzata, gli URL sono direttamente nella tabella principale.

* e ora ClickHouse ha anche un merge join e funziona in condizioni in cui i dati intermedi non rientrano nella RAM. Ma ciò è inefficace e la raccomandazione resta in vigore.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ancora un paio di esempi, ma dubito già che siano anti-pattern o meno.

ClickHouse ha un difetto noto. Non sa come aggiornare*. In un certo senso, questo è anche buono. Se disponi di dati importanti, ad esempio la contabilità, nessuno potrà inviarli, perché non ci sono aggiornamenti.

* il supporto per l'aggiornamento e l'eliminazione in modalità batch è stato aggiunto molto tempo fa.

Ma ci sono alcuni modi speciali che consentono gli aggiornamenti come in background. Ad esempio, tabelle come replaceMergeTree. Effettuano aggiornamenti durante le fusioni in background. Puoi forzarlo utilizzando la tabella di ottimizzazione. Ma non farlo troppo spesso, perché sovrascriverà completamente la partizione.

Anche i JOIN distribuiti in ClickHouse sono mal gestiti dal query planner.

Cattivo, ma a volte ok.

Utilizzare ClickHouse solo per rileggere i dati utilizzando select*.

Non consiglierei di utilizzare ClickHouse per calcoli complicati. Ma questo non è del tutto vero, perché ci stiamo già allontanando da questa raccomandazione. E recentemente abbiamo aggiunto la possibilità di applicare modelli di machine learning in ClickHouse - Catboost. E mi dà fastidio perché penso: “Che orrore. Questo è il numero di cicli per byte che risulta! Odio davvero sprecare orologi in byte.

Utilizzo efficace di ClickHouse. Alexey Milovidov (Yandex)

Ma non aver paura, installa ClickHouse e andrà tutto bene. Se non altro, abbiamo una comunità. A proposito, la comunità sei tu. E se hai qualche problema, puoi almeno andare nella nostra chat, e speriamo che ti aiutino.

domande

Grazie per la segnalazione! Dove posso lamentarmi del crash di ClickHouse?

Puoi lamentarti con me personalmente adesso.

Recentemente ho iniziato a utilizzare ClickHouse. Ho immediatamente abbandonato l'interfaccia CLI.

Sei fortunato.

Poco dopo ho bloccato il server con una piccola selezione.

Hai talento.

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

Vedremo.

Alexey mi ha convinto con l'inganno a partecipare al rapporto, promettendomi di dirmi come si accede ai dati all'interno.

Molto semplice.

Me ne sono reso conto ieri. Più specifici.

Non ci sono trucchi terribili lì. C'è solo la compressione blocco per blocco. L'impostazione predefinita è LZ4, puoi abilitare ZSTD*. Blocchi da 64 kilobyte a 1 megabyte.

* è disponibile anche il supporto per codec di compressione specializzati che possono essere utilizzati in catena con altri algoritmi.

I blocchi sono solo dati grezzi?

Non completamente crudo. Ci sono array. Se hai una colonna numerica, i numeri in una riga vengono inseriti in un array.

Capisco.

Alexey, un esempio relativo a uniqExact over IPs, ovvero il fatto che uniqExact impiega più tempo a calcolare in base alle righe che in base ai numeri, e così via. Cosa succede se usiamo una finta con le orecchie e lanciamo al momento della correzione di bozze? Cioè, sembra che tu abbia detto che sul nostro disco non è molto diverso. Se leggiamo le righe dal disco e le trasmettiamo, i nostri aggregati saranno più veloci o no? O guadagneremo ancora leggermente qui? Mi sembra che tu lo abbia testato, ma per qualche motivo non lo hai indicato nel benchmark.

Penso che sarà più lento che senza casting. In questo caso, l'indirizzo IP deve essere analizzato dalla stringa. Naturalmente, su ClickHouse, anche l'analisi del nostro indirizzo IP è ottimizzata. Ci abbiamo provato molto, ma ecco i numeri scritti nella forma del diecimillesimo. Molto scomodo. D'altra parte, la funzione uniqExact lavorerà più lentamente sulle stringhe, non solo perché si tratta di stringhe, ma anche perché è selezionata una diversa specializzazione dell'algoritmo. Le stringhe vengono semplicemente elaborate in modo diverso.

Cosa succede se prendiamo un tipo di dati più primitivo? Ad esempio, abbiamo annotato l'ID utente che abbiamo, lo abbiamo scritto come una riga e poi lo abbiamo codificato, sarà più divertente o no?

Dubito. Penso che sarà ancora più triste, perché dopo tutto, l'analisi dei numeri è un problema serio. Mi sembra che questo collega abbia anche riferito su quanto sia difficile analizzare i numeri nella forma decimillesimale, ma forse no.

Alexey, grazie mille per la segnalazione! E grazie mille per ClickHouse! Ho una domanda sui piani. È prevista una funzionalità per aggiornare i dizionari in modo incompleto?

Cioè un riavvio parziale?

Si si. Come la possibilità di impostare lì un campo MySQL, ovvero aggiornarlo dopo, in modo che vengano caricati solo questi dati se il dizionario è molto grande.

Una caratteristica molto interessante. E penso che qualcuno lo abbia suggerito nella nostra chat. Forse eri anche tu.

Non credo.

Ottimo, ora risulta che le richieste sono due. E puoi iniziare lentamente a farlo. Ma voglio avvertirti subito che questa funzionalità è abbastanza semplice da implementare. Cioè, in teoria, devi solo scrivere il numero di versione nella tabella e poi scrivere: versione inferiore a così e così. Ciò significa che, molto probabilmente, offriremo questo agli appassionati. Sei un appassionato?

Sì, ma sfortunatamente non in C++.

I tuoi colleghi sanno scrivere in C++?

Troverò qualcuno.

Grande*.

* la funzionalità è stata aggiunta due mesi dopo il rapporto: l'autore della domanda l'ha sviluppata e ha inviato la sua richiesta di pull.

Grazie!

Ciao! Grazie per la segnalazione! Hai detto che ClickHouse è molto bravo a consumare tutte le risorse a sua disposizione. E l'oratore accanto a Luxoft ha parlato della sua soluzione per Russian Post. Ha detto che a loro piaceva molto ClickHouse, ma non lo usavano al posto del loro principale concorrente proprio perché consumava tutta la CPU. E non potevano collegarlo alla loro architettura, al loro ZooKeeper con finestre mobili. È possibile limitare in qualche modo ClickHouse in modo che non consumi tutto ciò che gli diventa disponibile?

Sì, è possibile e molto facile. Se vuoi consumare meno core, scrivi semplicemente set max_threads = 1. E questo è tutto, eseguirà la richiesta in un core. Inoltre, puoi specificare impostazioni diverse per utenti diversi. Quindi nessun problema. E dì ai tuoi colleghi di Luxoft che non è bene che non abbiano trovato questa impostazione nella documentazione.

Alessio, ciao! Vorrei porre questa domanda. Non è la prima volta che sento che molte persone stanno iniziando a utilizzare ClickHouse come archivio per i log. Nel rapporto hai detto di non farlo, cioè non è necessario memorizzare stringhe lunghe. Cosa ne pensi?

Innanzitutto, i log non sono, di regola, stringhe lunghe. Ci sono, ovviamente, delle eccezioni. Ad esempio, alcuni servizi scritti in Java lanciano un'eccezione e vengono registrati. E così via in un ciclo infinito e lo spazio sul disco rigido si esaurisce. La soluzione è molto semplice. Se le linee sono molto lunghe, tagliale. Cosa significa lungo? Decine di kilobyte sono dannosi*.

* nelle ultime versioni di ClickHouse è abilitata la “granularità dell'indice adattivo”, che elimina la maggior parte del problema della memorizzazione di righe lunghe.

Un kilobyte è normale?

È normale

Ciao! Grazie per la segnalazione! Ho già chiesto informazioni in chat, ma non ricordo se ho ricevuto risposta. Ci sono piani per espandere in qualche modo la sezione WITH alla maniera di CTE?

Non ancora. La nostra sezione WITH è alquanto frivola. È come una piccola caratteristica per noi.

Capisco. Grazie!

Grazie per la segnalazione! Molto interessante! Domanda globale. Esistono piani per modificare la cancellazione dei dati, magari sotto forma di una sorta di stub?

Necessariamente. Questo è il nostro primo compito nella nostra coda. Ora stiamo pensando attivamente a come fare tutto correttamente. E dovresti iniziare a premere la tastiera*.

*premevo i tasti della tastiera e facevo tutto.

Ciò influirà in qualche modo sulle prestazioni del sistema o no? L'inserimento sarà veloce come lo è adesso?

Forse le eliminazioni stesse e gli aggiornamenti stessi saranno molto pesanti, ma ciò non influirà sulle prestazioni delle selezioni o sulle prestazioni degli inserimenti.

E un'altra piccola domanda. Alla presentazione hai parlato di chiave primaria. Di conseguenza, abbiamo il partizionamento, che è mensile per impostazione predefinita, corretto? E quando impostiamo un intervallo di date che rientra in un mese, viene letta solo questa partizione, giusto?

Sì.

Una domanda. Se non possiamo selezionare alcuna chiave primaria, allora è corretto farlo specificatamente in base al campo “Data” in modo che sullo sfondo ci sia meno riorganizzazione di questi dati in modo che si adattino in modo più ordinato? Se non disponi di query di intervallo e non puoi nemmeno selezionare alcuna chiave primaria, vale la pena inserire una data nella chiave primaria?

Sì.

Forse ha senso inserire un campo nella chiave primaria che comprimerà meglio i dati se vengono ordinati in base a questo campo. Ad esempio, ID utente. L'utente, ad esempio, visita lo stesso sito. In questo caso, inserisci l'ID utente e l'ora. E poi i tuoi dati saranno compressi meglio. Per quanto riguarda la data, se davvero non hai e non hai mai query di intervallo sulle date, non devi inserire la data nella chiave primaria.

Ok grazie mille!

Fonte: habr.com

Aggiungi un commento