ClickHouse per utenti avanzati in domande e risposte

Ad aprile, gli ingegneri di Avito si sono riuniti per un incontro online con il chief developer di ClickHouse, Alexey Milovidov, e Kirill Shvakov, sviluppatore Golang della compagnia Integros. Hanno discusso di come utilizziamo il sistema di gestione dei database e quali siano le difficoltà che incontriamo.

Sulla scia dell'incontro, abbiamo raccolto un articolo con le risposte degli esperti alle nostre domande e a quelle del pubblico su backup, resharding dei dati, dizionari esterni, driver Golang e aggiornamento delle versioni di ClickHouse. Può essere utile per gli sviluppatori che già lavorano attivamente con il DBMS di Yandex e sono interessati al suo presente e futuro. Di default, le risposte sono di Alexey Milovidov, salvo indicazioni contrarie.

Attenzione, il contenuto sotto è molto lungo. Speriamo che le domande vi aiutino a orientarvi.

ClickHouse per utenti avanzati in domande e risposte

Contenuto

Se non si desidera leggere il testo, è possibile guardare la registrazione della riunione sul nostro canale YouTube. I timestamp sono nel primo commento sotto al video.

ClickHouse si aggiorna continuamente, mentre i nostri dati no. Cosa possiamo fare?

ClickHouse si aggiorna continuamente, ma i nostri dati, che sono stati elaborati con optimize final, non si aggiornano e rimangono nel backup.

Supponiamo che ci sia stata qualche problema e che i dati siano stati persi. Abbiamo deciso di ripristinarci ma ci siamo resi conto che le vecchie partizioni, che sono conservate nei server di backup, divergono notevolmente dalla versione di ClickHouse attualmente in uso. Cosa fare in questa situazione, è possibile?

È impossibile che la situazione in cui si riprendano dati da un backup in un formato obsoleto, mentre nella nuova versione non si connettano. Ci assicuriamo che il formato dei dati in ClickHouse sia sempre retrocompatibile. Questo è molto più importante della retrocompatibilità nelle funzionalità, se il comportamento di una funzione poco utilizzata è cambiato. I dati archiviati su disco devono sempre poter essere letti dalla nuova versione di ClickHouse. Questa è una regola.

Quali sono le migliori pratiche attuali per il backup dei dati da ClickHouse?

Come fare backup tenendo conto che abbiamo operazioni di optimize final, un vasto database di terabyte e dati che vengono aggiornati, ad esempio, solo negli ultimi tre giorni, senza ulteriori procedure su di essi?

Possiamo costruire una nostra soluzione e sulla dash scrivere: raccogli questi backup in questo modo. Forse non serve inventare nulla e la bicicletta è già stata inventata?

Per cominciare sulle migliori pratiche. I miei colleghi consigliano sempre in risposta a domande sui backup di ricordare il servizio "Yandex.Cloud", dove questo compito è già risolto. Quindi, utilizzatelo se avete questa possibilità.

Non esiste una soluzione completa, integrata al cento per cento in ClickHouse, per i backup. Ci sono alcuni modelli che possono essere utilizzati. Per ottenere una soluzione completa, sarà necessario lavorare manualmente un po' oppure creare wrapper sotto forma di script.

Inizierò dalle soluzioni più semplici e finirò con quelle più complesse a seconda del volume dei dati e delle dimensioni del cluster. Più grande è il cluster, più complicata diventa la soluzione.

Se la tabella con i dati occupa solo pochi gigabyte, il backup può essere effettuato in questo modo:

  1. Salvare la definizione delle tabelle, ovvero i metadati — show create table.
  2. Fare un dump utilizzando il client di ClickHouse — select * from table in un file. Per impostazione predefinita, otterrete un file in formato TabSeparated. Se desiderate qualcosa di più efficiente, potete utilizzare il formato Native.

Se il volume dei dati è maggiore, il backup richiederà più tempo e molto spazio. Questo è chiamato backup logico, che non è legato al formato dati di ClickHouse. Se è disponibile, in caso estremo, potrete prendere il backup e caricarlo in MySQL per il ripristino.

Per casi più avanzati, ClickHouse ha integrato la possibilità di creare uno snapshot delle partizioni nel filesystem locale. Questa possibilità è disponibile sotto forma di query alter table freeze partition. Oppure semplicemente alter table freeze — questo è uno snapshot dell'intera tabella.

Lo snapshot sarà creato in modo coerente per una sola tabella su un singolo shard, cioè non è possibile creare uno snapshot coerente di tutto il cluster in questo modo. Ma per la maggior parte delle attività tale necessità non sussiste, e basta eseguire la query su ciascun shard per ottenere uno snapshot coerente. Viene creato sotto forma di hard links e quindi non occupa spazio aggiuntivo. Successivamente, questo snapshot lo copiate sul server di backup o nell'archivio che usate per i backup.

Ripristinare un tale backup è piuttosto semplice. Prima di tutto, create le tabelle secondo le definizioni già disponibili. Poi copiate gli snapshot salvati delle partizioni nella Directory-Detached per le tabelle dati e eseguite la query attach partition. Questa soluzione è perfettamente adatta per volumi di dati molto seri.

A volte è necessario qualcosa di ancora più potente: nei casi in cui si hanno decine o addirittura centinaia di terabyte su ogni server e centinaia di server. Qui c'è una soluzione che ho visto dai colleghi di «Yandex.Metrica». Non la consiglierei a tutti: leggete e decidete se fa per voi o meno.

Inizialmente, è necessario creare diversi server con ampie scorte di dischi. Successivamente, su questi server, è possibile installare diversi server ClickHouse e configurarli in modo che funzionino come un'ulteriore replica per i medesimi shard. Dopodiché, utilizzare su questi server un file system o un qualche strumento che consenta di creare snapshot. Ci sono due opzioni. La prima opzione sono gli snapshot LVM, la seconda opzione è ZFS su Linux.

Dopo di ciò, ogni giorno è necessario creare uno snapshot, che occuperà e utilizzerà un certo spazio. Ovviamente, se i dati cambiano, nel tempo l'occupazione di spazio aumenterà. Questo snapshot può essere recuperato in qualsiasi momento e i dati possono essere ripristinati, è una soluzione piuttosto strana. Inoltre, è necessario limitare queste repliche nella configurazione, affinché non tentino di diventare leader.

È possibile organizzare un ritardo controllato delle repliche nei shard?

Quest'anno pianificate di implementare i wal in ClickHouse. Sarà possibile organizzare un ritardo controllato delle repliche in essi? Ci piacerebbe utilizzare questo per proteggerci da scenari negativi con alterazioni e altre modifiche.

È possibile implementare dei rollback per gli alter? Ad esempio, in un wal esistente, è possibile dire di applicare modifiche fino a questo punto e di smettere di applicare le modifiche da quest'altro punto?

Se nel nostro cluster arriva un comando e lo danneggia, abbiamo una replica condizionata con un ritardo di un'ora, in cui possiamo dire: usiamo proprio quella in questo momento, ma non applicheremo le ultime dieci minuti di modifiche?

Iniziamo con il ritardo controllato delle repliche. Questa richiesta è stata fatta dagli utenti e abbiamo aperto un issue su GitHub con la domanda: «Se a qualcuno serve, mettete un like, un cuoricino». Nessuno ha messo un like, quindi l'issue è stato chiuso. Tuttavia, già adesso è possibile ottenere questa funzionalità configurando ClickHouse. Purtroppo, solo a partire dalla versione 20.3.

ClickHouse esegue costantemente fusioni di dati in background - merge. Quando la fusione viene effettuata, un certo insieme di blocchi di dati viene sostituito con un blocco più grande. Tuttavia, i blocchi di dati precedenti continuano a rimanere sul disco per un certo periodo di tempo.

In primo luogo, rimangono memorizzati fino a quando ci sono query di selezione che li utilizzano, per garantire un funzionamento non bloccante. Le query di selezione leggono tranquillamente dai vecchi blocchi.

In secondo luogo, c'è anche una soglia temporale: i vecchi blocchi di dati rimangono sul disco per otto minuti. Questi otto minuti possono essere configurati e possono arrivare anche a un giorno. Questo avrà un costo in termini di spazio su disco: a seconda del flusso di dati, potrebbe risultare che nell'ultimo giorno i dati non solo raddoppiano, ma possono aumentare di cinque volte. Ma sarete in grado di fermare il server ClickHouse e risolvere tutto in caso di problemi seri.

Ora sorge la domanda su come questo protegga dagli alter. Qui vale la pena approfondire, poiché nelle versioni precedenti di ClickHouse l'alter funzionava in modo tale da cambiare semplicemente i blocchi. C'era un blocco di dati con alcuni file, e noi facciamo, per esempio, alter drop column. Allora questa colonna viene fisicamente rimossa da tutti i blocchi.

Ma a partire dalla versione 20.3, il meccanismo degli alter è stato completamente modificato, e ora i blocchi di dati sono sempre immutabili. Non vengono mai cambiati: gli alter ora funzionano in modo simile alle fusioni. Invece di cambiare un blocco in loco, ne creiamo uno nuovo. Nel nuovo blocco, i file che non sono stati modificati diventano hardlink, e se abbiamo eliminato una colonna, semplicemente non sarà presente nel nuovo blocco. Il vecchio blocco verrà eliminato per impostazione predefinita dopo otto minuti, e qui è possibile modificare le impostazioni di cui si è parlato sopra.

