Ad aprile, gli ingegneri di Avito si sono incontrati in un incontro online con il principale sviluppatore di ClickHouse, Alexey Milovidov, e Kirill Shvakov, sviluppatore Golang della società Integros. Hanno discusso di come utilizziamo il sistema di gestione dei database e quali difficoltà stiamo affrontando.
Sulla base dell'incontro, abbiamo raccolto un articolo con le risposte degli esperti alle nostre domande e a quelle degli spettatori riguardo a backup, resharding dei dati, dizionari esterni, driver Golang e aggiornamenti delle versioni di ClickHouse. Può risultare utile per gli sviluppatori che lavorano attivamente con il DBMS di Yandex e sono interessati al suo presente e futuro. Per default, le risposte sono di Alexey Milovidov, salvo diversa indicazione.
Attenzione, ci sono molte informazioni sotto il tag. Speriamo che il contenuto con le domande vi aiuti a orientarvi.

Contenuto
Se non vuoi leggere il testo, puoi guardare la registrazione dell'incontro . I timestamp sono nel primo commento sotto il video.
ClickHouse si aggiorna continuamente, ma i nostri dati no. Cosa possiamo fare al riguardo?
ClickHouse si aggiorna continuamente, ma i nostri dati, che sono stati elaborati con optimize final, non si aggiornano e rimangono nel backup.
Supponiamo che si sia verificato un problema e che i dati siano stati persi. Abbiamo deciso di ripristinarli, 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?
La situazione in cui hai ripristinato dati da un backup in un vecchio formato e non possono connettersi alla nuova versione, non è possibile. Ci assicuriamo che il formato dei dati in ClickHouse rimanga sempre retrocompatibile. Questo è molto più importante della retrocompatibilità funzionale, se è cambiato il comportamento di qualche funzione raramente utilizzata. I dati conservati su disco, la nuova versione di ClickHouse deve sempre essere in grado di leggerli. Questa è una regola.
Quali sono le migliori pratiche attualmente disponibili per il backup dei dati da ClickHouse?
Come eseguire i backup considerando che abbiamo operazioni di ottimizzazione finale, un'enorme base di dati di terabyte e dati che vengono aggiornati, supponiamo, negli ultimi tre giorni, senza ulteriori procedure su di essi?
Possiamo realizzare una soluzione personalizzata e scrivere sul nostro cluster: raccogli questi backup in questo modo. O forse non c'è bisogno di reinventare la ruota, e la soluzione è già stata trovata?
Iniziamo con le migliori pratiche. I miei colleghi consigliano sempre in risposta alle domande sui backup di ricordare il servizio 'Yandex.Cloud', dove questo compito è già stato risolto. Quindi, usatelo se avete la possibilità.
Non esiste una soluzione completa, completamente integrata in ClickHouse, per i backup. Ci sono alcuni modelli che possono essere utilizzati. Per ottenere una soluzione completa, dovrai fare un po' di lavoro manuale o creare wrapper sotto forma di script.
Comincerò con le soluzioni più semplici e concluderò con le più complesse, a seconda della quantità di dati e della dimensione del cluster. Maggiore è il cluster, più complessa diventa la soluzione.
Se la tabella con i dati occupa solo pochi gigabyte, il backup può essere eseguito in questo modo:
- Salvare la definizione delle tabelle, cioè i metadati — show create table.
- Effettuare un dump utilizzando il client ClickHouse — select * from table in un file. Per impostazione predefinita, otterrai un file in formato TabSeparated. Se desideri qualcosa di più efficiente, puoi utilizzare il formato Native.
Se il volume dei dati è maggiore, il backup richiederà più tempo e spazio. Questo è chiamato backup logico, che non è legato al formato dei dati di ClickHouse. Se disponibile, in casi estremi potrai prendere il backup e caricarlo in MySQL per il ripristino.
Per casi più avanzati, ClickHouse offre la possibilità di creare uno snapshot delle partizioni nel file system locale. Questa funzionalità è disponibile tramite una query alter table freeze partition. O semplicemente alter table freeze — è uno snapshot dell'intera tabella.
Lo snapshot sarà creato in modo consistente per una sola tabella su una singola shard, quindi non è possibile creare uno snapshot consistente dell'intero cluster in questo modo. Tuttavia, per la maggior parte delle attività, tale necessità non sussiste, ed è sufficiente eseguire la query su ogni shard per ottenere uno snapshot consistente. È realizzato tramite hard link e quindi non occupa spazio aggiuntivo. Si copia poi questo snapshot sul server di backup o nello storage utilizzato per i backup.
Ripristinare un backup simile è abbastanza semplice. Prima di tutto, si creano le tabelle in base alle definizioni esistenti delle tabelle. Poi si copiano gli snapshot delle partizioni salvate in Directory-Detached per queste tabelle e si esegue la query attach partition. Questa soluzione è perfettamente adatta per volumi di dati molto grandi.
A volte è necessario qualcosa di ancora più potente: nei casi in cui hai decine o addirittura centinaia di terabyte su ogni server e centinaia di server. Qui c'è una soluzione che ho preso in prestito dai colleghi di «Yandex.Metrica». Non lo raccomanderei a tutti: leggi e decidi tu stesso se fa per te.
Prima di tutto, è necessario creare diversi server con ampi scaffali di archiviazione. Successivamente, su questi server, è possibile avviare diversi server ClickHouse e configurarli in modo che funzionino come un'altra replica per gli stessi shard. Inoltre, utilizzare su questi server un file system o uno strumento che consente di creare snapshot. Ci sono due opzioni. La prima opzione è gli snapshot LVM, la seconda è ZFS su Linux.
Dopo di ciò, è necessario creare ogni giorno uno snapshot, occupando un certo spazio. Naturalmente, se i dati cambiano, nel tempo lo spazio occupato aumenterà. Questo snapshot può essere recuperato in qualsiasi momento per ripristinare i dati, una soluzione piuttosto curiosa. Inoltre, è necessario limitare queste repliche nella configurazione affinché non cerchino di diventare leader.
Sarà possibile organizzare un ritardo controllato delle repliche nei cluster?
Quest'anno prevedete di realizzare i volumi in ClickHouse. Sarà possibile organizzare un ritardo controllato delle repliche in essi? Vorremmo usare questo per proteggerci da scenari negativi con alterazioni e altre modifiche.
È possibile effettuare dei rollback per gli alter? Ad esempio, nella volume esistente, si potrebbe dire di applicare le modifiche fino a un certo punto, e da quel momento smettere di applicarle?
Se nel nostro cluster arriva un comando e lo danneggia, abbiamo una replica condizionale con un ritardo di un’ora, dove possiamo dire di usarla in questo momento, ma senza applicare le ultime dieci minuti di modifiche?
Iniziamo con il ritardo controllato delle repliche. Questa è stata una richiesta da parte degli utenti, e abbiamo creato un issue su GitHub con la richiesta: "Se a qualcuno interessa, mettete un like, mettete un cuoricino". Nessuno ha messo like, e l’issue è stato chiuso. Tuttavia, già adesso è possibile ottenere tale funzione configurando ClickHouse. Solo a partire dalla versione 20.3.
ClickHouse esegue continuamente fusioni dei dati in background — merge. Quando una fusione è completata, un determinato insieme di parti di dati viene sostituito con un pezzo più grande. Nel frattempo, le parti di dati più vecchie rimangono su disco per un certo periodo di tempo.
Innanzitutto, esse continuano a essere conservate finché ci sono query select che le utilizzano, per garantire un funzionamento non bloccante. Le query select possono leggere tranquillamente dalle vecchie parti.
In secondo luogo, esiste anche una soglia temporale — le vecchie parti di dati rimangono su disco per otto minuti. Questi otto minuti possono essere configurati e aumentati fino a un giorno. Questo influisce sullo spazio su disco: a seconda del flusso di dati, risulta che nell'ultimo giorno i dati non solo raddoppieranno, ma potrebbero aumentarne anche di cinque volte. Ma in caso di un problema serio, potete fermare il server ClickHouse e risolvere tutto.
Ora sorge la domanda, come protegge questo dagli alter? Qui vale la pena guardare più a fondo, perché nelle versioni precedenti di ClickHouse, l'alter funzionava in modo tale da cambiare direttamente le parti. C'è un pezzo di dati con alcuni file, e per esempio facciamo... alter drop column. Questo colonna viene fisicamente rimossa da tutti i chunk.
Ma a partire dalla versione 20.3, il meccanismo degli alter è stato completamente cambiato, e ora i chunk di dati sono sempre immutabili. Non cambiano mai: gli alter ora funzionano in modo simile ai merge. Invece di modificare il chunk in loco, creiamo uno nuovo. Nel nuovo chunk, i file che non sono cambiati diventano hard link e, se abbiamo eliminato una colonna, semplicemente non sarà presente nel nuovo chunk. Il vecchio chunk verrà rimosso di default dopo otto minuti, e qui si possono regolare le impostazioni di cui si è parlato sopra.
Lo stesso vale per gli alter di tipo mutazione. Quando si fa alter delete o alter update, non modifica il chunk, ma crea uno nuovo. Poi elimina il vecchio.
Cosa fare se la struttura della tabella è cambiata?
Come ripristinare un backup fatto con un vecchio schema? E la seconda domanda riguarda il caso degli snapshot e dei sistemi di file. Va bene qui Btrfs invece di ZFS su Linux LVM?
Se si fa attach partition Se hai partizioni con una struttura diversa, ClickHouse ti comunicherà 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 l'attach, quindi esegui la query alter. Successivamente, puoi copiare o spostare questi dati e fare nuovamente l'attach, o utilizzare la query. alter table move partition.
Ora la seconda domanda è: è possibile utilizzare Btrfs? Inizialmente, se hai LVM, basta avere degli snapshot LVM, e il file system può essere anche ext4, non fa differenza. Per quanto riguarda Btrfs, tutto dipende dalla tua esperienza con la sua gestione. È un file system maturo, ma ci sono comunque alcuni dubbi su come si comporterà nella pratica in scenari specifici. Non consiglierei di usarlo se non hai Btrfs in produzione.
Quali sono le migliori pratiche attuali per il resharding dei dati?
La questione del ri-sharding è complessa e sfaccettata. Qui è possibile rispondere con diverse opzioni. Si può considerare da un lato che in ClickHouse non esiste una funzionalità di ri-sharding integrata. Ma temo che questa risposta non soddisfi nessuno. Quindi, si può considerare dall'altro lato che in ClickHouse ci sono molti modi per ri-shardare i dati.
Se lo spazio sul cluster sta per terminare o non riesce a gestire il carico, è necessario aggiungere nuovi server. Ma questi server sono vuoti per impostazione predefinita, non ci sono dati su di essi e non c'è carico. È necessario spostare i dati in modo che siano distribuiti in modo uniforme nel nuovo cluster, ampliato nelle dimensioni.
Il primo modo per farlo è copiare alcune partizioni sui nuovi server utilizzando la query alter table fetch partition. Ad esempio, se avevate partizioni mensili, potete prendere il primo mese del 2017 e copiarlo su un nuovo server, poi copiate il terzo mese su un altro nuovo server. E continuate finché non diventa più o meno uniforme.
Il trasferimento può essere eseguito solo per quelle partizioni che non cambiano durante la scrittura. Per le partizioni nuove, sarà necessario disabilitare la scrittura, poiché il loro trasferimento non è atomico. Altrimenti, si possono ottenere duplicati o mancanze nei dati. Tuttavia, questo metodo è pratico e funziona abbastanza efficacemente. Le partizioni già pronte vengono trasmesse in rete, il che significa che i dati non vengono decompressi né ricodificati.
Questo metodo ha un inconveniente, che dipende dallo schema di sharding: se vi siete basati su questo schema di sharding e quale fosse la vostra chiave di sharding. Nel vostro esempio, per il caso delle metriche, la chiave di sharding è l'hash del percorso. Quando si esegue un select su una tabella distribuita, la richiesta viene inviata immediatamente a tutti i shard del cluster e recupera i dati da lì.
Questo significa che, di fatto, per voi non ha importanza quali dati siano presenti su quale shard. L'importante è che i dati lungo un determinato percorso si trovino su uno stesso shard, mentre non è rilevante su quale specifico shard siano. In questo caso, il trasferimento delle partizioni già pronte è perfettamente adeguato, perché nelle query select, sia prima che dopo il ribilanciamento, la struttura dei valori non ha particolare importanza: riceverete dati completi.
Tuttavia, ci sono anche casi più complessi. Se a livello di logica applicativa ipotizzate uno schema di sharding specifico, in cui questo cliente è posizionato su un certo shard e la query può essere inviata direttamente lì, piuttosto che a una tabella Distribuita. Oppure se state utilizzando una versione relativamente recente di ClickHouse e avete attivato l'impostazione optimize skip unused shards. In questo caso, durante la query select, l'espressione nella sezione where verrà analizzata, e sarà determinato su quali shard è necessario andare in base allo schema di sharding. Questo funziona a condizione che i dati siano effettivamente distribuiti secondo questo schema di sharding. Se li avete riorganizzati manualmente, la corrispondenza potrebbe cambiare.
Quindi, questo è il metodo numero uno. Attendo la tua risposta, se è adatto o se andiamo avanti.
Vladimir Kolobayev, lead system administrator di Avito: Alexey, il metodo che hai menzionato non si adatta molto bene quando bisogna distribuire il carico anche per la lettura. Possiamo prendere una partizione mensile e possiamo spostare il mese precedente su un'altra nodo, ma quando arriverà la richiesta per questi dati, caricheremo solo quella. Sarebbe preferibile caricare tutto il cluster, perché altrimenti, per un certo periodo, tutto il carico di lettura sarà gestito solo da due shard.
Alexey Milovidov: La risposta qui è strana — sì, è male, ma potrebbe funzionare. Spiegherò come. Bisogna considerare lo scenario di carico che segue i tuoi dati. Se si tratta di dati di monitoraggio, si può dire con quasi certezza che la stragrande maggioranza delle richieste va verso i dati più recenti.
Hai impostato nuovi server, trasferito le vecchie partizioni, ma hai anche modificato 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 degli ultimi cinque minuti caricheranno uniformemente il cluster, e dopo un giorno, le richieste delle ultime 24 ore caricheranno uniformemente il cluster. Sfortunatamente, le richieste per il mese precedente andranno soltanto su una parte dei server del cluster.
Ma spesso non avrai richieste specifiche per febbraio 2019. È probabile che se le richieste arrivano nel 2019, siano per l'intero anno 2019 — per un lungo intervallo di tempo, e non per un breve periodo. E anche tali richieste potranno caricare uniformemente il cluster. Tuttavia, il tuo osservazione è assolutamente corretta, si tratta di una soluzione ad hoc che non distribuisce i dati completamente in modo uniforme.
Ho ancora alcuni punti da considerare per rispondere alla domanda. Uno di questi riguarda come impostare inizialmente il schema di sharding in modo che ci sia meno disagio nel processo di reshaping. Questo potrebbe non essere sempre possibile.
Ad esempio, hai dati di monitoraggio. I dati di monitoraggio crescono per tre motivi. Il primo è l'accumulo di dati storici. Il secondo è l'aumento del traffico. E il terzo è l'incremento del numero di elementi soggetti a monitoraggio. Emergence di nuovi microservizi e metriche da conservare.
Probabilmente, il più grande aumento è legato proprio alla terza ragione, cioè l'aumento dell'uso del monitoraggio. In questo caso, vale la pena esaminare la natura del carico, quali sono le query principali per select. Le query principali per select andranno probabilmente su un sottoinsieme di alcune metriche.
Ad esempio, l'uso della CPU su alcuni server da parte di un servizio. Quindi, c'è un certo sottoinsieme di chiavi tramite il quale recuperi questi dati. E la query per questi dati è probabilmente abbastanza semplice e viene eseguita in decine di millisecondi. Viene utilizzata per i servizi di monitoraggio, per i dashboard. Spero di capire correttamente.
Vladimir Kolobayev: Il fatto è che spesso facciamo riferimento a dati storici, poiché confrontiamo in tempo reale la situazione attuale con quella storica. Per noi è importante avere accesso rapido a un grande volume di dati, e ClickHouse gestisce questo aspetto in modo eccellente.
Hai assolutamente ragione, la maggior parte delle richieste di lettura le riscontriamo nell'ultimo giorno, come qualsiasi sistema di monitoraggio. Tuttavia, anche i dati storici subiscono un carico considerevole. Questo è principalmente dovuto al sistema di allerta, che ogni trenta secondi invia a ClickHouse la richiesta: «Dammi i dati delle ultime sei settimane. Ora costruiscimi una media mobile da questi e confrontiamo il valore attuale con quello storico».
Vorrei dire che per tali richieste molto fresche esiste anche un altro piccolo tavolo, in cui conserviamo dati solo per due giorni, e le richieste principali vengono elaborate lì. Nella grande tabella sharding inviamo solo grandi richieste storiche.
Alexey Milovidov: Sfortunatamente, per il tuo scenario non è molto applicabile, ma ti illustrerò due cattivi e complessi schemi di sharding da evitare, che però vengono utilizzati nel servizio dei miei amici.
C'è un cluster principale con eventi da "Yandex.Metrica". Gli eventi sono visualizzazioni di pagina, clic e transizioni. La maggior parte delle richieste è indirizzata a un sito web specifico. Apri il servizio "Yandex.Metrica", hai un sito — avito.ru, vai al rapporto e viene effettuata una richiesta per il tuo sito.
Ma ci sono anche altre richieste — analitiche e globali, fatte dai analisti interni. A scanso di equivoci, sottolineo che gli analisti interni effettuano richieste solo sui servizi di "Yandex". Tuttavia, anche i servizi di "Yandex" occupano una quota 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 anche con un solo contatore, oltre a gestire anche le richieste globali? La complessità sta anche nel fatto che il numero di richieste in ClickHouse per il cluster "Metriche" è di alcune migliaia al secondo. Tali richieste non banali, ad esempio, non possono essere gestite da un singolo server ClickHouse, che non riesce a reggere il carico di queste migliaia di richieste al secondo.
La dimensione del cluster è di seicento e oltre server. Se si applica semplicemente una tabella distribuita su questo cluster e si inviano lì diverse migliaia di richieste, sarà ancora peggio rispetto all'invio a un singolo server. D'altra parte, l'opzione in cui i dati sono distribuiti uniformemente, e noi andiamo a richiederli da tutti i server, la scartiamo subito.
Esiste un'opzione diametralmente opposta. Immaginate di sharding i dati per sito web, e che una richiesta per un determinato sito vada su un solo shard. Ora, il cluster potrebbe gestire fino a diecimila richieste al secondo, ma su uno shard una richiesta particolare potrebbe risultare eccessivamente 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. Gestirlo su un solo shard sarebbe una follia.
Pertanto, lo schema di sharding è progettato in modo più astuto. L'intero cluster è suddiviso in un certo numero di mini-cluster che chiamiamo strati. All'interno di ciascun mini-cluster ci sono da una dozzina a diverse dozzine di shard. In totale ci sono trenta nove di questi mini-cluster.
Come si scala tutto questo? Il numero di cluster rimane invariato: come erano trentanove alcuni anni fa, così è rimasto. Ma all'interno di ciascuno di essi aumentiamo progressivamente il numero di shard man mano che accumuliamo dati. E la strategia di sharding in generale è la seguente: la suddivisione in questi cluster avviene per siti web, e per capire quale sito è su quale cluster, utilizziamo una metabase separata in MySQL. Un sito—su un cluster. E al suo interno 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, la strategia di sharding cambia, continuiamo a suddividere, ma in base al resto della divisione per un altro numero. Questo significa che un visitatore è effettivamente posizionato su più server, e su questo non si può fare affidamento. Questo è stato fatto esclusivamente per garantire una migliore compressione dei dati. E quando facciamo richieste, ci rivolgiamo alla tabella Distribuita, che guarda al cluster e si connette a decine di server. Una schematizzazione piuttosto ingarbugliata.
Ma la mia storia sarebbe incompleta se non dicessi che abbiamo abbandonato questo schema. Nello schema nuovo, abbiamo apportato tutte le modifiche e abbiamo copiato tutti i dati utilizzando il clickhouse-copier.
Nel nuovo schema, tutti i siti sono divisi in due categorie: grandi e piccoli. Non so come sia stata stabilita la soglia, ma alla fine è risultato che i siti grandi si registrano su un solo cluster, con 120 shard e tre repliche per ognuno, cioè 360 server. E lo schema di sharding è tale che qualsiasi richiesta va immediatamente a tutti gli shard. Se adesso aprite qualsiasi pagina di report per avito.ru in 'Yandex.Metrica', la richiesta verrà inviata a 120 server. Ci sono pochi siti grandi nel Runet. E il numero di richieste non è mille al secondo, ma persino meno di cento. Tutto questo viene gestito tranquillamente dalla tabella Distributed, che ogni server elabora con 120 repliche.
Il secondo cluster è per i siti piccoli. 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 parlarne?
Voglio dire subito che questa soluzione è più ingombrante e leggermente meno performante. Il vantaggio è che distribuisce completamente i dati secondo lo schema che indicherai. Tuttavia, lo svantaggio dell'utilità è che non effettua il resharding. Essa copia i dati da un cluster a un altro.
Ciò significa che per il suo funzionamento è necessario avere due cluster. Possono trovarsi sugli stessi server, ma, tuttavia, i dati non verranno spostati in modo incrementale, bensì verranno copiati.
Per esempio, c'erano quattro server e ora sono otto. Crei una nuova tabella Distributed su tutti i server, nuove tabelle locali e avvii clickhouse-copier, specificando in esso lo schema operativo, in modo che legga da lì, accetti il nuovo schema di shard e vi distribuisca i dati. E avrai bisogno di 1,5 volte più spazio sui vecchi server di quello che hai attualmente, poiché i dati vecchi devono rimanere su di essi, e sopra arriverà metà di questi stessi dati. Se hai pensato in anticipo che i dati devono essere resharded e c'è spazio, allora questo metodo è adatto.
Come funziona internamente clickhouse-copier? Divide tutto il lavoro in un insieme di compiti per l'elaborazione di una singola partizione di una tabella su un singolo 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 singola partizione è semplicemente un insert select. I dati vengono letti, decompressi, ristrutturati, poi compressi di nuovo, scritti da qualche parte e riordinati. Si tratta di una soluzione più complessa.
Avevate un prototipo chiamato resharding. Che fine ha fatto?
Nel 2017 avevate già un progetto pilota chiamato resharding. C'è anche un'opzione in ClickHouse. Mi pare di capire che non ha avuto successo. Potete raccontare perché è andata così? Sembra essere piuttosto rilevante.
Il problema principale è che, quando è necessario ripartizionare i dati in loco, è 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 manifestati immediatamente nella pratica come qualcosa di molto semplice: nulla funziona.
È possibile unire tutte le parti dei dati in un unico blocco prima di spostarle su dischi lenti?
Domanda sul TTL con l'opzione di spostamento su disco lento nel contesto delle fusioni. C'è un modo, oltre a utilizzare cron, per unire tutte le parti in una sola prima di spostarle su dischi lenti?
La risposta alla domanda se è possibile unire automaticamente tutti i pezzi in uno prima del loro trasferimento è no. Non vedo la necessità di farlo. Non è obbligatorio unire tutte le parti in una sola, basta fare affidamento sul fatto che saranno trasferite automaticamente su dischi lenti.
Abbiamo due criteri per il trasferimento. Il primo è in base all'utilizzo. Se il livello corrente di archiviazione ha meno di una certa percentuale di spazio libero, selezioniamo un segmento e lo trasferiamo su un'archiviazione più lenta. Più precisamente, non più lenta, ma la successiva — come configurato.
Il secondo criterio è basato sulla dimensione. Riguarda il trasferimento di segmenti di grandi dimensioni. Puoi impostare una soglia per lo spazio libero sul disco veloce e i dati verranno trasferiti automaticamente.
Come si esegue l'aggiornamento a nuove versioni di ClickHouse se non è possibile testare la compatibilità in anticipo?
Questo argomento viene discusso regolarmente tenendo conto delle diverse versioni, e comunque. 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 a nuove versioni senza avere la possibilità di controllare in anticipo la compatibilità in un ambiente di test?
Ci sono alcune 'regole d'oro'. La prima è . È ampio, ma ci sono punti specifici riguardanti le modifiche incompatibili. Non bisogna considerare questi punti come un segnale di allerta. Di solito si tratta di piccole incompatibilità legate a funzionalità marginali che, molto probabilmente, non stai utilizzando.
In secondo luogo, se non hai la possibilità di verificare la compatibilità in un ambiente di test e desideri aggiornare direttamente in produzione, il consiglio è di non farlo. Prima crea un ambiente di test e verifica. Se non hai un ambiente di prova, probabilmente la tua azienda non è molto grande, quindi puoi copiare parte dei dati sul tuo laptop e verificare che tutto funzioni correttamente. Puoi anche avviare diverse repliche localmente sul tuo computer. Oppure puoi creare una nuova versione altrove e caricare parte dei dati, creando così un ambiente di test improvvisato.
Un altro principio è di non aggiornare per una settimana dopo il rilascio della versione, a causa della risoluzione dei bug in produzione e dei successivi rapidi fix. Esploriamo la numerazione delle versioni di ClickHouse per non confonderci.
È disponibile la versione 20.3.4. Il numero 20 indica l'anno di rilascio—2020. Dal punto di vista di ciò che c'è dentro, questo non ha alcun significato, quindi non ci concentreremo su di esso. Passiamo a 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 l'abbiamo fatto quattro volte.
Non bisogna pensare che sia qualcosa di terribile. Di solito, un utente può installare l'ultima versione e funzionerà senza alcun problema di uptime per un anno. Ma immaginate che in qualche funzionalità per l'elaborazione di bitmap, aggiunta dai nostri compagni cinesi, il server si blocchi quando vengono forniti argomenti errati. Dobbiamo risolverlo. Rilasceremo una nuova patch-version, e ClickHouse diventerà più stabile.
Se hai ClickHouse in produzione e viene rilasciata una nuova versione con funzionalità aggiuntive — ad esempio, la 20.4.1 — non affrettarti a installarla in produzione il primo giorno. A cosa serve realmente? Se non stai già utilizzando ClickHouse, puoi installarla, e probabilmente andrà tutto bene. Ma se ClickHouse funziona già stabilmente, segui i patch e gli aggiornamenti — quali problemi stiamo risolvendo.
Kirill Shvakov: Voglio aggiungere qualcosa sulle ambienti di test. Tutti hanno molta paura degli ambienti di test e, per qualche motivo, pensano che, se hai un grande cluster ClickHouse, anche l'ambiente di test debba essere su larga scala, o almeno dieci volte più piccolo. Non è affatto così.
Posso parlare per esperienza personale. Ho un progetto in cui c'è ClickHouse. Il nostro ambiente di test per lui è una piccola macchina virtuale in Hetzner che costa venti euro, dove tutto è stato completamente distribuito. Per fare questo, abbiamo un'automazione completa in Ansible, quindi, in teoria, non c'è differenza se distribuiamo su server fisici o semplicemente in macchine virtuali.
Cosa si può fare? Sarebbe utile fornire nella documentazione di ClickHouse un esempio su come implementare un piccolo cluster — in Docker, in LXC, e forse creare un playbook Ansible, poiché ogni persona ha le proprie modalità di deploy. Questo semplificherebbe molto. Quando riesci a configurare un cluster in cinque minuti, è molto più facile cercare di capire come funziona. È decisamente più comodo, perché andare in produzione con una versione che non hai testato è un vicolo cieco. A volte funziona, a volte no. Pertanto, fare affidamento sul successo è rischioso.
Maksim Kotjakov, senior backend engineer di Avito: Aggiungo qualche dettaglio sugli ambienti di test derivati dai problemi delle grandi aziende. Abbiamo un cluster di accettazione ClickHouse completo, che è una copia esatta delle schemi dei dati e delle configurazioni presenti in produzione. Questo cluster è implementato in contenitori piuttosto ridotti con risorse minime. Scriviamo lì una certa percentuale dei dati di produzione, grazie anche alla possibilità di replicare il flusso in Kafka. Tutto è sincronizzato e scalato — sia in termini di capacità che di flusso, e, in teoria, con tutte le altre condizioni uguali, dovrebbe comportarsi come la produzione secondo le metriche. Tutto ciò che è potenzialmente esplosivo passa prima su questa piattaforma e riposa lì per alcuni giorni fino a quando è pronto. Ma naturalmente, questa soluzione è costosa, pesante e con costi di supporto non trascurabili.
Alexey Milovidov: Vi racconterò cosa rappresenta l'ambiente di test dei nostri amici di «Yandex.Metrica». Un cluster era composto da oltre 600 server, un altro da 360, e c'è anche un terzo cluster insieme ad altri. L'ambiente di test per uno di essi consiste semplicemente in due shard con due repliche ciascuno. Perché due shard? Per non averne solo uno. E anche avere le repliche. È solo un numero minimo di risorse che possiamo permetterci.
Questo ambiente di test consente di verificare la funzionalità delle richieste e se ci sia stato qualche grande malfunzionamento. Ma spesso sorgono problemi di natura completamente diversa, quando tutto funziona, ma ci sono piccoli cambiamenti nel carico.
Faccio un esempio. Abbiamo deciso di installare una nuova versione di ClickHouse. Essa è stata resa disponibile nell'ambiente di test, sono stati eseguiti test automatizzati in «Yandex.Metrica» che confrontano i dati della vecchia versione con quelli della nuova, eseguendo l'intero processo. E naturalmente, i test verdi del nostro CI. Altrimenti non avremmo nemmeno proposto questa versione.
Tutto procede bene. Iniziamo a mettere in produzione. Ricevo un messaggio che indica un aumento del carico nei grafici. Torniamo alla versione precedente. Controllo il grafico e vedo che il carico è effettivamente aumentato di diversi fattori durante il rollout e poi è tornato a diminuire una volta eseguito. Poi abbiamo iniziato a fare il rollback della versione. E il carico è aumentato di nuovo e poi è diminuito di nuovo allo stesso modo. Quindi la conclusione è che il carico è aumentato a causa del rilascio, non c'è nulla di sorprendente.
In seguito è stato difficile convincere i colleghi a installare la nuova versione. Dico: «Va tutto bene, procedete. Incrociate le dita, funzionerà. Adesso il carico è aumentato nei grafici, ma va tutto bene. Tenete duro». In generale, abbiamo fatto così, e tutti — la versione è stata rilasciata in produzione. Ma quasi a ogni rilascio si presentano problemi simili.
Kill query dovrebbe terminare le query, ma non lo fa. Perché?
Un utente, un analista, è venuto da me e ha creato una richiesta che ha messo in crisi il mio cluster ClickHouse. Una qualche nodo o l'intero cluster, a seconda della replica o dello shard in cui la richiesta è stata ricevuta. Vedo che tutte le risorse CPU su questo server sono al massimo, tutto è rosso. Tuttavia, ClickHouse risponde comunque alle richieste. E io scrivo: «Per favore, mostrami la lista dei processi, quale richiesta ha generato questo caos».
Trovo questa richiesta e gli ordino di kill. E vedo che non succede nulla. Il mio server è al massimo, ClickHouse continua a darmi qualche comando, mostrando che il server è vivo e tutto va bene. Ma ho una degradazione in tutte le richieste degli utenti, inizia la degradazione nella scrittura in ClickHouse, e il mio kill query non si attua. Perché? Pensavo che il kill query dovesse annullare le richieste, ma non succede.
Ora avrà una risposta piuttosto strana. Il fatto è che il kill query non annulla le richieste.
Il flag 'voglio che questa query venga uccisa' viene impostato in modo che la query, durante l'elaborazione di ciascun blocco, controlli questo flag. Se è attivo, la query smette di funzionare. Quindi, nessuno uccide la query; essa stessa deve verificare e fermarsi. Questo deve funzionare in tutti i casi in cui la query è in fase di elaborazione dei blocchi dati. Elaborerà il successivo blocco di dati, controllerà il flag e si fermerà.
Questo non funziona nei casi in cui la query è bloccata su qualche operazione. Tuttavia, probabilmente non è il tuo caso, poiché, 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 succedere; è un bug. E l'unica cosa che posso consigliare è di aggiornare ClickHouse.
Come calcolare il tempo di risposta con un carico in lettura?
C'è una tabella che memorizza aggregati per item - vari contatori. Il numero di righe è di circa cento milioni. È possibile aspettarsi un tempo di risposta prevedibile se si inviano 1K RPS per 1K item?
Secondo il contesto, si tratta del carico di lettura, perché non ci sono problemi per la scrittura — si possono inserire mille, cento mila, e a volte anche diversi milioni di righe.
Le query di lettura possono variare notevolmente. In select 1, ClickHouse può eseguire circa decine di migliaia di query al secondo, quindi anche le query su una singola chiave richiederanno alcune risorse. Queste query puntuali sono più complesse rispetto a quelle in banche dati key-value, poiché per ogni lettura è necessario leggere un blocco di dati tramite l'indice. L'indice non si riferisce a ogni singolo record, ma a ogni intervallo. Questo significa che è necessario leggere l'intero intervallo — che di default è di 8192 righe. Inoltre, sarà necessario decomprimere un blocco di dati compresso di 64 Kb fino a 1 Mb. Di solito, queste query puntuali richiedono da pochi millisecondi. Ma questa è l'opzione più semplice.
Proviamo a fare un semplice calcolo. Se si moltiplicano alcuni millisecondi per mille, si ottengono alcuni secondi. Come se non fosse possibile gestire mille richieste al secondo, ma in realtà lo è, perché abbiamo diversi core del processore. Quindi in linea di principio ClickHouse può talvolta gestire 1000 RPS, ma per richieste brevi, quelle puntuali.
Se è necessario scalare un cluster ClickHouse in base al numero di richieste semplici, consiglio di fare 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, il che è del tutto fattibile, tre repliche gestiranno millecinquecento.
A volte, ovviamente, è possibile anche ottimizzare ClickHouse per il massimo numero di letture puntuali. Cosa è necessario fare? Primo: ridurre la granularità dell'indice. Tuttavia, dovresti ridurla non fino a uno, ma calcolando 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, allora come granularità si può impostare 64.
È possibile ridurre la dimensione del blocco compresso. Ci sono impostazioni per questo. dimensione minima del blocco compresso, dimensione massima del blocco compresso. Possono essere ridotti, rielaborati i dati, così le richieste puntuali saranno più veloci. Tuttavia, ClickHouse non è un database key-value. Un grande numero di richieste piccole è un antipattern di carico.
Kirill Shvakov: Ti darò un consiglio nel caso ci siano normali contatori. Questa è una situazione abbastanza standard in cui ClickHouse memorizza un contatore. Ho un utente, proviene da un certo paese, e c'è un terzo campo, e bisogna aumentare qualcosa in modo incrementale. Prendi MySQL, crea una chiave unica: in MySQL è una chiave duplicata, mentre in PostgreSQL è un conflitto — e aggiungi con il segno più. Questo funzionerà molto meglio.
Quando hai pochi dati, non ha molto senso utilizzare ClickHouse. Ci sono database normali che gestiscono bene questa situazione.
Cosa sintonizzare in ClickHouse per avere più dati nella cache?
Immagina una 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 sintonizzare per avere più dati nella cache e, di conseguenza, meno accessi al disco?
In genere, la cache della pagina del sistema operativo gestisce bene questo compito. Se apri semplicemente il top, guarda là cached o free — è scritto anche quanto è stato memorizzato nella cache — puoi notare che tutta la memoria libera è utilizzata per la cache. E questi dati, durante la lettura, saranno letti non dal disco, ma dalla RAM. Posso dire che la cache viene utilizzata in modo efficace, perché vengono memorizzati nella cache dati compressi.
Tuttavia, se desideri accelerare ulteriormente alcune richieste semplici, c'è la possibilità di attivare all'interno di ClickHouse la cache per i dati non compressi. Questo è chiamato uncompressed cache. Nel file di configurazione config.xml imposti la dimensione della cache non compressa al valore desiderato — ti consiglio di non oltrepassare la metà della RAM libera, perché il resto sarà utilizzato per la cache della pagina.
Inoltre, ci sono due impostazioni a livello di query. La prima impostazione è use uncompressed cache — include il suo utilizzo. Si consiglia di attivarlo per tutte le query tranne quelle pesanti, che possono leggere tutti i dati e svuotare questa cache. E la seconda impostazione è qualcosa come il numero massimo di righe per l'utilizzo della cache. Limita automaticamente le query grandi per bypassare la cache.
Come si può configurare storage_configuration per la memorizzazione in RAM?
Nella nuova documentazione di ClickHouse ho trovato una sezione relativa . Nella descrizione c'è un esempio con SSD veloci.
È interessante come possa essere configurato allo stesso modo con la memoria calda del volume. E un'altra domanda. Come funziona il select con questa organizzazione dei dati, leggerà l'intero set o solo quello che è su disco, e questi dati vengono compressi in memoria? E come funziona la sezione prewhere con questa organizzazione dei dati?
Questa impostazione influisce sulla conservazione dei pezzi di dati, e il loro formato non cambia.
Esaminiamo più nel dettaglio.
È possibile configurare lo storage in memoria. L'unica configurazione per il disco è il suo percorso. Si crea una partizione tmpfs, che è montata in un certo percorso nel file system. Si specifica questo percorso come il percorso di storage per la partizione più attiva, dove iniziano ad arrivare e a essere scritti i dati, tutto funziona bene.
Tuttavia, non consiglio di farlo a causa della bassa affidabilità, anche se se hai almeno tre repliche in diversi data center, si può. In caso contrario, i dati saranno recuperabili. Immaginiamo che il server venga improvvisamente spento e poi riacceso. La partizione viene rimontata, ma è vuota. Il server ClickHouse all'avvio vede che quei pezzi mancano, anche se, secondo i metadati di ZooKeeper, dovrebbero esserci. Controlla su quali repliche sono presenti, le richiede e le scarica. In questo modo i dati saranno recuperati.
In questo senso, la memorizzazione dei dati nella RAM non è fondamentalmente diversa dalla memorizzazione su disco, perché, quando i dati vengono scritti su disco, vengono anch'essi inizialmente memorizzati nel page cache e registrati fisicamente in modo differito. Questo dipende dal tipo di montaggio del file system. Tuttavia, è opportuno dire che ClickHouse non esegue fsync durante l'insert.
Inoltre, i dati nella RAM vengono memorizzati nello stesso formato di quelli su disco. La query select recupera esattamente le porzioni necessarie da leggere, selezionando i necessari intervalli di dati, e li legge. Anche il prewhere funziona esattamente allo stesso modo, indipendentemente dal fatto che i dati siano nella RAM o su disco.
Fino a quanti valori univoci è efficace Low Cardinality?
Low Cardinality è progettato in modo astuto. Crea dizionari di dati, ma questi sono locali. Innanzitutto, i dizionari sono specifici per ciascuna porzione, e in secondo luogo, anche all'interno della stessa porzione possono differire per ciascun intervallo. Quando il numero di valori unici raggiunge una soglia — mi sembra un milione — il dizionario viene semplicemente accantonato e ne viene creato uno nuovo.
Risposta in generale: per ogni intervallo locale — diciamo, per ogni giorno — Low Cardinality è efficace fino a circa un milione di valori unici. Dopodiché ci sarà semplicemente un fallback, in cui verranno utilizzati più dizionari anziché uno solo. Funzionerà in modo simile a una normale colonna di tipo string, forse con un'efficienza leggermente inferiore, ma non ci saranno gravi degradi 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, come ad esempio, e . Tuttavia, incontro sempre più spesso persone che dicono di passare da Elasticsearch a ClickHouse.
Perché succede questo? Lo spiegano dicendo che Elasticsearch smette di gestire il carico su alcuni volumi, a partire dalla costruzione degli indici. Gli indici diventano troppo ingombranti e, se si spostano semplicemente i dati in ClickHouse, si scopre che vengono memorizzati in modo molto più efficiente. Inoltre, le query di ricerca spesso non richiedevano di trovare una frase specifica all'interno dell'intero volume di dati, ma richiedevano qualcosa di completamente diverso. Per esempio, trovare negli ultimi ore nei log un determinato sottosequenza di byte.
In questo caso, in ClickHouse crei un indice, il primo campo del quale sarà la data con l'ora. E il maggior filtraggio dei dati sarà proprio basato sull'intervallo di date. All'interno dell'intervallo di date scelto, generalmente è possibile eseguire una ricerca testuale completa anche con il metodo bruteforce utilizzando like. L'operatore like in ClickHouse è l'operatore like più efficiente che puoi trovare. Se ne trovi uno migliore, fammelo sapere.
Tuttavia, un'operazione like è sempre una scansione completa. E una scansione completa può risultare lenta non solo per la CPU, ma anche per il disco. Se ad esempio hai un terabyte di dati al giorno e cerchi una parola in un giorno, dovrai scansionare un terabyte. E questo è sicuramente su dischi rigidi ordinari, e alla fine saranno sovraccaricati al punto che non potrai accedere a quel server via SSH.
In questo caso, sono pronto a suggerire un altro piccolo trucco. È di natura sperimentale: potrebbe funzionare o meno. In ClickHouse esistono indici full-text sotto forma di filtri di Bloom trigrammici. I nostri colleghi di Arenadata hanno già testato questi indici e spesso funzionano esattamente come previsto.
Per utilizzarli correttamente, è importante comprendere come funzionano: cosa rappresenta un filtro di Bloom trigrammico e come scegliere la sua dimensione. Posso dire che saranno utili per le query su frasi rare, sottostringhe che si incontrano di rado nei dati. In questo caso, verranno selezionati sottointervalli tramite gli indici e verranno letti meno dati.
Recentemente, ClickHouse ha introdotto funzionalità ancora più avanzate per la ricerca Full Text. Queste comprendono, in primo luogo, la ricerca di più sottostringhe in un'unica passata, inclusa la gestione delle varianti con o senza distinzione tra maiuscole e minuscole, con supporto per UTF-8 o solo per ASCII. Scegli l'opzione più efficace per le tue esigenze.
È ora possibile cercare più espressioni regolari in un'unica passata. Non è necessario scrivere X like una sottostringa o X like un'altra sottostringa. Scrivi direttamente e tutto verrà eseguito in modo ottimale.
In terzo luogo, ora è disponibile la ricerca approssimativa di regex e la ricerca approssimativa di sottostringhe. Se qualcuno ha scritto una parola con un errore di battitura, verrà cercata in base alla corrispondenza massima.
Come organizzare l'accesso a ClickHouse per un gran numero di utenti?
Raccontaci 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 quali strumenti utilizzare?
Se il cluster è abbastanza grande, una buona soluzione sarebbe aggiungere due server che fungano da punto di accesso per gli analisti. In altre parole, non consentire agli analisti di accedere ai singoli shard del cluster, ma semplicemente creare due server vuoti, senza dati, e configurare i diritti di accesso su di essi. In questo modo, le impostazioni degli utenti per le query distribuite vengono trasferite ai server remoti. Quindi configuri tutto su questi due server e le impostazioni avranno effetto su tutto il cluster.
In linea di principio, questi server non hanno dati, ma la quantità di RAM su di essi è molto importante per l'esecuzione delle query. Il disco può anche essere utilizzato per dati temporanei, se l'aggregazione esterna o l'ordinamento esterno sono abilitati.
È importante esaminare le impostazioni relative a tutti i limiti possibili. Se ora accedo al cluster di 'Yandex.Metrics' come analista e invio una query select count from hits, mi verrà subito restituita un'eccezione che non posso eseguire la query. Il numero massimo di righe che mi è consentito scansionare è di cento miliardi, mentre nel cluster ce ne sono cinquantanovemila miliardi in una singola tabella. Questa è la prima limitazione.
Supponiamo che rimuova il limite sul numero di righe e riesegua la query. Allora vedrò la seguente eccezione: è attivata l'impostazione force index by date. Non posso eseguire la query se non ho specificato l'intervallo di date. Non bisogna contare sul fatto che gli analisti lo indichino manualmente. Un caso tipico è che si scrive un intervallo di date where event date between settimana. E poi si è semplicemente inserita la parentesi nel posto sbagliato, così al posto di and si ottiene or — or URL match. Se non ci sono limiti, andrà a scansionare la colonna URL e consumerà semplicemente una montagna di risorse.
Inoltre, in ClickHouse ci sono due impostazioni di priorità. Sfortunatamente, sono molto primitive. Una si chiama semplicemente priorità. Se priority ≠ 0 e vengono eseguite query con una certa priorità, ma insieme viene eseguita una query con una priorità che ha un valore inferiore, il che significa una priorità più alta, allora la query con un valore di priorità maggiore, che indica una priorità più bassa, verrà semplicemente sospesa e non funzionerà affatto in quel momento.
Questa è una configurazione molto grezza, e non è adatta nei casi in cui il cluster ha un carico costante. Ma se avete richieste brevi e impulsive che sono importanti, e il cluster è per lo più inattivo, questa configurazione potrebbe funzionare.
La seguente impostazione delle priorità si chiama priorità del thread OS. Essa semplicemente imposta per tutti i thread di esecuzione della richiesta il valore nice per lo scheduler Linux. Funziona piuttosto male, ma funziona comunque. Se si imposta il valore nice più basso — che in realtà è il più alto in termini di grandezza, e quindi il priorità più bassa — e per le richieste ad alta priorità si imposta -19, la CPU consumerà richieste a bassa priorità circa quattro volte meno rispetto a quelle ad alta priorità.
È anche necessario impostare il tempo massimo di esecuzione della richiesta — diciamo, cinque minuti. La velocità minima di esecuzione della richiesta — è la cosa più importante. Questa impostazione è esistita a lungo, ed è necessaria per non solo affermare che ClickHouse non rallenta, ma per forzarlo.
Immaginate di configurare: se una richiesta elabora meno di un milione di righe al secondo, non è accettabile. Questo danneggia il nostro buon nome, il nostro database di qualità. Dobbiamo semplicemente vietarlo. Ci sono in realtà due impostazioni. Una si chiama min execution speed — in righe al secondo, e l'altra si chiama timeout before checking min execution speed — di default quindici secondi. In altre parole, possiamo permettere quindici secondi, e dopo, se è lento, dobbiamo semplicemente sollevare un'eccezione per interrompere la richiesta.
Dobbiamo anche configurare le quote. In ClickHouse c'è una funzionalità integrata per le quote, che misura il consumo delle risorse. Tuttavia, purtroppo non riguarda le risorse fisiche come CPU e dischi, ma quelle logiche — il numero di richieste elaborate, righe e byte letti. È possibile impostare, ad esempio, un massimo di cento richieste in cinque minuti e mille richieste all'ora.
Perché è importante? Perché una parte delle richieste analitiche verrà eseguita manualmente direttamente dal client di ClickHouse. E andrà tutto bene. Ma se nella vostra azienda ci sono analisti esperti, scriveranno uno script, e potrebbe esserci un errore nello script. Questo errore potrebbe far sì che la richiesta venga eseguita in un ciclo infinito. È da questo che bisogna proteggersi.
È possibile fornire i risultati di una query a dieci clienti?
Abbiamo alcuni utenti che amano presentarsi con richieste molto grandi nello stesso momento. La richiesta è grande, viene eseguita rapidamente in linea di massima, ma a causa del numero elevato di richieste contemporanee, è molto doloroso. È possibile eseguire la stessa richiesta che arriva dieci volte di seguito, una sola volta e restituire il risultato a dieci clienti?
Il problema è che non abbiamo i risultati della cache o della cache dei dati intermedi. Esiste la cache di pagina del sistema operativo, che permette di non leggere i dati dal disco ripetutamente, ma, sfortunatamente, i dati dovranno comunque essere decompressi, deserializzati e rielaborati.
Vorremmo in qualche modo evitarlo, sia memorizzando nella cache i dati intermedi, sia organizzando richieste simili in una coda e aggiungendo una cache dei risultati. Attualmente stiamo sviluppando una pull request che aggiunge una cache per le richieste, ma solo per i sottoquery nelle sezioni in e join, quindi la soluzione è incompleta.
Tuttavia, ci troviamo anche in situazioni simili. Un esempio classico sono le richieste con paginazione. C'è un report con diverse pagine, e viene eseguita una richiesta limit 10. Poi la stessa cosa, ma limit 10,10. E poi la pagina successiva. La domanda è: perché dobbiamo calcolarlo ogni volta? Ma al momento non c'è soluzione, e non possiamo evitarlo.
Esiste una soluzione alternativa che si affianca a ClickHouse — .
Kirill Shvakov: In ClickHouse Proxy c'è un limitatore di velocità integrato e una cache dei risultati. Sono state implementate molte impostazioni, poiché è stata affrontata una problematica simile. Il Proxy consente di limitare le richieste mettendole in coda e di configurare per quanto tempo vive la cache delle richieste. Se le richieste erano effettivamente identiche, il Proxy le restituirà molte volte, mentre si collegherà a ClickHouse solo una volta.
In Nginx, there's also a cache in the free version, and it will work too. Nginx even has settings that, if requests come in simultaneously, it will throttle the others until one is completed. However, the configuration in ClickHouse Proxy is much better. It was designed specifically for ClickHouse and these types of requests, so it suits better. Plus, it's easy to set up.
Come gestire le operazioni asincrone e le viste materializzate?
There is a problem that operations with the replacing engine are asynchronous — first, the data is written, then it gets collapsed. If there’s a materialized table with some aggregates living under the table, duplicates will be written to it. If there isn’t any complex logic, the data will be duplicated. What can be done about it?
An obvious solution is to implement a trigger on a certain class of materialized views during the asynchronous collapse operation. Are there any 'silver bullets', plans for implementing such functionalities?
It's worth understanding how deduplication works. What I’ll discuss now is not directly related to the question, but it’s good to keep it in mind just in case.
Quando si inserisce in una tabella replicata, ci sarà deduplicazione di interi blocchi inseriti. Se reinserisci lo stesso blocco, contenente lo stesso numero di righe nello stesso ordine, i dati verranno deduplicati. Riceverai 'Ok' in risposta all'insert, ma effettivamente verrà registrato un solo pacchetto di dati, e non verrà duplicato.
Questo è necessario per certezza. Se durante l'inserimento hai ricevuto 'Ok', significa che i tuoi dati sono stati inseriti. Se hai ricevuto un errore da ClickHouse, significa che non sono stati inseriti, e l'inserimento deve essere ripetuto. Ma se la connessione si è interrotta durante l'inserimento, non sai se i dati sono stati inseriti o meno. L'unica opzione è ripetere l'inserimento di nuovo. Se i dati erano stati effettivamente inseriti e li reinserisci, c'è deduplicazione dei blocchi. Questo è necessario per evitare duplicati.
È anche importante come funziona per le viste materializzate. Se i dati sono stati deduplicati durante l'inserimento nella tabella principale, non andranno nemmeno nella vista materializzata.
Ora, riguardo alla questione. La vostra situazione è più complessa perché state registrando duplicati di singole righe. Non è quindi l'intero gruppo a essere duplicato, ma specifiche righe che si sovrappongono in background. Infatti, i dati si ridurranno nella tabella principale, mentre nella vista materializzata andranno le righe non ridotte, e durante le fusioni nulla accadrà alle viste materializzate. Perché la vista materializzata non è altro che un trigger su insert. In altre operazioni, non succede nulla di aggiuntivo con essa.
E qui non posso offrire alcuna buona notizia. È necessario cercare una soluzione specifica per questo caso. Ad esempio, è possibile anche fare un replace nella vista materializzata e il metodo di deduplicazione potrebbe funzionare allo stesso modo. Ma purtroppo, non sempre. Se è aggregante, allora non funzionerà.
Kirill Shvakov: Anche noi abbiamo avuto i nostri problemi con i sistemi. C'era un problema con le visualizzazioni degli annunci e alcuni dati che potevamo mostrare in tempo reale — si trattava solo di visualizzazioni. Raramente si duplicano, ma se succede, poi li accorciamo comunque. E c'erano cose che non si potevano duplicare — click e tutta quella storia. Ma volevamo mostrarli quasi immediatamente.
Come sono stati creati le viste materializzate? Ci sono viste in cui i dati vengono scritti direttamente — i dati grezzi vengono registrati e scritti nelle viste. A un certo punto i dati potrebbero non essere molto corrette, si duplicano e così via. E c'è una seconda parte della tabella, che appare esattamente come le viste materializzate, ovvero sono strutturalmente identiche. Di tanto in tanto ricalcoliamo i dati, conteggiamo i dati senza duplicati e li scriviamo in quelle tabelle.
Abbiamo utilizzato l'API: farlo manualmente in ClickHouse non è possibile. L'API controlla quando ho la data dell'ultimo inserimento in una tabella, dove ci sono già dati corretti e calcolati, e poi fa una richiesta a una tabella e a un'altra. Da una tabella seleziona i dati fino a un certo intervallo temporale, mentre dall'altra raccoglie ciò che non è ancora stato calcolato. Funziona, ma non può essere fatto solo con ClickHouse.
Se avete un API — per analisti, per utenti — fondamentalmente è un'opzione. Potete sempre aggregare, sempre ricalcolare. Potete farlo una volta al giorno o in un altro momento. Scegliete da soli il range che non vi serve e che non è critico.
In ClickHouse ci sono molti log. Come posso vedere tutto ciò che accade con il server in tempo reale?
In ClickHouse c'è un'enorme quantità di log diversi, e questo numero è in aumento. Nelle nuove versioni alcuni di essi sono attivati di default, mentre nelle versioni precedenti devono essere abilitati durante l'aggiornamento. Tuttavia, ci sono sempre più log. Vorrei vedere, alla fine, ciò che sta succedendo ora con il mio server, magari su un dashboard riepilogativo.
Non hai in squadra ClickHouse o tra i tuoi amici qualcuno che supporti una certa funzionalità di dashboard pronte in grado di visualizzare questi log come prodotto finito? Alla fine, guardare i log su ClickHouse è interessante. Ma sarebbe fantastico se fosse già disponibile in formato dashboard. Mi piacerebbe molto.
Ci sono dashboard, ma non sono standardizzate. Nella nostra azienda, circa 60 team utilizzano ClickHouse e la cosa strana è che molti di loro hanno creato delle dashboard personalizzate, leggermente diverse. Alcuni team utilizzano un'installazione interna di Yandex.Cloud. Ci sono alcuni report pronti, anche se non tutti quelli necessari. Altri hanno le loro soluzioni.
I miei colleghi di Metriche hanno la loro dashboard in Grafana, mentre io ho la mia sul loro stesso cluster. Lì monitoro cose come il cache hit per il caching. E la situazione è complicata poiché utilizziamo strumenti diversi. Ho creato la mia dashboard con uno strumento molto vecchio chiamato Graphite-web. È abbastanza brutta. E continuo a usarla, anche se Grafana sarebbe probabilmente più comoda e bella.
La parte fondamentale dei dashboard è la stessa. Si tratta delle metriche di sistema del 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 parti per le partizioni delle tabelle MergeTree, il lag di replica, la dimensione della coda di replica, il numero di righe inserite al secondo, il numero di blocchi inseriti al secondo. Queste sono tutte informazioni ottenute non dai log, ma dalle metriche.
Vladimir Kolobayev: Alexey, vorrei fare alcune piccole correzioni. C'è Grafana. Grafana ha un datasource, che è ClickHouse. Quindi posso fare richieste direttamente a ClickHouse da Grafana. In ClickHouse c'è una tabella con i log, ed è la stessa per tutti. Vorrei poter consultare questa tabella dei log in Grafana e vedere le richieste che il mio server invia. Sarebbe fantastico avere un dashboard del genere.
L'ho creato da solo. Ma ho una domanda: se tutto è standardizzato e Grafana è utilizzata da tutti, perché non c'è un dashboard ufficiale su Yandex?
Kirill Shvakov: In realtà, il datasource per ClickHouse è attualmente supportato da Altinity. Voglio semplicemente fornire una direzione su dove indagare e chi contattare. È possibile chiedere a loro, poiché Yandex sviluppa ClickHouse e non la storia che lo circonda. Altinity è l'azienda principale che attualmente promuove ClickHouse. Non lo abbandoneranno, ma continueranno a supportarlo. Infatti, per caricare un dashboard su Grafana, è sufficiente registrarsi e caricarlo: non ci sono particolari problemi.
Alexey Milovidov: Negli ultimi anni, ClickHouse ha aggiunto molte funzionalità per il profiling delle query. Esistono metriche per ogni query riguardo all'utilizzo delle risorse. Recentemente è stato aggiunto anche un profiler delle query a livello più basso, per vedere dove una query utilizza ogni millisecondo. Tuttavia, per utilizzare questa funzionalità, devo aprire il client da console e digitare la query che dimentico costantemente. Ho salvato da qualche parte e continuo a dimenticare dove.
Mi piacerebbe avere uno strumento che mostri semplicemente - ecco le vostre query pesanti, raggruppate per classi di query. Clicco su una qualsiasi e mi viene detto che è pesante per questo motivo. Attualmente non c'è una soluzione del genere, ed è davvero piuttosto strano che, quando le persone mi chiedono: «Ci sono già dei dashboard pronti per Grafana?», io rispondo: «Visita il sito di Grafana, lì c'è la community 'Dashboard', e ci sono dashboard di Dimka, ci sono dashboard di Kostyan. Cos'è? Non lo so, io stesso non li ho mai usati».
Come influenzare i merge in modo che il server non cada in OOM?
Ho una tabella, con solo una partizione, è ReplacingMergeTree. Ci scrivo dati da quattro anni. Dovevo fare un alter e rimuovere alcuni dati.
L'ho fatto e durante l'elaborazione di questa query tutta la memoria in tutti i server del cluster è stata consumata, e tutti i server del cluster sono andati insieme in OOM. Poi si sono rialzati tutti insieme, hanno iniziato a eseguire il merge di questa stessa operazione, di questo blocco di dati, e sono di nuovo caduti in OOM. Poi si sono rialzati di nuovo e sono di nuovo caduti. E questa cosa non si fermava.
Poi si è rivelato essere un bug, che i ragazzi hanno risolto. Questo è fantastico, grazie mille. Ma il problema persiste. E adesso, quando penso a come fare una qualche fusione nella tabella, mi viene da chiedermi perché non posso influenzare in qualche modo queste fusioni? Ad esempio, limitarle in base alla quantità di memoria RAM richiesta, o in termini di numero, di quale sarà specificamente quello che gestirà questa tabella.
Ho una tabella chiamata "Metriche"; ti prego di elaborarla in due thread. Non è necessario creare dieci o cinque fusioni in parallelo, fallo in due. Penso che in due avrà abbastanza memoria, mentre per gestirne dieci potrebbe non bastare. Perché resta la paura? Perché la tabella cresce, e prima o poi mi troverò nella situazione in cui, non per colpa di un bug, ma perché i dati cambieranno in quantità così elevate, non avrò semplicemente memoria a sufficienza sul server. E allora il server andrà in OOM durante la fusione. Inoltre, posso annullare la mutazione, ma non le fusioni.
Sai che, durante i merge, il server non andrà in OOM, perché durante il merge viene utilizzata solo una piccola quantità di RAM per un limitato intervallo di dati. Quindi, andrà tutto bene, indipendentemente dal volume dei dati.
Vladimir Kolobayev: Va bene. Il punto è che, dopo aver fatto il bug fix, ho scaricato la nuova versione e su un'altra tabella, più piccola, dove ci sono molte partizioni, ho effettuato un'operazione simile. Durante il merge, il server ha consumato circa 100 GB di RAM. Avevo 150 GB utilizzati, 100 GB consumati, e mi sono rimasti 50 GB, quindi non sono andato in OOM.
Cosa mi protegge al momento dal cadere in OOM, se effettivamente consuma 100 GB di RAM? Come comportarsi se la RAM si esaurisce durante i merge?
Alexey Milovidov: Esiste un problema: l'uso della memoria RAM durante i merge non è limitato. Un secondo problema è che, se un merge è stato programmato, deve essere eseguito, poiché è registrato nel log di replica. Il log di replica contiene le azioni necessarie per portare la replica in uno stato consistente. Se non si eseguono le operazioni manuali che lo riporterebbero indietro, il merge dovrà comunque essere completato in un modo o nell'altro.
Certo, sarebbe utile avere un limite per la RAM che protegge, ''per ogni evenienza'', contro l'OOM. Questo non aiuterà il merge a completarsi; ricomincerà, raggiungerà un certo punto, darà un'eccezione e poi riprenderà di nuovo — non ne verrà fuori nulla di buono. Tuttavia, sarebbe utile introdurre questo tipo di limitazione.
Come si svilupperà il driver Golang per ClickHouse?
Il driver Golang scritto da Kirill Shvakov è attualmente ufficialmente supportato dal team di ClickHouse. Esso , ed è ora grande e reale.
Una piccola nota. Esiste un meraviglioso e amato sistema di archiviazione di forme normali di ordine infinito — Vertica. Hanno anche il loro driver Python ufficiale, supportato dagli sviluppatori di Vertica. È successo diverse volte che le versioni del sistema di archiviazione e del driver si discostassero notevolmente, causando l'interruzione del funzionamento del driver in un certo momento. E un secondo punto. La supporto per questo driver ufficiale, mi sembra, è gestito con il sistema "nipplet" — scrivi loro un issue, e rimane in sospeso 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 l'interfaccia http, perché gli piace di più. Come avverrà lo sviluppo di questo driver? Sarà sincronizzato con eventuali cambiamenti significativi nel sistema di archiviazione stesso? E qual è la procedura per la revisione degli issue?
Kirill Shvakov: Primo — come è tutto strutturato burocraticamente. Questo aspetto non è stato discusso, quindi non posso rispondere.
Per rispondere alla domanda sull'issue, è necessaria una breve storia del driver. Ho lavorato in un'azienda che gestiva molti dati. Era una rotatrice pubblicitaria con una quantità enorme di eventi che dovevano essere memorizzati da qualche parte. A un certo punto è arrivato ClickHouse. Abbiamo caricato i dati lì e per un po' tutto è andato bene, poi ClickHouse è andato in crash. A quel punto abbiamo deciso che non ci serviva.
Un anno dopo siamo tornati all'idea di usare ClickHouse e dovevamo trovare un modo per scrivere i dati. La premessa era questa: l'hardware era molto debole e le risorse scarse. Ma abbiamo sempre lavorato in questo modo, quindi abbiamo guardato verso il protocollo nativo.
Dato che lavoravamo in Go, era chiaro che era necessario un driver per Go. L'ho fatto praticamente a tempo pieno: era il mio compito di lavoro. Fino a un certo momento siamo riusciti a svilupparlo e in sostanza nessuno si aspettava che qualcun altro lo avrebbe usato. Poi è arrivato CloudFlare con esattamente lo stesso problema, e per un certo periodo abbiamo lavorato molto a stretto contatto, poiché avevano gli stessi compiti. Infatti, lo abbiamo fatto sia in ClickHouse stesso, sia nel driver.
A un certo punto ho semplicemente smesso di occuparmene, perché la mia attività in merito a ClickHouse e il mio lavoro sono cambiati un po'. Di conseguenza, i problemi non vengono chiusi. Di tanto in tanto, nel repository ci sono commit da parte di persone che hanno bisogno di qualcosa. Allora guardo le pull request e a volte anche modifico qualcosa, ma succede raramente.
Vorrei tornare al driver. Qualche anno fa, quando tutto questo è iniziato, ClickHouse era diverso e offriva altre possibilità. Ora abbiamo compreso come ristrutturare il driver per migliorarlo. Se ciò avverrà, la versione 2 sarà comunque incompatibile a causa dei workaround accumulati.
Non so come organizzare questa cosa. Io stesso non ho molto tempo. Se ci sono persone che possono completare il driver, posso aiutarli e spiegare cosa fare. Ma la partecipazione attiva di 'Yandex' nello sviluppo del progetto non è stata discussa finora.
Alexey Milovidov: In realtà, non c'è ancora burocrazia riguardo a questi driver. L'unica cosa è che sono stati ufficialmente riconosciuti dalla nostra organizzazione, il che significa che questo driver è considerato la soluzione predefinita per Go. Ci sono altri driver, ma sono separati.
Non abbiamo alcuno sviluppo interno per questi driver. La questione è se possiamo assumere una persona specificamente per lo sviluppo di tutti i driver della community, oppure se possiamo trovare qualcuno esternamente.
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 automaticamente. Si attiva solo quando l'utente lo richiede. E al primo accesso restituisce un errore. È possibile caricare automaticamente i dizionari utilizzando ClickHouse, o dobbiamo sempre controllarne la disponibilità affinché gli utenti non ricevano errori?
È possibile che abbiamo una versione obsoleta di ClickHouse, quindi il dizionario non si è caricato automaticamente? Può succedere?
In primo luogo, i dizionari possono essere forzati a caricarsi tramite una query. ricarica dizionari di sistema. In secondo luogo, riguardo all'errore: se il dizionario è già stato caricato, le richieste funzioneranno con i dati già caricati. Se il dizionario non è stato ancora caricato, verrà caricato proprio durante la richiesta.
Per i dizionari pesanti questo non è molto comodo. Ad esempio, è necessario estrarre un milione di righe da MySQL. Qualcuno esegue una semplice selezione, ma questa selezione attenderà proprio quel milione di righe. Qui ci sono due soluzioni. La prima è disattivare lazy_load. La seconda è, quando il server si avvia, prima di caricarlo, eseguire ricarica dizionario di sistema oppure semplicemente eseguire la richiesta che utilizza il dizionario. In tal caso, il dizionario verrà caricato. È necessario controllare la disponibilità dei dizionari con l'impostazione lazy_load attivata, perché ClickHouse non li preleva automaticamente.
Alla domanda finale la risposta è: o la versione è obsoleta, o è necessario fare il debug.
Come gestire il fatto che il comando system reload dictionaries non carica nessuno dei molteplici dizionari se anche solo uno di essi fallisce con un errore?
C'è ancora una domanda riguardo al caricamento dei dizionari di sistema. Abbiamo due dizionari: uno non si carica, l'altro sì. In questo caso, il caricamento dei dizionari di sistema non carica nessun dizionario, e dobbiamo caricare specificamente quello richiesto usando il caricamento di un dizionario specifico. Questo è anche legato alla versione di ClickHouse?
Voglio darvi una buona notizia. Questo comportamento è cambiato. Quindi, se aggiornate ClickHouse, cambierà anche questo. Se non vi soddisfa l'attuale comportamento ricarica dizionari di sistema, aggiornate e speriamo che migliori.
C'è 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 ai dizionari, in particolare le credenziali. Abbiamo specificato 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 estraendo le credenziali nel file di configurazione del driver ODBC. Esiste un modo per configurare le credenziali nel file di configurazione di ClickHouse senza esporre queste credenziali in caso di errori?
La soluzione è semplice: indicare queste credenziali in odbc.ini e, nel ClickHouse, specificare solo il nome della fonte dati ODBC. Per gli altri sorgenti di dizionario non sarà così: né per il dizionario MySQL, né per gli altri dovreste vedere la password quando compare un messaggio di errore. Anche per ODBC farò un controllo — se esiste, è meglio rimuoverlo.
Bonus: sfondi per Zoom da incontri
Cliccando sull'immagine, per i lettori più tenaci si apriranno sfondi bonus delle nostre riunioni. Spegniamo il fuoco insieme ai mascotte delle tecnologie di Avito, discutiamo con i colleghi dalla sala dell'amministratore di sistema o dal vecchio club dei computer e svolgiamo il nostro incontro quotidiano sotto il ponte, sullo sfondo dei graffiti.
Fonte: habr.com