Lo stesso vale per gli alter di tipo mutazioni. Quando si fa alter delete o alter update, non modifica il blocco, ma ne crea uno nuovo. E poi elimina il vecchio.

Cosa fare se la struttura della tabella è cambiata?

Come ripristinare un backup che è stato effettuato con un vecchio schema? E la seconda domanda riguarda il caso degli snapshot e dei mezzi del file system. È adatta qui Btrfs invece di ZFS su Linux LVM?

Se si fa attach partition Se le partizioni hanno una struttura diversa, ClickHouse ti dirà che non è possibile. La soluzione è la seguente. Prima di tutto, crea una tabella temporanea di tipo MergeTree con la vecchia struttura, attacca i dati usando attach, esegui la query alter. Poi puoi o copiare o spostare questi dati e fare nuovamente attach, oppure usare la query alter table move partition.

Ora la seconda domanda è: si può usare Btrfs? Per cominciare, se hai LVM, bastano gli snapshot LVM, e il file system può essere anche ext4, non ha importanza. Con Btrfs tutto dipende dalla tua esperienza nel suo utilizzo. È un file system maturo, ma ci sono ancora alcuni dubbi su come si comporterà nella pratica in uno scenario specifico. Non lo consiglierei se non hai Btrfs in produzione.

Quali sono le migliori pratiche attuali per il resharding dei dati?

La questione del resharding è complessa e sfaccettata. Qui ci sono diverse risposte possibili. Si può affrontare da un lato e dire che in ClickHouse non esiste una funzione integrata per il resharing. Ma temo che questa risposta non soddisfi nessuno. Quindi si può affrontare da un altro lato e dire che in ClickHouse ci sono molti modi per fare resharing dei dati.

Se lo spazio sul cluster sta per finire o non riesce a gestire il carico, aggiungi nuovi server. Ma questi server sono vuoti di default, non ci sono dati e non c'è carico. Devi ripartire i dati affinché siano distribuiti uniformemente sul nuovo cluster di dimensioni aumentate.

Il primo modo in cui puoi farlo è copiare alcune partizioni sui nuovi server usando la query alter table fetch partition. Ad esempio, se avevi partizioni mensili, prendi il primo mese del 2017 e copialo su un nuovo server, poi copia il terzo mese su un altro nuovo server. E continui a farlo fino a ottenere una distribuzione più o meno uniforme.

Lo spostamento può essere effettuato solo per quelle partizioni che non cambiano durante la scrittura. Per le partizioni fresche, sarà necessario disattivare la scrittura, poiché il loro spostamento non è atomico. Altrimenti, otterrai duplicati o mancanze nei dati. Tuttavia, questo metodo è pratico e funziona in modo abbastanza efficiente. Le partizioni compresse vengono trasferite in rete, quindi i dati non vengono ri-compressi né ri-codificati.

Questo metodo ha un inconveniente, che dipende dallo schema di sharding, se è stato previsto questo schema di sharding e quale chiave di sharding era utilizzata. Nel tuo esempio, per il caso delle metriche, la chiave di sharding è l'hash del percorso. Quando esegui una select su una tabella distribuita, essa si rivolge immediatamente a tutti gli shard del cluster e recupera i dati da lì.

Significa che, in pratica, non importa quali dati si trovino su ciascun shard. L'importante è che i dati relativi a un percorso specifico si trovino su uno stesso shard, mentre il particolare shard non è fondamentale. In questo caso, il trasferimento di partizioni già pronte è perfettamente adatto, poiché nelle query di selezione otterrai comunque dati completi, sia prima che dopo lo resharing, poiché la struttura dei valori non ha rilevanza particolare.

Tuttavia, ci sono casi più complessi. Se a livello logico dell'applicazione si prevede uno specifico schema di sharding, in cui questo cliente è posizionato su un determinato shard, e la richiesta può essere inviata direttamente a quell'host, anziché alla tabella distribuita. Oppure si sta utilizzando una versione di ClickHouse relativamente recente e si è attivata l'impostazione optimize skip unused shards. In questo caso, durante la query di selezione, l'espressione nella sezione where verrà analizzata e sarà calcolato su quali shard è necessario operare secondo lo schema di sharding. Questo funziona a condizione che i dati siano dislocati proprio secondo questo schema di sharding. Se li hai riposizionati manualmente, la corrispondenza potrebbe cambiare.

Quindi, questo è il primo metodo. E aspetto la tua risposta, se ti sembra adatto o dobbiamo andare avanti.

Vladimir Kolobaev, lead system administrator in Avito: Alexey, il metodo che hai menzionato non si adatta molto bene quando bisogna distribuire il carico anche per la lettura. Possiamo selezionare una partizione mensile e portare il mese precedente su un'altra node, ma quando verrà una richiesta per quei dati, caricheremo solo quella. Sarebbe preferibile caricare l'intero cluster, poiché, in caso contrario, per un certo periodo tutto il carico di lettura sarà elaborato da solo due shard.

Alexey Milovidov: La risposta è strana: sì, è brutta, ma potrebbe funzionare. Spiego come. È opportuno osservare lo scenario di carico che segue i vostri dati. Se si tratta di dati di monitoraggio, si può praticamente dire che la stragrande maggioranza delle richieste scaricherà dati freschi.

Avete installato nuovi server, trasferito le vecchie partizioni, ma avete anche cambiato il modo in cui vengono registrati i dati freschi. E i dati freschi saranno distribuiti su tutto il cluster. Così, già dopo cinque minuti, le richieste per gli ultimi cinque minuti caricheranno uniformemente il cluster e, dopo un giorno, le richieste per un giorno caricheranno uniformemente il cluster. Sfortunatamente, le richieste per il mese precedente andranno solo su parte dei server del cluster.

Ma spesso non avrete richieste specifiche per febbraio 2019. Probabilmente, se le richieste sono nel 2019, riguarderanno l'intero anno 2019, quindi un grande intervallo di tempo, e non un piccolo range. E tali richieste possono anche caricare uniformemente il cluster. Tuttavia, in generale, la vostra osservazione è assolutamente corretta: si tratta di una soluzione ad hoc che non distribuisce i dati in modo completamente uniforme.

Ho ancora alcuni punti da cui rispondere alla domanda. Uno di essi riguarda come inizialmente rendere il sistema di sharding tale da ridurre il dolore da re-sharding. Questo potrebbe non essere sempre possibile.

Ad esempio, avete dati di monitoraggio. I dati di monitoraggio crescono per tre motivi. Primo: accumulo di dati storici. Secondo: aumento del traffico. E terzo: incremento del numero di elementi da monitorare. Emergeranno nuovi microservizi e metriche da salvare.

È possibile che il maggiore incremento sia legato proprio al terzo motivo: l'aumento dell'uso del monitoraggio. In questo caso, è opportuno osservare la natura del carico, quali siano le principali richieste per il select. Le principali richieste per il select, molto probabilmente, interesseranno un certo sottoinsieme di metriche.

Ad esempio, l'uso della CPU su alcuni server da parte di un certo servizio. Risulta che c'è un sottoinsieme di chiavi secondo cui si ottengono questi dati. E la stessa richiesta di questi dati è, molto probabilmente, abbastanza semplice e viene eseguita in decine di millisecondi. Viene utilizzato per servizi di monitoraggio, per dashboard. Spero di aver compreso correttamente.

Vladimir Kolobaev: Il fatto è che facciamo frequentemente riferimento ai dati storici, poiché confrontiamo in tempo reale la situazione attuale con quella storica. E per noi è importante avere accesso rapido a un grande volume di dati, e ClickHouse si occupa di questo in modo eccellente.

Ha completamente ragione, la maggior parte delle richieste di lettura che gestiamo proviene dall'ultimo giorno, come qualsiasi sistema di monitoraggio. Ma anche i dati storici hanno un carico piuttosto elevato. Questo è principalmente dovuto al sistema di alerting, che ogni trenta secondi interroga ClickHouse: «Restituisci i dati delle ultime sei settimane. E ora costruisci per me una media mobile e confrontiamo il valore attuale con quello storico».

Vorrei dire che abbiamo una piccola tabella per queste richieste molto recenti, dove conserviamo solo due giorni di dati, e le richieste principali vengono indirizzate lì. In una grande tabella shardizzata inviamo solo le grandi richieste storiche.

Alexey Milovidov: Sfortunatamente, non è applicabile al vostro scenario, ma racconterò la descrizione di due cattive e complesse schemi di sharding che non dovrebbero essere utilizzati, ma che vengono utilizzati nel servizio dei miei amici.

C'è un cluster principale con eventi di «Yandex.Metrica». Gli eventi sono visualizzazioni di pagina, clic e transizioni. La maggior parte delle richieste va a un sito web specifico. Aprite il servizio «Yandex.Metrica», avete un sito — avito.ru, accedete al rapporto e viene effettuata una richiesta per il vostro sito.

Ma ci sono anche altre richieste — analitiche e globali, che vengono effettuate dagli analisti interni. Nota che gli analisti interni fanno richieste solo per i servizi di «Yandex». Tuttavia, anche i servizi di «Yandex» rappresentano una parte significativa di tutti i dati. Queste sono richieste non per contatori specifici, ma per una filtrazione più ampia.

Come organizzare i dati in modo che tutto funzioni efficacemente sia per un singolo contatore che per le richieste globali? La difficoltà risiede anche nel fatto che il numero di richieste in ClickHouse sul cluster "Metriche" è di diverse migliaia al secondo. E queste non sono richieste banali; per esempio, diverse migliaia al secondo non possono essere gestite da un singolo server ClickHouse.

La dimensione del cluster è di seicento e qualcosa server. Se sopra questo cluster si semplicemente applica una tabella Distribuita e si inviano diverse migliaia di richieste, la situazione peggiorerà ulteriormente rispetto all'invio verso un singolo server. D'altra parte, l'idea che i dati siano distribuiti uniformemente e che richiediamo da tutti i server la scartiamo immediatamente.

Esiste un'opzione diametralmente opposta. Immagina se shardassimo i dati per siti web e le richieste per un singolo sito andassero su un singolo shard. Ora il cluster potrebbe gestire tranquillamente diecimila richieste al secondo, ma in uno shard una richiesta specifica potrebbe risultare troppo lenta. Non sarebbe più scalabile in termini di capacità. Soprattutto se si tratta del sito avito.ru. Non svelerò un segreto dicendo che Avito è uno dei siti più visitati nel runet. E gestirlo su un singolo shard sarebbe pazzesco.

Perciò, lo schema di sharding è stato progettato in modo più ingegnoso. L'intero cluster è suddiviso in un certo numero di cluster, che chiamiamo strati. All'interno di ogni cluster ci sono da una decina a diverse decine di shard. In totale ci sono trentanove di questi cluster.

Come si scala tutto questo? Il numero di cluster non cambia: come erano trentanove alcuni anni fa, così è rimasto. Ma all'interno di ciascuno di essi aumentiamo gradualmente il numero di shard man mano che accumuliamo dati. Lo schema di sharding in generale è questo: la suddivisione in questi cluster avviene per siti web, e per comprendere quale sito si trovi su quale cluster si utilizza una base dati separata in MySQL. Un sito — su un cluster. E all'interno di esso lo sharding avviene per identificatori dei visitatori.

Durante la registrazione, li suddividiamo in base al resto della divisione dell'identificatore del visitatore. Ma quando aggiungiamo un nuovo shard, lo schema di sharding cambia, continuiamo a suddividere, ma in base al resto della divisione per un altro numero. Ciò significa che un visitatore è comunque posizionato su più server, e non si può fare affidamento su questo. Questo è stato fatto esclusivamente per migliorare la compressione dei dati. Quando facciamo delle richieste, andiamo nella tabella Distributed, che consulta il cluster e si rivolge a decine di server. È uno schema piuttosto complicato.

Tuttavia, la mia storia sarebbe incompleta se non dicessi che abbiamo abbandonato questo schema. Nella nuova configurazione abbiamo cambiato tutto e copiato tutti i dati utilizzando clickhouse-copier.

Nella nuova configurazione, tutti i siti sono divisi in due categorie: grandi e piccoli. Non so come sia stato scelto il limite, ma alla fine è risultato che i grandi siti vengono registrati su un solo cluster, con 120 shard e tre repliche per ciascuno, cioè 360 server. E lo schema di sharding è tale che qualsiasi richiesta va subito a tutti gli shard. Se ora aprite una qualsiasi pagina di report per avito.ru in "Yandex.Metrica", la richiesta andrà a 120 server. Ci sono pochi grandi siti in Runet. E le richieste non sono mille al secondo, ma addirittura meno di cento. Tutto questo viene gestito senza problemi dalla tabella Distributed, che ognuno di essi processa con 120 server.

Il secondo cluster è per i piccoli siti. Qui lo schema di sharding è basato sull'identificatore del sito, e ogni richiesta va esattamente a uno shard.

In ClickHouse c'è uno strumento chiamato clickhouse-copier. Puoi parlarcene?

Dirò subito che questa soluzione è più ingombrante e un po' meno performante. Il vantaggio è che distribuisce completamente i dati secondo lo schema che specificherete. Ma lo svantaggio dell'utility è che non esegue affatto il re-sharding. Copia i dati da un schema di cluster a un'altra schema di cluster.

Ciò significa che per il suo funzionamento avete bisogno di due cluster. Possono trovarsi sugli stessi server, ma, comunque, i dati non verranno spostati in modo incrementale, ma verranno copiati.

Ad esempio, c'erano quattro server, ora sono diventati otto. Crei una nuova tabella Distributed su tutti i server, nuove tabelle locali e avvii clickhouse-copier, specificando in esso lo schema di lavoro, che deve leggere da lì, accettare il nuovo schema di shard e spostare i dati di là. E avrà bisogno di uno spazio sui vecchi server che è un uno e mezzo volte maggiore di quello che c'è ora, perché i dati vecchi devono rimanere su di essi, e in aggiunta a ciò arriverà la metà di questi stessi dati vecchi. Se hai pensato in anticipo che i dati devono essere resharded e c'è spazio, allora questo metodo sarà adatto.

Come funziona internamente clickhouse-copier? Divide tutto il lavoro in un insieme di compiti per elaborare una partizione di una tabella su uno shard. Tutti questi compiti possono essere eseguiti in parallelo, e clickhouse-copier può essere avviato su macchine diverse in più istanze, ma ciò che fa per una partizione non è altro che un insert select. I dati vengono letti, decompressi, ristrutturati, poi compressi di nuovo, scritti da qualche parte, riordinati. È una soluzione più pesante.

Avevate un progetto pilota chiamato resharding. Che fine ha fatto?

Avevi già nel 2017 una prova, chiamata resharding. C'è anche un'opzione in ClickHouse. Mi sembra di capire che non ha avuto successo. Puoi raccontare perché è andata così? Sembrava essere molto attuale.

Il problema è che, nel caso sia necessario reshardeare i dati sul posto, è necessaria una sincronizzazione piuttosto complessa per farlo in modo atomico. Quando abbiamo iniziato a esaminare come è strutturata questa sincronizzazione, è diventato chiaro che ci sono problemi fondamentali. E questi problemi fondamentali non sono solo teorici, ma si sono immediatamente manifestati nella pratica con ciò che può essere spiegato molto semplicemente: nulla funziona.

È possibile unire tutte le parti dei dati in un unico blocco prima di spostarle su dischi lenti?

Domanda su TTL con l'opzione move to slow disk nel contesto delle fusioni. C'è modo, oltre a usare cron, di unire tutte le parti in una prima di spostarle su dischi lenti?

La risposta alla domanda se sia possibile unire automaticamente tutti i pezzi in uno prima del loro trasferimento è no. Penso che non ci sia necessità. Non è necessario unire tutte le parti in uno, ma si può semplicemente contare sul fatto che saranno trasferite automaticamente su dischi lenti.

Abbiamo due criteri per le regole di trasferimento. Il primo è in base alla quantità di spazio disponibile. Se nel livello attuale di archiviazione c'è meno di una certa percentuale di spazio libero, scegliamo un pezzo e lo trasferiamo in un'archiviazione più lenta. Più precisamente, non più lenta, ma la successiva — come configurato.

Il secondo criterio è basato sulle dimensioni. Riguarda il trasferimento di grandi pezzi. Puoi regolare la soglia in base allo spazio libero sul disco veloce e i dati verranno trasferiti automaticamente.

Come si possono aggiornare le versioni di ClickHouse se non si ha la possibilità di verificare preventivamente la compatibilità?

Questo tema viene discusso regolarmente nel chat di Telegram ClickHouse considerando diverse versioni, eppure. Quanto è sicuro aggiornare dalla versione 19.11 alla 19.16 e, ad esempio, dalla 19.16 alla 20.3. Qual è il modo migliore per passare alle nuove versioni, senza avere la possibilità di controllare la compatibilità in un ambiente di test?

Ci sono alcune regole ‘d'oro’. La prima è leggere il changelog. È ampio, ma ci sono punti separati riguardanti le modifiche non retrocompatibili. Non dovresti considerare questi punti come un flag rosso. In genere, si tratta di piccole incompatibilità legate a determinate funzionalità marginali che, molto probabilmente, non utilizzi.

Secondo — se non hai la possibilità di controllare la compatibilità in un ambiente di test e desideri aggiornarti direttamente in produzione, la raccomandazione è di non farlo. Prima crea un ambiente di test e verifica. Se non hai un ambiente di test, probabilmente la tua azienda non è troppo grande, quindi puoi copiare parte dei dati sul tuo laptop e verificare che tutto funzioni correttamente. Puoi anche avviare alcune repliche localmente sul tuo computer. Oppure puoi avviare una nuova versione da qualche parte vicino e trasferire parte dei dati, cioè creare un ambiente di test improvvisato.

Un'altra regola è di non aggiornare durante la settimana successiva all'uscita di una versione a causa del monitoraggio dei bug in produzione e delle successive correzioni rapide. Analizziamo la numerazione delle versioni ClickHouse per non confonderci.

C'è una versione 20.3.4. Il numero 20 indica l'anno di rilascio - 2020. Dal punto di vista di ciò che contiene, questo non ha alcun significato, quindi non ci concentreremo su questo. Poi c'è 20.3. Aumentiamo il secondo numero, in questo caso 3, ogni volta che rilasciamo una versione con qualche nuova funzionalità. Se vogliamo aggiungere qualche possibilità a ClickHouse, dobbiamo aumentare questo numero. Quindi nella versione 20.4 ClickHouse funzionerà ancora meglio. Il terzo numero - 20.3.4. Qui 4 è il numero di patch release in cui non abbiamo aggiunto nuove funzionalità, ma abbiamo corretto alcuni bug. E 4 significa che lo abbiamo fatto quattro volte.

Non bisogna pensare che sia qualcosa di terribile. Di solito, l'utente può installare l'ultima versione e funzionerà senza alcun problema per un anno. Ma immaginate che in qualche funzionalità per l'elaborazione delle bitmap, che è stata aggiunta dai nostri colleghi cinesi, il server si blocchi quando vengono passati argomenti errati. Dobbiamo correggerlo. Rilasceremo una nuova versione di patch e ClickHouse diventerà più stabile.

Se ClickHouse è in produzione e viene rilasciata una nuova versione di ClickHouse con funzionalità aggiuntive - per esempio, 20.4.1 - non affrettatevi a metterla in produzione il primo giorno. A cosa serve? Se non state ancora utilizzando ClickHouse, potete installarlo e probabilmente tutto andrà bene. Ma se ClickHouse è già stabile in produzione, seguite le patch e gli aggiornamenti - quali problemi stiamo correggendo.

Kirill Shvakov: Voglio aggiungere qualcosa sugli ambienti di test. Tutti temono gli ambienti di test e in qualche modo pensano che, se avete un grande cluster di ClickHouse, allora anche l'ambiente di test debba essere almeno della stessa dimensione o, almeno, dieci volte più piccolo. Non è affatto così.

Posso parlare per esperienza personale. Ho un progetto e lì c'è ClickHouse. Il nostro ambiente di test per questo è una piccola macchina virtuale in Hetzner che costa venti euro, dove è completamente tutto implementato. Per fare questo, abbiamo una completa automazione in Ansible e quindi, in linea di principio, non c'è differenza se eseguiamo su server fisici o semplicemente su macchine virtuali.

Cosa si può fare? Non sarebbe male inserire nella documentazione di ClickHouse un esempio su come implementare un piccolo cluster in Docker, in LXC, e magari creare un playbook di Ansible, poiché ognuno ha i propri metodi di deployment. Questo semplificherebbe molto. Quando puoi creare un cluster in cinque minuti, è molto più facile provare a risolvere eventuali problemi. È decisamente più comodo, perché spingere in produzione una versione che non hai testato è una strada senza uscita. A volte funziona, a volte no. E quindi sperare in un successo è inadeguato.

Maksim Kotiakov, ingegnere backend senior di Avito: Aggiungo qualche informazione sugli ambienti di test nella serie dei problemi delle grandi aziende. Abbiamo un cluster di accettazione ClickHouse, che è una copia esatta di quello che abbiamo in produzione per schemi di dati e configurazioni. Questo cluster è implementato in contenitori piuttosto ridotti, con risorse minime. Registriamo lì una certa percentuale dei dati di produzione, per fortuna abbiamo la possibilità di replicare il flusso in Kafka. Tutto è sincronizzato e scalato—sia per potenza che per flusso—e, in teoria, in condizioni normali dovrebbe comportarsi come in produzione per quanto riguarda le metriche. Tutto ciò che è potenzialmente esplosivo viene prima testato su questa piattaforma e si attende alcuni giorni prima che sia pronto. Ma ovviamente, questa soluzione è costosa, difficile e comporta costi di supporto.

Alexey Milovidov: Vi racconterò cosa rappresenta l'ambiente di test dei nostri amici di «Yandex.Metrica». Un cluster aveva oltre 600 server, un altro 360, e c'è anche un terzo cluster e diversi altri. L'ambiente di test per uno di essi è semplicemente composto da due shard con due repliche ciascuno. Perché due shard? Perché non può esserci solo uno. E anche le repliche devono essere presenti. È solo una quantità minima che ci si può permettere.

Questo ambiente di test consente di verificare la funzionalità delle query e se ci sono stati grossi malfunzionamenti. Ma spesso i problemi hanno un'altra natura, quando tutto funziona ma ci sono piccole variazioni nel carico.

Faccio un esempio. Abbiamo deciso di installare una nuova versione di ClickHouse. È stata rilasciata nell'ambiente di test, eseguiti test automatizzati in «Yandex.Metrica», che confrontano i dati tra la vecchia e la nuova versione, eseguendo l'intero processo. E naturalmente, i test verdi del nostro CI. Altrimenti non avremmo nemmeno proposto questa versione.

Tutto va benissimo. Iniziamo a portarlo in produzione. Ricevo un messaggio che carico sui grafici è aumentato diverse volte. Torniamo indietro con la versione. Guardando il grafico vedo che il carico è effettivamente aumentato diverse volte durante il rilascio, e poi è diminuito di nuovo dopo. Poi abbiamo iniziato a tornare indietro con la versione. E il carico è aumentato allo stesso modo e poi è ricaduto allo stesso modo. Quindi la conclusione è che il carico è aumentato a causa del rilascio, niente di sorprendente.

Poi è stato difficile convincere i colleghi a installare la nuova versione. Dico: "Va tutto bene, procedete. Incrociate le dita, tutto funzionerà. Ora il carico è aumentato sui grafici, ma va bene. Tenete duro". Insomma, abbiamo fatto così, e tutti — la versione è stata rilasciata in produzione. Ma praticamente ogni volta ci sono problemi simili durante il rilascio.

Il comando Kill query dovrebbe terminare le query, ma non lo fa. Perché?

Un utente è venuto da me, un analista, e ha creato una sorta di query che ha bloccato il mio cluster ClickHouse. Una certa nodo o l'intero cluster — a seconda di quale replica o shard la query ha colpito. Vedo che tutte le risorse CPU su questo server sono ai minimi storici, tutto è rosso. Eppure ClickHouse sta rispondendo alle query. E scrivo: "Mostrami per favore, la lista dei processi, quale query ha generato questa follia".

Trovo questa query e scrivo kill. E vedo che non succede nulla. Il mio server è ai minimi storici, ClickHouse continua a restituirmi alcuni comandi, mostrando che il server è vivo e tutto va bene. Ma ho una degradazione in tutte le query degli utenti, inizia la degradazione nella scrittura in ClickHouse, e il mio kill query non funziona. Perché? Pensavo che la kill query dovesse fermare le query, ma non succede.

Ora arriverà una risposta piuttosto strana. Il fatto è che la kill query non ferma le query.

La kill query imposta un piccolo flag chiamato "voglio che questa query venga uccisa". E la query stessa, durante l'elaborazione di ogni blocco, controlla questo flag. Se è impostato, la query smette di funzionare. Risulta che nessuno ferma la query, deve controllare tutto da sola e fermarsi. E questo dovrebbe funzionare in tutti i casi quando la query è in fase di elaborazione dei blocchi di dati. Elaborerà il prossimo blocco di dati, controllerà il flag e si fermerà.

Questo non funziona nei casi in cui la richiesta è bloccata per qualche operazione. Tuttavia, è probabile che questo non sia il tuo caso, perché, a quanto pare, utilizza molte risorse del server. Potrebbe non funzionare nel caso di ordinamento esterno e in alcuni altri dettagli. Ma in generale, non dovrebbe essere così, è un bug. E l'unica cosa che posso consigliare è di aggiornare ClickHouse.

Come calcolare il tempo di risposta sotto carico in lettura?

C'è una tabella che conserva aggregati per item - diversi contatori. Il numero di righe è di circa cento milioni. È possibile aspettarsi un tempo di risposta prevedibile se si inviano 1K RPS su 1K item?

A giudicare dal contesto, si tratta di carico in lettura, perché non ci sono problemi con la scrittura - si possono inserire mille, centomila o anche milioni di righe.

Le richieste di lettura possono essere molto varie. In un select 1, ClickHouse può eseguire circa decine di migliaia di richieste al secondo, quindi anche le richieste su una sola chiave richiederanno alcune risorse. E queste richieste puntuali saranno più complicate rispetto a quelle in alcuni database key-value, perché per ogni lettura è necessario leggere un blocco di dati per indice. L'indice non si riferisce a ogni record, ma a ciascun intervallo. Quindi dovremo leggere l'intero intervallo - che è di 8192 righe di default. E dovremo decomprimere un blocco di dati compresso da 64 KB a 1 MB. Di solito, tali richieste puntuali richiedono da alcuni millisecondi. Ma questa è l'opzione più semplice.

Facciamo una semplice aritmetica. Se moltiplichiamo alcuni millisecondi per mille, otteniamo alcuni secondi. È come se non si potessero gestire mille richieste al secondo, ma in realtà è possibile, perché abbiamo diversi core di processore. Quindi, in linea di massima, ClickHouse può gestire 1000 RPS a volte, ma con richieste brevi, precisamente puntuali.

Se è necessario scalare il cluster ClickHouse per il numero di richieste semplici, consiglio la cosa più semplice: aumentare il numero di repliche e inviare le richieste a una replica casuale. Se una replica gestisce cinquecento richieste al secondo, cosa del tutto realistica, allora tre repliche gestiranno milleseicento.

A volte, certo, è possibile configurare ClickHouse per massimizzare il numero di letture puntuali. Cosa serve per farlo? Prima di tutto, ridurre la granularità dell'indice. Tuttavia, non bisogna ridurla a uno, ma tenere conto che il numero di record nell'indice sarà di diversi milioni o decine di milioni sul server. Se nella tabella ci sono cento milioni di righe, si può impostare la granularità su 64.

È possibile ridurre la dimensione del blocco compresso. Per questo ci sono impostazioni min compress block size, max compress block size. Queste possono essere ridotte, ricaricando i dati, e così le query puntuali saranno più veloci. Tuttavia, ClickHouse non è un database key-value. Un elevato numero di piccole query rappresenta un antipattern di carico.

Kirill Shvakov: Vorrei dare un consiglio nel caso in cui ci siano degli encounter normali. Questa è una situazione abbastanza standard, in cui ClickHouse memorizza un contatore. Ho un utente, proviene da un certo paese, c'è un terzo campo, e qualcosa deve essere incrementato. Prendi MySQL, crea una chiave unica: in MySQL è una chiave duplicata, mentre in PostgreSQL è un conflitto, e aggiungi con un segno più. Questo funzionerà sicuramente meglio.

Quando hai pochi dati, non ha molto senso usare ClickHouse. Ci sono database normali che gestiscono bene questa situazione.

Cosa ottimizzare in ClickHouse per avere più dati nella cache?

Immaginiamo la situazione: i server hanno 256 GB di RAM, nella routine quotidiana ClickHouse utilizza circa 60-80 GB, nel picco fino a 130. Cosa si può attivare e ottimizzare per avere più dati nella cache e, di conseguenza, meno accessi al disco?

Di solito, la cache delle pagine del sistema operativo gestisce bene questo compito. Se apri semplicemente top e controlli, c'è scritto cached o free, puoi anche notare quanto è stato messo in cache: quindi puoi vedere che tutta la memoria libera viene utilizzata per la cache. E questi dati, quando letti, non saranno letti dal disco, ma dalla RAM. A tal proposito, posso dire che la cache viene utilizzata in modo efficiente, perché vengono memorizzati proprio i dati compressi.

Tuttavia, se desideri accelerare ulteriormente alcune query semplici, c'è la possibilità di attivare all'interno di ClickHouse la cache dei dati decompressi. Questo si chiama uncompressed cache. Nel file di configurazione config.xml impostate la dimensione della cache non compressa al valore desiderato: consiglio di non superare la metà della RAM disponibile, poiché il resto verrà utilizzato per la cache della pagina.

Inoltre, ci sono due impostazioni a livello di richiesta. La prima impostazione è usa cache non compressa che ne attiva l'uso. Si consiglia di attivarla per tutte le richieste, tranne quelle pesanti che potrebbero leggere tutti i dati e svuotare questa cache. E la seconda impostazione è qualcosa come il numero massimo di righe da utilizzare per la cache. Limita automaticamente le grandi richieste, affinché non passino attraverso la cache.

Come si può configurare storage_configuration per l'archiviazione in RAM?

Nella nuova documentazione di ClickHouse ho trovato una sezione relativa allo storage dei dati. Nella descrizione c'è un esempio con SSD veloci.

È interessante come si possa configurare la stessa cosa con la memoria calda del volume. E un'altra domanda. Come funziona il select con tale organizzazione dei dati? Leggerà l'intero set o solo quello che si trova su disco, e questi dati vengono compressi in memoria? E come funziona la sezione prewhere con tale organizzazione dei dati?

Questa impostazione influisce sullo storage dei chunk di dati, e il loro formato non cambia.
Esaminiamo più in dettaglio.

È possibile configurare lo storage dei dati in memoria. Tutto ciò che si configura per il disco è il suo percorso. Creerete una partizione tmpfs, che è montata in un percorso della filesystem. Indicate questo percorso come percorso per lo storage dei dati per la partizione più calda, dove iniziano a pervenire e a essere scritti i chunk di dati, tutto funziona bene.

Ma non consiglio di farlo a causa della bassa affidabilità, anche se, se avete almeno tre repliche in diversi data center, allora si può fare. In caso di problemi, i dati saranno recuperati. Immaginiamo che il server venga improvvisamente spento e riacceso. La partizione viene rimontata di nuovo, ma lì c'è il vuoto. Il server ClickHouse, all'avvio, vede che questi chunk mancano, anche se, secondo i metadati di ZooKeeper, dovrebbero esserci. Controlla quali repliche li hanno, li richiede e li scarica. In questo modo i dati saranno recuperati.

In questo senso, la memorizzazione dei dati in RAM non è sostanzialmente diversa dalla loro memorizzazione su disco, poiché quando i dati vengono scritti su disco, anche all'inizio passano tramite page cache e vengono scritti fisicamente in modo differito. Questo dipende dalla modalità di montaggio del file system. Ma per sicurezza dico che ClickHouse non esegue fsync durante l'insert.

Inoltre, i dati in RAM sono memorizzati esattamente nel stesso formato di quelli su disco. La query select seleziona esattamente i pezzi che devono essere letti, selezionando i necessari intervalli di dati, e li legge. E prewhere funziona esattamente allo stesso modo, indipendentemente dal fatto che i dati fossero in RAM o su disco.

Fino a quanti valori unici è efficace Low Cardinality?

Low Cardinality è strutturato in modo astuto. Crea dizionari di dati, ma sono locali. Innanzitutto, i dizionari sono specifici per ogni pezzo e, in secondo luogo, anche all'interno di un singolo pezzo possono essere diversi per ogni intervallo. Quando il numero di valori unici raggiunge una soglia — credo sia un milione — il dizionario viene semplicemente accantonato e ne viene creato uno nuovo.

In generale, la risposta è: per ogni intervallo locale — diciamo, per ogni giorno — fino a circa un milione di valori unici, Low Cardinality è efficace. Dopo ci sarà semplicemente un fallback, in cui verranno utilizzati molti dizionari diversi piuttosto che uno solo. Funzionerà più o meno come una normale colonna di tipo string, forse con un'efficienza leggermente inferiore, ma non ci sarà una seria degradazione delle prestazioni.

Quali sono le migliori pratiche per la ricerca full-text su una tabella con cinque miliardi di righe?

Ci sono diverse opzioni di risposta. La prima è dire che ClickHouse non è un sistema per la ricerca full-text. Per questo ci sono sistemi specializzati, ad esempio, Elasticsearch e Sphinx. Tuttavia, incontro sempre più persone che affermano di passare da Elasticsearch a ClickHouse.

Perché sta succedendo questo? Loro spiegano che Elasticsearch smette di gestire il carico su alcuni volumi, a partire da ciò che riguarda la costruzione degli indici. Gli indici diventano troppo ingombranti e, se si trasferiscono semplicemente i dati in ClickHouse, si ha una memorizzazione molto più efficiente in termini di volume. Inoltre, le query di ricerca erano spesso diverse, non era necessario trovare in tutto il volume dei dati una certa frase tenendo conto della morfologia, ma cose completamente diverse. Ad esempio, trovare nelle ultime ore nei log una certa sottosequenza di byte.

In questo caso, in ClickHouse crei un indice, il cui primo campo sarà la data con il tempo. E il maggiore taglio dei dati avverrà proprio in base all'intervallo di date. All'interno dell'intervallo di date selezionato, di solito è possibile effettuare una ricerca full-text anche con il metodo brute force utilizzando like. L'operatore like in ClickHouse è il più efficiente che tu possa trovare. Se ne trovi uno migliore, fammelo sapere.

Ma comunque like è una scansione completa. E la scansione completa può essere lenta non solo per il CPU, ma anche per il disco. Se per caso hai un terabyte di dati al giorno e stai cercando una parola in un giorno, dovrai scansionare un terabyte. E sicuramente si trova su normali hard disk, e alla fine saranno caricati a tal punto che non potrai accedere a quel server via SSH.

In questo caso, sono pronto a suggerire un altro piccolo trucco. È di tipo sperimentale: potrebbe funzionare, oppure no. In ClickHouse ci sono indici full-text sotto forma di filtri Bloom trigram. I nostri colleghi dell'azienda Arenadata li hanno già testati e spesso funzionano proprio come previsto.

Per utilizzarli correttamente, è importante comprendere bene come funzionano: cosa rappresenta un filtro Bloom trigram e come scegliere la sua dimensione. Posso dire che saranno utili per le query relative a frasi strane, sottostringhe che si trovano raramente nei dati. In questo caso, verranno selezionati sottointervalli negli indici e verranno letti meno dati.

Recentemente, ClickHouse ha introdotto funzioni ancora più avanzate per la ricerca full-text. Questo, innanzitutto, consente di cercare un sacco di sottostringhe in un passaggio, inclusi varianti con rispetto della maiuscole e minuscole, senza rispetto della maiuscole e minuscole, con supporto per UTF-8 o solo per ASCII. Scegli la più efficace di cui hai bisogno.

C'è anche la ricerca di più espressioni regolari in un passaggio. Non è necessario scrivere X like una sottostringa o X like un'altra sottostringa. Scrivi tutto insieme e tutto verrà eseguito nel modo più efficiente.

Terzo: ora ci sono ricerche approssimative per regex e ricerche approssimative per sottostringhe. Se qualcuno ha scritto una parola con un errore di battitura, sarà cercata per la corrispondenza massima.

Come organizzare al meglio l'accesso a ClickHouse per un grande numero di utenti?

Racconta come organizzare al meglio l'accesso per un gran numero di consumatori e analisti. Come formare una coda, prioritizzare le richieste max concurrent queries e con quali strumenti?

Se il cluster è abbastanza grande, una buona soluzione sarebbe alzare altri due server che diventeranno il punto d'ingresso per gli analisti. Cioè, non consentire agli analisti di accedere a specifici shard del cluster, ma semplicemente creare due server vuoti, senza dati, e impostare i diritti di accesso su di essi. Inoltre, le impostazioni degli utenti per le richieste distribuite vengono trasmesse ai server remoti. Cioè, configuri tutto su questi due server e le impostazioni hanno effetto su tutto il cluster.

In linea di principio, questi server sono senza dati, ma la quantità di RAM su di essi è molto importante per l'esecuzione delle richieste. Anche il disco può essere utilizzato per dati temporanei, se è attivata l'aggregazione esterna o l'ordinamento esterno.

È importante guardare le impostazioni che sono legate a tutti i possibili limiti. Se entro nel cluster "Yandex.Metrica" come analista e faccio una richiesta select count from hits, riceverò subito un'eccezione che non posso eseguire la richiesta. Il numero massimo di righe che mi è consentito esaminare è di cento miliardi, mentre nel cluster ci sono cinquanta trilioni in una sola tabella. Questo è il primo limite.

Supponiamo di rimuovere il limite sul numero di righe e di eseguire nuovamente la richiesta. Allora vedrò la seguente eccezione: è attivata l'impostazione force index by date. Non posso eseguire la richiesta se non ho specificato l'intervallo di date. Non bisogna contare sul fatto che gli analisti lo indichino manualmente. Un caso tipico è l'indicazione di un intervallo di date where event date between settimana. E poi viene semplicemente indicata la parentesi nel punto sbagliato, e invece di and si ottiene or — or URL match. Se non ci sono limiti, andrà a scandire la colonna URL e consumerà semplicemente un sacco di risorse.

Inoltre, in ClickHouse ci sono due impostazioni di priorità. Sfortunatamente, sono molto primitive. Una si chiama semplicemente priorità. Se la priorità ≠ 0 e vengono eseguite richieste con qualche priorità, ma nel frattempo viene eseguita una richiesta con una priorità che ha un valore inferiore, il che significa una priorità più alta, allora la richiesta con il valore di priorità maggiore, che indica una priorità più bassa, viene semplicemente sospesa e non funzionerà affatto durante questo tempo.

Questa è un'impostazione molto grossolana e non è adatta per i casi in cui ci sia un carico costante sul cluster. Ma se hai richieste brevi e impulsive che sono importanti e il cluster è per lo più inattivo, tale impostazione è adatta.

La seguente impostazione delle priorità si chiama priorità del thread OS. Essa semplicemente imposta per tutti i thread che eseguono la richiesta un valore di nice per lo scheduler Linux. Funziona così così, ma comunque funziona. Se imposti il valore nice più basso - significa il valore più alto, e quindi la priorità più bassa - e per le richieste ad alta priorità imposti -19, allora la CPU consumerà richieste a bassa priorità circa quattro volte meno rispetto a quelle ad alta priorità.

È inoltre necessario impostare il tempo massimo per l'esecuzione della richiesta - diciamo cinque minuti. La velocità minima per l'esecuzione della richiesta - è la cosa migliore. Questa impostazione esiste da tempo ed è necessaria non solo per affermare che ClickHouse non rallenta, ma per forzarlo.

Immagina di impostare: se una richiesta elabora meno di un milione di righe al secondo - non si può fare così. Questo infanga il nostro buon nome, il nostro buon database. Diciamo che lo vietiamo semplicemente. Ci sono in realtà due impostazioni. Una si chiama velocità minima di esecuzione — in righe al secondo, e l'altra si chiama timeout prima di controllare la velocità minima di esecuzione — di default quindici secondi. Quindi quindici secondi vanno bene, ma poi, se è lento, semplicemente lancia un'eccezione — interrompi la richiesta.

È inoltre necessario impostare le quote. In ClickHouse c'è una funzionalità integrata di quote che calcola il consumo delle risorse. Ma, sfortunatamente, non delle risorse fisiche come CPU, dischi, ma logiche - il numero di richieste elaborate, righe e byte letti. E puoi impostare, ad esempio, un massimo di cento richieste in cinque minuti e mille richieste all'ora.

Perché è importante? Perché parte delle richieste analitiche verrà eseguita manualmente direttamente dal client ClickHouse. E andrà tutto bene. Ma se nella vostra azienda ci sono analisti avanzati, scriveranno uno script, e nello script potrebbe esserci un errore. E questo errore porterà a che la richiesta venga eseguita in un ciclo infinito. Da questo bisogna proteggersi.

È possibile restituire i risultati di una query a dieci client?

Abbiamo alcuni utenti che amano inviare richieste molto grandi allo stesso momento. La richiesta è grande, viene eseguita rapidamente, ma poiché ci sono molte richieste simultaneamente, diventa molto doloroso. È possibile eseguire la stessa richiesta, che è arrivata dieci volte di seguito, una sola volta e restituire il risultato a dieci clienti?

Il problema è che non abbiamo risultati della cache o della cache dei dati intermedi. C'è la cache della pagina del sistema operativo, che consente di non leggere i dati dal disco ripetutamente, ma sfortunatamente i dati dovranno comunque essere decompressi, deserializzati e elaborati nuovamente.

Ci piacerebbe in qualche modo evitarlo, memorizzando in cache i dati intermedi o organizzando richieste simili in una coda ed aggiungendo la cache dei risultati. Attualmente stiamo sviluppando una pull request che aggiunge la cache delle richieste, ma solo per le sotto-richieste nelle sezioni in e join — quindi la soluzione è incompleta.

Tuttavia, si presenta anche a noi una situazione simile. Un esempio canonico è dato dalle richieste con paginazione. C'è un report, ci sono diverse pagine e si invia una richiesta limitata a 10. Poi lo stesso, ma limitata a 10,10. Poi un'altra pagina ancora. E ci si chiede, perché contare tutto questo ogni volta? Ma al momento non c'è soluzione, e non si può evitarlo.

C'è una soluzione alternativa, che viene installata come sidecar accanto a ClickHouse — ClickHouse Proxy.

Kirill Shvakov: In ClickHouse Proxy c'è un limiter di rate e una cache dei risultati integrati. Sono state fatte molte impostazioni, poiché si è affrontata una problematica simile. Il Proxy permette di limitare le richieste, mettendole in coda, e configurare per quanto tempo vive la cache delle richieste. Se le richieste sono davvero identiche, il Proxy le restituirà più volte, mentre effettuerà la richiesta a ClickHouse solo una volta.

Anche Nginx ha una cache nella versione gratuita, e questo funzionerà. Nginx ha anche impostazioni che, se le richieste arrivano contemporaneamente, rallenta le altre finché una non viene eseguita. Ma nel ClickHouse Proxy la configurazione è molto migliore. È stato progettato specificamente per ClickHouse, proprio per queste richieste, quindi si adatta meglio. Inoltre, è semplice da installare.

Come gestire le operazioni asincrone e le viste materializzate?

C'è un problema: le operazioni con il motore di sostituzione sono asincrone: prima vengono registrati i dati, poi avviene la loro compressione. Se sotto la tabella c'è una tabella materializzata con alcuni aggregati, i duplicati verranno registrati lì. E se non c'è una logica complessa, i dati saranno duplicati. Cosa si può fare al riguardo?

C'è una soluzione ovvia: implementare un trigger su una certa classe di materializzazioni durante l'operazione asincrona di compressione. Ci sono alcune "pallottole d'argento", piani per implementare funzionalità simili?

È importante capire come funziona la deduplicazione. Ciò di cui sto per parlare non è direttamente collegato alla questione, ma vale la pena tenerlo a mente.

Durante l'inserimento in una tabella replicata, viene eseguita la deduplicazione dei blocchi inserti per intero. Se hai reinserito lo stesso blocco, contenente lo stesso numero di righe nello stesso ordine, i dati saranno deduplicati. Riceverai 'Ok' in risposta all'inserimento, ma in realtà verrà registrato solo un pacchetto di dati, e non sarà duplicato.

Questo è necessario per chiarezza. Se durante l'inserimento ricevi 'Ok', significa che i tuoi dati sono stati inseriti. Se ricevi un errore da ClickHouse, significa che non sono stati inseriti, e devi ripetere l'inserimento. Ma se durante l'inserimento la connessione viene interrotta, non sai se i dati sono stati inseriti o meno. L'unica opzione è ripetere l'inserimento. Se i dati sono stati realmente inseriti e li reinserisci, c'è deduplicazione dei blocchi. È necessaria per evitare duplicati.

È anche importante come funziona per le viste materializzate. Se i dati sono stati deduplicati durante l'inserimento nella tabella principale, allora non andranno nemmeno nella vista materializzata.

Ora riguardo alla questione. Hai una situazione più complessa, perché stai registrando duplicati di singole righe. Cioè, non è l'intero pacchetto che viene duplicato, ma righe specifiche, e queste si aggregano in background. Infatti, i dati si aggregano nella tabella principale, mentre nella vista materializzata vanno i dati non aggregati, e durante i merge non accade niente con le viste materializzate. Perché la vista materializzata non è nient'altro che un trigger per un insert. Non accade nulla di aggiuntivo con altre operazioni.

E io qui non posso proprio di certo rallegrarti. Si deve solo cercare una soluzione specifica per questo caso. Ad esempio, è possibile fare anche il suo replacement nella vista materializzata, e il metodo di deduplicazione potrebbe funzionare allo stesso modo. Ma purtroppo non sempre. Se è aggregante, non funziona.

Kirill Shvakov: Anche noi abbiamo avuto una certa impalcatura nel nostro tempo. C'era un problema, ci sono le visualizzazioni pubblicitarie e ci sono alcuni dati che possiamo mostrare in tempo reale — sono semplicemente visualizzazioni. Rare volte vengono duplicate, ma se ciò accade, noi le aggregiamo comunque. E ci sono cose che non possono essere duplicate — i clic e tutta questa storia. Ma anche questi li desideravamo mostrare praticamente subito.

Come sono state fatte le viste materializzate? C'erano viste dove viene scritto direttamente — i dati grezzi vengono registrati e scritti nelle viste. A un certo punto i dati non sono molto corretti, si duplicano e così via. E c'è una seconda parte della tabella, dove sembrano assolutamente uguali alle viste materializzate, cioè nella struttura sono completamente identiche. Ogni tanto ricalcoliamo i dati, contiamo i dati senza duplicati e li scriviamo in quelle tabelle.

Siamo passati attraverso l'API — in ClickHouse a mano non funzionerà. E l'API controlla: quando ho la data dell'ultimo inserimento nella tabella, dove i dati sono sicuramente già corretti, calcolati, fa una richiesta a una tabella e all'altra. Da una seleziona fino a un certo intervallo temporale, e dall'altra recupera ciò che non è stato ancora calcolato. E questo funziona, ma non con i mezzi di un solo ClickHouse.

Se hai un'API — per analisti, per utenti — è comunque un'opzione. Puoi sempre contare, sempre ricalcolare. Puoi farlo una volta al giorno o in un altro momento. Scegli tu stesso l'intervallo che non ti serve e non è critico.

In ClickHouse ci sono molti log. Come posso vedere tutto ciò che succede con il server in tempo reale?

In ClickHouse c'è un numero molto elevato di log diversi e questo numero continua a crescere. Nelle nuove versioni alcuni di essi sono attivati per impostazione predefinita, mentre nelle versioni precedenti devono essere attivati durante l'aggiornamento. Tuttavia, ce ne sono sempre di più. Vorrei vedere, alla fine, cosa sta succedendo ora con il mio server, magari su un dashboard riepilogativo.

Non hai nel tuo team ClickHouse, o nei team dei tuoi amici, qualcuno che supporti una certa funzionalità di dashboard già pronte che mostrino questi log come un prodotto già finito? Alla fine, guardare i log in ClickHouse è interessante. Ma sarebbe davvero fantastico se fosse già presentato in forma di dashboard. Mi farebbe piacere.

Ci sono dashboard, anche se non sono standardizzate. Nella nostra azienda circa 60 team utilizzano ClickHouse, e la cosa strana è che molti di loro hanno dashboard che hanno creato da soli, e sono leggermente diverse. Alcuni team utilizzano un'installazione interna di 'Yandex.Cloud'. Lì ci sono alcuni report pronti, anche se non tutti i necessari. Altri hanno i loro.

I miei colleghi di 'Metrics' hanno il loro dashboard in Grafana, mentre io ho il mio sul loro cluster. Lì guardo cose come il cache hit per la cache delle metriche. E ancor più complicato è che utilizziamo strumenti diversi. Il mio dashboard l'ho creato con uno strumento molto vecchio chiamato Graphite-web. È assolutamente brutto. E fino ad ora lo uso, anche se Grafana probabilmente sarebbe più comoda e bella.

L'elemento di base nei dashboard è lo stesso. Si tratta delle metriche di sistema per il cluster: CPU, memoria, disco, rete. Altre metriche includono il numero di richieste simultanee, il numero di merge simultanei, il numero di richieste al secondo, il numero massimo di pezzi per le partizioni delle tabelle MergeTree, il ritardo di replica, la dimensione della coda di replica, il numero di righe inserite al secondo, il numero di blocchi inseriti al secondo. Questo è tutto ciò che deriva dalle metriche e non dai log.

Vladimir Kolobaev: Alexey, vorrei fare qualche correzione. C'è Grafana. Grafana ha una datasource, che è ClickHouse. Ciò significa che posso fare richieste direttamente a ClickHouse da Grafana. In ClickHouse c'è una tabella con i log, ed è identica per tutti. Vorrei quindi poter accedere a questa tabella dei log in Grafana e vedere le richieste che il mio server esegue. Sarebbe fantastico avere un dashboard del genere.

L'ho creato da solo. Ma mi sorge una domanda: se è tutto standardizzato e Grafana è utilizzata da tutti, perché in "Yandex" non c'è un dashboard ufficiale di questo tipo?

Kirill Shvakov: In effetti, la datasource per ClickHouse è attualmente supportata da Altinity. E io voglio semplicemente dare una direzione su dove scavare e chi spingere. Possono chiedere a loro, perché "Yandex" alla fine sviluppa ClickHouse, non la storia attorno a esso. Altinity è l'azienda principale che attualmente promuove ClickHouse. Non lo abbandoneranno, ma lo supporteranno. Perché, in linea di principio, per caricare un dashboard sul sito di Grafana, è sufficiente registrarsi e caricarlo: non ci sono particolari problemi.

Alexey Milovidov: Negli ultimi anni sono state aggiunte molte funzionalità in ClickHouse per il profiling delle richieste. Ci sono metriche per ogni richiesta riguardo all'uso delle risorse. E recentemente è stato aggiunto un profiler delle richieste ancora più a basso livello, per vedere dove la richiesta spende ogni millisecondo. Ma per utilizzare questa funzionalità, devo aprire il client della console e digitare la richiesta che continuo a dimenticare. L'ho salvata da qualche parte, ma continuo a dimenticare dove.

Vorrei uno strumento in cui sia semplicemente indicato: ecco le vostre query pesanti, raggruppate per classi di query. Clicchi su una qualsiasi e mi dicessero che è pesante per questo motivo. Al momento non esiste una tale soluzione. Ed è davvero piuttosto strano che, quando le persone mi chiedono: «Ci sono dashboard pronte per Grafana?», io dica: «Visitate il sito di Grafana, lì c'è la community

Come influenzare i merge in modo che il server non vada in OOM?

Ho una tabella con un'unica partizione, è un ReplacingMergeTree. Scrivo dati in essa da quattro anni. Dovevo fare un alter e rimuovere alcuni dati.

L'ho fatto, e durante l'elaborazione di questa richiesta è stata utilizzata tutta la memoria su tutti i server del cluster e tutti i server del cluster sono andati in OOM. Poi si sono tutti riavviati, hanno iniziato a eseguire il merge della stessa operazione, di quel blocco di dati, e sono di nuovo andati in OOM. Poi si sono rialzati e sono caduti di nuovo. E questa cosa non si fermava.

Poi si è scoperto che era in realtà un bug, che i ragazzi hanno corretto. È davvero fantastico, grazie mille. Ma il malcontento è rimasto. E ora, quando penso che devo fare un certo merge nella tabella, mi viene una domanda: perché non posso in qualche modo influenzare questi merge? Per esempio, limitandoli in base alla quantità di memoria RAM necessaria, o in generale in base alla loro quantità che elaborerà specificamente questa tabella.

Ho una tabella chiamata "Metriche", elaborala per me, per favore, in due thread. Non è necessario generare dieci o cinque merge in parallelo, fallo in due. Penso che in due avrò abbastanza memoria, mentre per dieci potrebbe non bastare. Perché rimane la paura? Perché la tabella cresce, e un giorno mi troverò in una situazione in cui, in linea di principio, non a causa di un bug, ma perché i dati cambieranno in una quantità così grande che semplicemente non avrò sufficiente memoria sul server. E allora il server andrà in OOM durante il merge. Inoltre, posso annullare la mutazione, ma i merge già no.

Sai, durante i merge il server non andrà in OOM, perché durante il merge viene utilizzata solo una piccola quantità di memoria per un intervallo ridotto di dati. Quindi andrà tutto bene indipendentemente dal volume dei dati.

Vladimir Kolobaev: Va bene. Qui c'è il punto: dopo aver effettuato la correzione di un bug, ho scaricato una nuova versione e su un altro tavolo, più piccolo, con molte partizione, ho eseguito un'operazione simile. E durante il merge sul server sono stati consumati circa 100 GB di memoria. Ne avevo 150 occupati, 100 li ha mangiati, e mi è rimasto uno spazio di 50 GB, quindi non sono andato in OOM.

Cosa mi protegge attualmente dall'andare in OOM se sta realmente consumando 100 GB di memoria? Come comportarsi se la memoria finisce durante i merge?

Alexey Milovidov: C'è un problema: il consumo di memoria RAM durante i merge non è limitato. E il secondo problema è che, se un merge è stato programmato, deve essere eseguito perché è registrato nel log di replica. Il log di replica è ciò che serve per portare la replica in uno stato consistente. Se non si eseguono le operazioni manuali che riporteranno indietro questo log di replica, il merge dovrà essere completato, in un modo o nell'altro.

Certo, non sarebbe male avere un limite di memoria che protegga «per ogni evenienza» esattamente da OOM. Non aiuterà il completamento del merge, ricomincerà da capo, raggiungerà qualche soglia, genererà un'eccezione e poi ricomincerà — non uscirà nulla di buono da questo. Ma in linea di principio, introdurre questo limite sarebbe utile.

Come avverrà lo sviluppo di un driver Golang per ClickHouse?

Il driver Golang scritto da Kirill Shvakov è ora ufficialmente supportato dal team di ClickHouse. In repository ClickHouse, ora è grande e reale.

Una piccola nota. C'è un meraviglioso e amatissimo archiviazione di forme normali di ordine infinito: è Vertica. Hanno anche un loro driver python ufficiale, supportato dagli sviluppatori di Vertica. Diverse volte è successo che le versioni dell'archiviazione e le versioni del driver si discostassero notevolmente, e il driver a un certo punto ha smesso di funzionare. E un secondo punto. Il supporto per questo driver ufficiale, mi sembra, viene fornito dal sistema "nipple": scrivi loro un problema, e rimane appeso per sempre.

Ho due domande. Attualmente il driver Golang di Kirill è quasi il modo predefinito per comunicare da Golang con ClickHouse. A meno che qualcuno non comunichi ancora tramite interfaccia http, perché gli piace così. Come avverrà lo sviluppo di questo driver? Si sincronizzerà con eventuali cambiamenti di rottura nell'archiviazione stessa? E qual è l'ordine di esame dei problemi?

Kirill Shvakov: Primo: come funziona tutto burocraticamente. Questo punto non è stato discusso, quindi non ho nulla da rispondere.

Per rispondere alla domanda sui problemi, è necessaria una piccola storia del driver. Ho lavorato in una azienda che aveva molti dati. Era una piattaforma pubblicitaria con un'enorme quantità di eventi da archiviare. E a un certo punto è emerso ClickHouse. Abbiamo caricato i dati lì, e per un certo periodo è andato tutto bene, poi ClickHouse è crollato. A quel punto abbiamo deciso che non ne avevamo bisogno.

Un anno dopo siamo tornati all'idea di utilizzare ClickHouse e dovevamo trovare un modo per scrivere dati lì. Le condizioni erano queste: l'hardware era molto debole, le risorse scarse. Ma abbiamo sempre lavorato in questo modo, quindi abbiamo guardato alla possibilità di utilizzare il protocollo nativo.

Poiché lavoravamo in Go, era chiaro che avevamo bisogno di un driver in Go. L'ho sviluppato praticamente a tempo pieno: era il mio compito di lavoro. Fino a un certo punto lo abbiamo portato avanti, e in effetti nessuno si aspettava che qualcuno al di fuori di noi lo utilizzasse. Poi è arrivato CloudFlare con esattamente lo stesso problema, e per un certo periodo abbiamo lavorato molto a stretto contatto con loro, perché avevano gli stessi obiettivi. Infatti, lo abbiamo fatto sia in ClickHouse stesso che nel driver.

A un certo punto ho semplicemente smesso di occuparmene, perché la mia attività in termini di ClickHouse e il mio lavoro sono cambiati un po'. Pertanto, i problemi non vengono risolti. Di tanto in tanto, nel repository fanno commit persone che hanno bisogno di qualcosa. Allora guardo le richieste di pull e a volte aggiusto qualcosa io stesso, ma succede raramente.

Voglio tornare al driver. Qualche anno fa, quando tutto questo è iniziato, ClickHouse era diverso e con altre possibilità. Ora c'è una comprensione di come rifare il driver affinché sia migliore. Se questo accade, la versione 2 sarà comunque incompatibile a causa delle soluzioni temporanee accumulate.

Non so come organizzare questa cosa. Io stesso non ho molto tempo. Se alcune persone si dedicheranno a sviluppare il driver, potrò aiutarli e dire loro cosa fare. Ma la partecipazione attiva di 'Yandex' nello sviluppo del progetto non è stata ancora discussa.

Alexey Milovidov: In realtà, al momento non c'è alcuna burocrazia riguardo a questi driver. L'unica cosa è che sono stati trasferiti a un'organizzazione ufficiale, quindi questo driver è stato riconosciuto come soluzione ufficiale di default per Go. Ci sono altri driver, ma vanno separatamente.

Internamente non abbiamo alcuno sviluppo per questi driver. La questione è se possiamo assumere una persona separata, non specificamente per questo driver, ma per lo sviluppo di tutti i driver della comunità, oppure se riusciremo a trovare qualcuno dall'esterno.

Il dizionario esterno non si attiva dopo un riavvio con l'impostazione lazy_load attivata. Cosa fare?

Abbiamo attivato l'impostazione lazy_load e dopo il riavvio del server il dizionario non si attiva da solo. Si attiva solo dopo che un utente interagisce con questo dizionario. E alla prima richiesta restituisce un errore. È possibile in qualche modo caricare automaticamente i dizionari tramite ClickHouse, o dobbiamo sempre controllare noi stessi che siano pronti, affinché gli utenti non ricevano errori?

Forse abbiamo una vecchia versione di ClickHouse, quindi il dizionario non si è caricato automaticamente. È possibile?

Innanzitutto, i dizionari possono essere forzatamente caricati tramite la query system reload dictionaries. In secondo luogo, riguardo all'errore — se il dizionario è già stato caricato, le richieste funzioneranno con i dati che sono stati caricati. Se il dizionario non è stato ancora caricato, verrà caricato direttamente al momento della richiesta.

Per i dizionari pesanti non è molto comodo. Ad esempio, è necessario estrarre milioni di righe da MySQL. Qualcuno esegue una semplice select, ma questa select attenderà quelle milioni di righe. Ci sono due soluzioni. La prima è disattivare lazy_load. La seconda è, quando il server si avvia, prima di caricarlo, eseguire system reload dictionary o semplicemente eseguire una query che utilizza il dizionario. Allora il dizionario verrà caricato. È necessario controllare l'accessibilità dei dizionari con l'impostazione lazy_load attivata, perché automaticamente ClickHouse non li carica.

Per l'ultima domanda la risposta è che o la versione è obsoleta, o è necessario fare il debug.

Come gestire il fatto che il comando system reload dictionaries non carica nessuno dei vari dizionari se anche solo uno di essi restituisce un errore?

C'è un'altra domanda riguardo a system reload dictionaries. Abbiamo due dizionari: uno non si carica, l'altro si carica. In tal caso, system reload dictionaries non carica nessun dizionario e bisogna caricarne specificamente uno per nome utilizzando system reload dictionary. È anche questo legato alla versione di ClickHouse?

Voglio darvi una buona notizia. Questo comportamento è cambiato. Quindi, se aggiornate ClickHouse, cambierà anche. Se non vi piace il comportamento attuale system reload dictionaries, aggiornate e speriamo che migliori.

Esiste un modo per configurare le credenziali nel file di configurazione di ClickHouse senza esporle in caso di errori?

La prossima domanda riguarda gli errori legati al dizionario, in particolare le credenziali. Abbiamo configurato le credenziali di connessione nel file di configurazione di ClickHouse per il dizionario, e in caso di errore riceviamo queste credenziali e la password nella risposta.

Abbiamo risolto questo errore spostando le credenziali nel file di configurazione del driver ODBC. C'è un modo per configurare le credenziali nel file di configurazione di ClickHouse, senza esporre queste credenziali in caso di errori?

Qui la soluzione è effettivamente indicare queste credenziali in odbc.ini e nel ClickHouse stesso indicare solo il nome della sorgente dati ODBC. Per le altre sorgenti di dizionari non ci sarà questo — né per il dizionario con MySQL, né per le altre non dovreste vedere la password in caso di messaggi di errore. Per ODBC controllerò anch'io — se esiste, è necessario rimuoverlo.

Bonus: sfondi per Zoom dalle riunioni

Cliccando sull'immagine, per i lettori più instancabili si apriranno sfondi bonus delle riunioni. Spegniamo il fuoco insieme ai mascot delle tecnologie Avito, ci consultiamo con i colleghi nella sala dell'amministratore di sistema o nel club di computer old school e facciamo il daily sotto il ponte sullo sfondo dei graffiti.

ClickHouse per utenti avanzati in domande e risposte

Fonte: habr.com

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