ClickHouse pentru utilizatori avansați în întrebări și răspunsuri

În aprilie, inginerii Avito au organizat întâlniri online cu principalul dezvoltator ClickHouse, Alexei Milovidov, și Kirill Shvakov, dezvoltator Golang de la compania Integros. Au discutat despre modul în care folosim sistemul de gestionare a bazelor de date și despre dificultățile cu care ne confruntăm.

Pe baza întâlnirii, am realizat un articol cu răspunsuri ale experților la întrebările noastre și ale spectatorilor despre backup-uri, resharding de date, dicționare externe, driver Golang și actualizarea versiunilor ClickHouse. Acesta poate fi util dezvoltatorilor care lucrează deja activ cu SGBD-ul „Yandex” și care sunt interesați de prezentul și viitorul său. Răspunsurile lui Alexei Milovidov sunt preluate, cu excepția cazului în care se specifică altceva.

Atenție, sub această secțiune se află mult text. Sperăm că conținutul cu întrebările vă va ajuta să vă orientați.

ClickHouse pentru utilizatori avansați în întrebări și răspunsuri

Cuprins

Dacă nu doriți să citiți textul, puteți viziona înregistrarea întâlnirilor pe canalul nostru de youtube. Timpurile sunt în primul comentariu de sub video.

ClickHouse se actualizează constant, dar datele noastre nu. Ce ar trebui să facem în această situație?

ClickHouse se actualizează constant, dar datele noastre, care au fost procesate cu optimize final, nu se actualizează și rămân în backup.

Să presupunem că a apărut o problemă și datele au fost pierdute. Am decis să ne recuperăm, și s-a dovedit că partțiile vechi, care sunt păstrate pe serverele de backup, se deosebesc semnificativ de versiunea ClickHouse utilizată în prezent. Ce să facem în această situație și este posibilă această diferență?

Situația în care ați recuperat datele din backup într-un format vechi, iar în noua versiune acestea nu se conectează, nu este posibilă. Ne asigurăm că formatul datelor în ClickHouse rămâne întotdeauna compatibil cu versiunile anterioare. Acest aspect este mult mai important decât compatibilitatea în funcționalitate, în cazul în care comportamentul unei funcții rar folosite s-a modificat. Datele stocate pe disc trebuie să poată fi citite întotdeauna de către noua versiune ClickHouse. Asta este o lege.

Care sunt cele mai bune practici în prezent pentru backup-ul datelor din ClickHouse?

Cum să facem backup-uri ținând cont că avem operațiuni optimize final, o bază de date imensă de terabaiți și date care sunt actualizate, să zicem, în ultimele trei zile, iar ulterior nu se mai întâmplă nimic cu ele?

Putem să ne facem un propriul nostru soluție și să scriem în Bash: adună aceste backup-uri așa cum trebuie. Poate că nu e nevoie de improvizații, iar bicicleta a fost déjà inventată?

Pentru început, în legătură cu cele mai bune practici. Colegii mei recomandă întotdeauna, în răspuns la întrebările despre backup-uri, să amintim de serviciul „Yandex.Cloud”, unde această problemă a fost deja rezolvată. Așa că folosiți-l dacă aveți ocazia.

Nu există o soluție completă, integrată în proporție de o sută la sută în ClickHouse, pentru backup-uri. Există anumite șabloane care pot fi utilizate. Pentru a obține o soluție completă, va trebui fie să lucrați un pic manual, fie să creați wrapper-e sub formă de scripturi.

Voi începe cu cele mai simple soluții și voi termina cu cele mai sofisticate, în funcție de volumul de date și dimensiunea cluster-ului. Cu cât clusterele sunt mai mari, cu atât soluția devine mai complexă.

Dacă tabelul cu date ocupă doar câteva gigaocteți, backup-ul poate fi realizat astfel:

  1. Salvați definiția tabelelor, adică metadata — show create table.
  2. Realizați un dump folosind clientul ClickHouse — select * from table în fișier. În mod implicit, veți obține un fișier în format TabSeparated. Dacă doriți mai eficiență — puteți utiliza formatul Native.

Dacă volumul de date este mai mare, backup-ul va dura mai mult timp și va ocupa mult spațiu. Aceasta se numește backup logic, care nu este legat de formatul de date ClickHouse. Dacă există, în cel mai rău caz, veți putea lua backup-ul și încărca-l în MySQL pentru recuperare.

Pentru cazuri mai avansate, ClickHouse are încorporată capacitatea de a crea snapshot-uri ale partițiilor în sistemul de fișiere local. Această capacitate este disponibilă sub formă de interogare alter table freeze partition. Sau simplu alter table freeze — acesta este un snapshot al întregului tabel.

Snapshot-ul va fi creat consistent pentru un singur tabel pe un singur shard, adică nu este posibil să creați un snapshot consistent pentru întregul cluster în acest mod. Dar pentru majoritatea sarcinilor, nu este necesară o astfel de cerință, iar pentru fiecare shard este suficient să efectuați cererea și să obțineți un snapshot consistent. Acesta este creat sub formă de hard link-uri și, prin urmare, nu ocupă spațiu suplimentar. Ulterior, copiați acest snapshot pe serverul de backup sau în spațiul de stocare pe care îl folosiți pentru backup-uri.

Restaurarea unui astfel de backup este destul de ușoară. Primul pas este să creați tabelele conform definițiilor tabelelor existente. Ulterior, copiați snapshot-urile salvate ale partițiilor în Directory-Detached pentru tabelele respective și efectuați interogarea attach partition. Această soluție este perfect potrivită pentru cele mai serioase volume de date.

Uneori este nevoie de ceva și mai tare — în acele situații când aveți zeci sau chiar sute de terabytes pe fiecare server și sute de servere. Aici există o soluție pe care am văzut-o la colegii de la „Yandex.Metrica”. Nu aș recomanda-o fiecăruia — citiți și decideți singuri dacă vi se potrivește sau nu.

Mai întâi trebuie să creați mai multe servere cu cutii de disc mari. Apoi, pe aceste servere, să ridicați câteva servere ClickHouse și să le configurați astfel încât să funcționeze ca o altă replică pentru aceleași sharduri. Și mai departe, să folosiți pe aceste servere un sistem de fișiere sau un instrument care permite crearea de snapshot-uri. Există două variante. Prima variantă — snapshot-uri LVM, a doua variantă — ZFS pe Linux.

După aceea, trebuie să creați un snapshot în fiecare zi, care va ocupa un anumit spațiu. Firește, dacă datele se schimbă, în timp, volumul de spațiu va crește. Acest snapshot poate fi recuperat în orice moment pentru a restaura datele, o soluție oarecum ciudată. Plus, de asemenea, trebuie să limitați aceste replici în configurație, astfel încât să nu încerce să devină leaderi.

Va fi posibil să organizăm o întârziere controlată a replicilor în vârfuri?

Anul acesta plănuiți să faceți valuri în ClickHouse. Va fi posibil să organizați un decalaj controlat al replicilor în acestea? Am dori să ne protejăm de scenarii negative cu alterări și alte modificări prin intermediul acestuia.

Se pot face rollback-uri pentru alterări? De exemplu, în valul existent să luăm și să spunem că până în acest moment să aplicăm modificările, iar de acest moment înainte să nu mai aplicăm modificările?

Dacă în clusterul nostru a venit o comandă și l-a distrus, atunci avem o replică condiționată cu un decalaj de o oră, unde putem spune că să o folosim exact pe aceasta deocamdată, dar să nu aplicăm ultimele zece minute de modificări în ea?

Pentru început, despre decalajul controlat al replicilor. Am primit această solicitare din partea utilizatorilor, iar noi am creat un issue pe GitHub cu rugămintea: „Dacă cuiva îi trebuie, dați un like, puneți o inimă”. Nimeni nu a dat like, și issue-ul a fost închis. Cu toate acestea, deja acum este posibil să obțineți această funcționalitate, configurând ClickHouse. Adevărat, doar începând cu versiunea 20.3.

ClickHouse efectuează constant fuziunea datelor în fundal - merge. Când fuziunea este realizată, un anumit set de părți de date este înlocuit cu o parte mai mare. Totuși, părțile de date anterioare rămân pe disc pentru o anumită perioadă.

În primul rând, acestea continuă să fie stocate atâta timp cât există interogări select care le utilizează, pentru a asigura o funcționare neblokantă. Interogările select citesc fără probleme din părțile mai vechi.

În al doilea rând, există și un prag de timp - părțile vechi de date sunt stocate pe disc timp de opt minute. Aceste opt minute pot fi setate și pot fi transformate chiar și într-o zi. Aceasta va consuma spațiu pe disc: în funcție de fluxul de date, s-ar putea ca în ultima zi datele să nu se dubleze, ci să devină de cinci ori mai multe. Dar veți putea, în caz de problemă serioasă, să opriți serverul ClickHouse și să rezolvați totul.

Acum apare întrebarea, cum protejează aceasta împotriva alterărilor. Aici merită să ne uităm mai profund, deoarece în versiunile vechi de ClickHouse alterul funcționa astfel încât modifica direct părțile. Există o parte de date cu anumite fișiere, și facem, de exemplu, alter drop column. Atunci, această coloană este ștearsă fizic din toate părțile.

Dar începând cu versiunea 20.3, mecanismul alterărilor a fost complet schimbat, iar acum părțile de date sunt întotdeauna imutabile. Acestea nu se schimbă deloc - alterele funcționează acum aproximativ ca fuziunile. În loc să schimbăm o parte pe loc, creăm una nouă. În noua parte, fișierele care nu s-au schimbat devin hardlink-uri, iar dacă am șters o coloană, aceasta va lipsi pur și simplu din noua parte. Partea veche va fi ștearsă implicit după opt minute, iar aici se pot ajusta setările despre care s-a vorbit mai sus.

Același lucru se aplică și alterărilor de tip mutații. Când faceți alter delete sau alter update, acesta nu modifică partea, ci creează una nouă. Apoi șterge partea veche.

Ce facem dacă structura tabelului s-a schimbat?

Cum se restaurează un backup care a fost realizat cu o schemă veche? Și a doua întrebare despre cazul cu instantanee și instrumentele sistemului de fișiere. Este Btrfs potrivit în loc de ZFS pe Linux LVM?

Dacă faceți attach partition partiții cu o altă structură, ClickHouse vă va spune că nu este posibil. Soluția este următoarea. În primul rând, creați o tabelă temporară de tip MergeTree cu vechea structură, adăugați datele folosind attach, faceți o cerere alter. Apoi puteți fie să copiați sau să mutați aceste date și să faceți attach din nou, fie să folosiți cererea alter table move partition.

Acum, a doua întrebare - se poate folosi Btrfs. În primul rând, dacă aveți LVM, este suficient să faceți snapshots LVM, iar sistemul de fișiere poate fi și ext4, nu contează. Cu Btrfs, totul depinde de experiența dumneavoastră în utilizarea acestuia. Este un sistem de fișiere matur, dar totuși există unele suspiciuni cu privire la modul în care va funcționa în practică în scenarii specifice. Nu aș recomanda utilizarea acestuia dacă nu aveți Btrfs în producție.

Care sunt cele mai bune practici curente în resharding-ul datelor?

Întrebarea despre resharding este complexă și multifacetată. Aici se poate răspunde imediat cu mai multe opțiuni. Se poate aborda dintr-o parte și se poate spune așa - în ClickHouse nu există posibilitatea încorporată de resharding. Dar mă tem că acest răspuns nu va mulțumi pe nimeni. Așadar, se poate aborda din cealaltă parte și se poate spune că în ClickHouse există multe metode de resharding a datelor.

Dacă se termină spațiul pe cluster sau nu poate face față încărcării, adăugați servere noi. Dar aceste servere sunt, prin default, goale, nu au date, nu există nicio încărcare. Trebuie să redistribuiți datele pentru a fi uniform distribuite pe noul cluster extins.

Prima metodă de a face acest lucru este să copiați o parte a partițiilor pe servere noi folosind cererea alter table fetch partition. De exemplu, dacă ați avut partiții pe luni, luați prima lună din 2017 și copiați-o pe un nou server, apoi - copiați a treia lună pe un alt nou server. Și faceți așa până devine mai mult sau mai puțin uniform.

Transferul se poate efectua doar pentru acele partiții care nu se schimbă în timpul scrierii. Pentru partițiile noi, va trebui să dezactivați scrierea, deoarece transferul lor nu este atomic. Altfel, veți obține duplicaturi sau omisiuni în date. Cu toate acestea, această metodă este practică și funcționează destul de eficient. Partiințile comprimate deja sunt transmise prin rețea, ceea ce înseamnă că datele nu sunt recompresate sau recodificate.

Această metodă are un dezavantaj, și anume că depinde de schema de sharding, dacă ați fost nevoit să vă bazați pe această schemă de sharding, ce cheie de sharding ați avut. În exemplul dumneavoastră, pentru cazul cu metrici, cheia de sharding este hash-ul căii. Când executați un select pe o tabelă distribuită, acesta merge imediat la toate shard-urile clusterului și preia datele de acolo.

Aceasta înseamnă că, în fapt, nu contează pentru dumneavoastră ce date se află pe care shard. Important este că datele de pe aceeași cale se găsesc pe același shard, dar care anume, nu este esențial. În acest caz, mutarea partițiilor deja create se potrivește perfect, deoarece la interogările select veți obține, atât înainte de re-sharding, cât și după, date complete, schema valorii neavând o importanță deosebită.

Dar sunt și cazuri mai complicate. Dacă la nivelul logicii aplicației vă bazați pe o schemă specială de sharding, în care acest client se află pe un anumit shard, și solicitarea poate fi trimisă direct acolo, nu în tabelul distribuit. Sau utilizați o versiune destul de recentă de ClickHouse și ați activat setarea optimize skip unused shards. În acest caz, în timpul interogării select, expresia din secțiunea where va fi analizată și se va calcula pe ce shard-uri trebuie să mergeți conform schemei de sharding. Acest lucru funcționează cu condiția ca datele să fie aranjate conform acestei scheme de sharding. Dacă le-ați reorganizat manual, corespondența se poate schimba.

Așadar, aceasta este prima metodă. Aștept răspunsul dumneavoastră, dacă metoda se potrivește sau dacă mergem mai departe.

Vladimir Kolobaev, administrator de sistem principal la Avito: Alexey, metoda pe care ați menționat-o nu se potrivește foarte bine când trebuie să dispersăm încărcătura inclusiv pe citire. Putem lua o partiție de o lună și putem muta luna precedentă pe un alt nod, dar când va veni o solicitare pentru aceste date, vom încărca doar acea nod. Ar fi bine să încărcăm întregul cluster, deoarece, în caz contrar, pentru o perioadă întreagă, toată încărcătura de citire va fi procesată de două shard-uri.

Alexey Milovidov: Răspunsul aici este ciudat — da, este rău, dar ar putea funcționa. Voi explica cum. Este important să ne uităm la scenariul de încărcare care stă în spatele datelor dumneavoastră. Dacă este vorba de date de monitorizare, atunci aproape cu siguranță putem spune că cea mai mare parte a interogărilor se îndreaptă spre datele recente.

Ați instalat noi servere, ați mutat partițiile vechi, dar ați schimbat și modul în care sunt scrise datele recente. Datele recente vor fi distribuite pe tot clusterul. Astfel, deja după cinci minute, interogările pentru ultimele cinci minute vor încărca uniform clusterul, iar după o zi, interogările pentru ultimele 24 de ore vor încărca uniform clusterul. Din păcate, interogările pentru luna anterioară vor merge doar pe o parte a serverelor din cluster.

Dar adesea nu veți avea interogări specifice pentru februarie 2019. Cel mai probabil, dacă interogările sunt pentru anul 2019, atunci ele vor fi pentru întreg anul 2019 — pentru o perioadă mai lungă, nu pentru o mică fereastră de timp. Și astfel de interogări vor putea de asemenea să împartă uniform încărcarea pe cluster. Însă, în general, observația dumneavoastră este complet corectă că este o soluție ad hoc care nu distribuie datele complet uniform.

Am câteva puncte suplimentare pentru a răspunde la întrebare. Unul dintre ele se referă la modul în care să faceți schema de shardare astfel încât re-shardarea să fie mai puțin dureroasă. Acest lucru nu este întotdeauna posibil.

De exemplu, aveți date de monitorizare. Datele de monitorizare cresc din trei motive. Primul — acumularea de date istorice. Al doilea — creșterea traficului. Și al treilea — creșterea numărului de lucruri care sunt monitorizate. Apar noi microservicii și metrici care trebuie păstrate.

Este posibil ca cea mai mare creștere să fie legată de a treia cauză — aceasta este creșterea utilizării monitorizării. Și în acest caz, ar trebui să ne uităm la caracterul încărcării, care sunt principalele interogări pentru select. Principalele interogări pentru select vor veni, cel mai probabil, dintr-un subgrup de metrici.

De exemplu, utilizarea CPU-ului pe anumite servere de către un anumit serviciu. Asta înseamnă că există un anumit subset de chei prin care accesați aceste date. Iar cererea pentru aceste date este, cel mai probabil, destul de simplă și se execută în zeci de milisecunde. Este utilizat pentru servicii de monitorizare, pentru tablouri de bord. Sper că am înțeles corect.

Vladimir Kolobaev: Problema este că apelăm foarte des la date istorice, deoarece comparăm în timp real situația curentă cu cea istorică. Și este important pentru noi să avem acces rapid la un volum mare de date, iar ClickHouse se descurcă excelent în acest sens.

Aveți absolut dreptate, majoritatea cererilor de citire le avem din ultimele 24 de ore, la fel ca orice sistem de monitorizare. Dar, bineînțeles, există și o povară considerabilă pe datele istorice. Aceasta provine în principal de la sistemul de alertare, care la fiecare treizeci de secunde solicită ClickHouse: „Dă-mi datele din ultimele șase săptămâni. Și acum, construiește-mi o medie mobilă din ele și să comparăm valoarea curentă cu cea istorică”.

Aș dori să spun că avem pentru aceste cereri foarte recente un alt tabel mic, în care stocăm datele doar pentru două zile, iar cererile principale se îndreaptă către el. Tabelul mare, șardificat, primește doar cereri istorice mari.

Alexey Milovidov: Din păcate, pentru scenariul dumneavoastră este aplicabil într-o manieră proastă, dar voi descrie două scheme de șardificare proaste și complexe pe care nu ar trebui să le folosiți, dar care sunt utilizate în serviciul prietenilor mei.

Există un cluster principal cu evenimente din „Yandex.Metrica”. Evenimentele sunt vizite pe pagini, clicuri și treceri. Majoritatea cererilor se îndreaptă către un site web specific. Deschideți serviciul „Yandex.Metrica”, aveți un site — avito.ru, intrați în raport și se trimite cererea pentru site-ul dumneavoastră.

Dar există și alte cereri — analitice și globale, care sunt făcute de analiști interni. În caz că mă întrebați, analiștii interni fac cereri doar pentru serviciile „Yandex”. Totuși, și serviciile „Yandex” reprezintă o parte semnificativă din toate datele. Aceste cereri nu sunt pentru contoare specifice, ci pentru o filtrare mai largă.

Cum să organizezi datele astfel încât să funcționeze eficient atât pentru un singur contor, cât și pentru cererile globale? Complexitatea este sporită de faptul că numărul cererilor în ClickHouse pe clusterul „Metrici” este de câteva mii pe secundă. În plus, cererile non-triviale, de exemplu, câteva mii pe secundă, un singur server ClickHouse nu poate gestiona.

Dimensiunea clusterului este de șase sute și ceva de servere. Dacă doar întindem o tabelă Distribuită peste acest cluster și trimitem acolo câteva mii de cereri, va fi și mai rău decât să le trimitem pe un singur server. Pe de altă parte, varianta în care datele sunt distribuite uniform, iar noi cerem de pe toate serverele, este complet exclusă.

Există o variantă diametral opusă. Imaginează-ți că vom sharda datele pe website-uri, iar cererea pentru un singur site va merge pe un singur shard. Acum clusterul va putea gestiona cu siguranță zece mii de cereri pe secundă, dar pe un singur shard, o cerere anume va funcționa prea încet. Aceasta nu va mai putea scalabilitate în funcție de capacitate. Mai ales dacă este vorba despre site-ul avito.ru. Nu va fi un secret dacă spun că Avito este unul dintre cele mai vizitate site-uri din Runet. Așadar, a-l procesa pe un singur shard ar fi o nebunie.

Prin urmare, schema de sharding este organizată într-un mod mai inteligent. Întregul cluster este împărțit în câteva grupuri pe care le numim straturi. În interiorul fiecărui grup sunt între zece și câteva zeci de sharde. Iar în total, aceste grupuri sunt treizeci și nouă.

Cum se scalează toate acestea? Numărul grupurilor nu se schimbă - a fost acum câțiva ani treizeci și nouă și a rămas așa. Dar în interiorul fiecăruia dintre ele, creștem treptat numărul de sharde pe măsură ce acumulăm date. Schema de sharding, în general, este astfel încât împărțirea în aceste grupuri se face pe baza site-urilor web, iar pentru a înțelege ce site se află pe ce grup, se utilizează o bază de date separată în MySQL. Un site - pe un singur grup. Și în interiorul acestuia, sharding-ul se face pe baza identificatorilor vizitatorilor.

Când înregistrăm, le împărțim în funcție de restul împărțirii identificatorului vizitatorului. Dar atunci când adăugăm un nou shard, schema de sharding se schimbă; continuăm să împărțim, dar în funcție de restul împărțirii la un alt număr. Asta înseamnă că un vizitator se află deja pe mai multe servere, și nu putem conta pe aceasta. Aceasta s-a realizat exclusiv pentru a comprima mai bine datele. Iar la cereri, ne îndreptăm către tabelul Distributed, care verifică clusterul și se adresează zecilor de servere. Așa arată schema aceasta ciudată.

Dar povestea mea va fi incompletă dacă nu spun că am renunțat la această schemă. În noua schemă, am schimbat totul și am copiat toate datele cu ajutorul clickhouse-copier.

În noua schemă, toate site-urile sunt împărțite în două categorii - mari și mici. Nu știu cum a fost ales praful, dar în rezultat, marile site-uri sunt înregistrate pe un singur cluster, cu 120 de sharde, fiecare având câte trei replici - adică 360 de servere. Iar schema de sharding este astfel încât orice cerere merge imediat către toate shardele. Dacă acum deschideți „Yandex.Metrica” și accesați orice pagină de raport pentru avito.ru, cererea va merge către 120 de servere. Site-urile mari în spațiul rus sunt puține. Și numărul cererilor nu este de o mie pe secundă, ci chiar mai puțin de o sută. Tot acest trafic este procesat cu ușurință de tabelul Distributed, pe care fiecare dintre ele îl procesează cu 120 de servere.

Iar al doilea cluster este pentru site-urile mici. Aici, schema de sharding se face pe baza identificatorului site-ului, iar fiecare cerere merge exact către un singur shard.

ClickHouse are o utilitară numită clickhouse-copier. Puteți să ne povestiți despre ea?

Voi spune imediat că această soluție este mai voluminoasă și ceva mai puțin performantă. Avantajul este că împrăștie datele complet conform schemei specificate de dumneavoastră. Dar dezavantajul utilitarului este că nu face deloc resharding. Aceasta copiază datele dintr-o schemă de cluster în alta schemă de cluster.

Asta înseamnă că pentru funcționarea sa aveți nevoie de două clustere. Acestea pot fi situate pe servere identice, dar, cu toate acestea, datele nu vor fi mutată incremental, ci vor fi copiate.

De exemplu, erau patru servere, acum sunt opt. Creezi o nouă tabelă Distributed pe toate serverele, noi tabele locale și lansezi clickhouse-copier, specificând în el schema de lucru, că acesta trebuie să citească de acolo, să accepte noua schemă de sharding și să mută datele acolo. Și vei avea nevoie de spațiu pe serverele vechi de o dată și jumătate mai mult decât există acum, deoarece datele vechi trebuie să rămână pe ele, iar în plus la ele vor veni jumătate din aceleași date vechi. Dacă ai gândit dinainte că datele trebuie regândite și există suficient spațiu, atunci această metodă va fi potrivită.

Cum este structurat clickhouse-copier? Împarte întreaga muncă într-un set de sarcini pentru procesarea unei partituri ale unei tabele pe un shard. Toate aceste sarcini pot fi efectuate în paralel, iar clickhouse-copier poate fi lansat pe mașini diferite în mai multe instanțe, dar ceea ce face pentru o partidă – nu este altceva decât un insert select. Datele sunt citite, dezarchivate, reorganizate, apoi din nou comprimate, scrise undeva, reorganizate. Aceasta este o soluție mai complexă.

Ați avut o caracteristică pilot numită resharding. Ce s-a întâmplat cu ea?

Ați avut încă în 2017 o versiune pilot care se numea resharding. Există chiar și o opțiune în ClickHouse. Înțeleg că nu a avut succes. Puteți să ne spuneți de ce s-a întâmplat asta? pare să fie destul de relevant.

Toată problema este că, atunci când este necesar să se reshardeze datele, este necesară o sincronizare destul de complicată pentru a face acest lucru atomic. Când am început să examinăm cum este realizată această sincronizare, a devenit clar că există probleme fundamentale. Și aceste probleme fundamentale nu sunt doar teoretice, ci au început imediat să se manifeste în practică sub forma a ceva ce poate fi explicat foarte simplu — nimic nu funcționează.

Este posibil să unificăm toate părțile datelor înainte de a le muta pe discuri lente?

Întrebare despre TTL cu opțiunea de mutare pe discuri lente în contextul merge-urilor. Există vreo modalitate, în afară de cron, de a fuziona toate părțile într-una înainte de a fi mutați pe discuri lente?

Răspunsul la întrebarea dacă poate fi cumva automatizat fuzionarea tuturor bucatelor într-una înainte de transfer — nu. Mi se pare că nu este necesar. Nu este necesar să fuzionăm toate părțile într-una, ci pur și simplu să ne bazăm pe faptul că vor fi mutate automat pe discuri lente.

Avem două criterii pentru migrare. Primul este pe baza gradului de ocupare. Dacă în nivelul curent de stocare există mai puțin de un anumit procent de spațiu liber, selectăm un chunk și îl transferăm pe un stocare mai lentă. De fapt, nu mai lentă, ci următoarea – așa cum ați configurat.

Al doilea criteriu este pe baza dimensiunii. Acesta se referă la migrarea chunk-urilor mari. Puteți ajusta pragul de spațiu liber pe discul rapid, iar datele vor fi migrate automat.

Cum putem să ne mutăm la versiunile noi ClickHouse dacă nu avem posibilitatea de a verifica compatibilitatea din timp?

Aceasta temă este discutată regulat în chat-ul de Telegram ClickHouse ținând cont de diferite versiuni, și totuși. Cât de sigur este să se actualizeze de la versiunea 19.11 la 19.16 și, de exemplu, de la 19.16 la 20.3. Cum este mai bine să se migreze la versiunile noi, fără posibilitatea de a verifica înainte compatibilitatea într-un sandbox?

Aici sunt câteva reguli „de aur”. Prima este să citiți changelog-ul. Este lung, dar există puncte separate despre modificările incompatibile cu versiunile anterioare. Nu trebuie să priviți aceste puncte ca pe un semnal de alarmă. De obicei, aceste incompatibilități sunt minore și sunt legate de o funcționalitate marginală, care, cel mai probabil, nu este utilizată de dvs.

A doua este – dacă nu aveți posibilitatea de a verifica compatibilitatea într-un sandbox și doriți să vă actualizați direct în producție, recomandarea este – nu trebuie să faceți asta. În primul rând, creați un sandbox și verificați. Dacă nu există un mediu de testare, atunci cel mai probabil nu aveți o companie foarte mare, așa că puteți copia o parte a datelor pe laptopul dvs. și verifica dacă totul funcționează corect. Puteți chiar să ridicați câteva replici local pe mașina dvs. Și sau puteți ridica o nouă versiune undeva în apropiere și să importați o parte a datelor – adică să creați un mediu de testare improvizat.

Încă o regulă – nu actualizați în termen de o săptămână de la lansarea versiunii din cauza prinderii bug-urilor în producție și a corecturilor rapide ulterioare. Hai să ne ocupăm de numerotarea versiunilor ClickHouse, pentru a nu ne confunda.

Există versiunea 20.3.4. Numărul 20 indică anul lansării - 2020. Din punct de vedere al conținutului intern, acest lucru nu are semnificație, așa că nu ne vom concentra pe asta. Mai departe - 20.3. A doua cifră - în acest caz 3 - o vom crește de fiecare dată când lansăm o versiune cu o funcționalitate nouă. Dacă dorim să adăugăm o capacitate în ClickHouse, trebuie să creștem acest număr. Asta înseamnă că în versiunea 20.4, ClickHouse va funcționa și mai bine. A treia cifră - 20.3.4. Aici 4 este numărul de patch-uri, în care nu am adăugat funcții noi, ci am corectat anumite bug-uri. Iar 4 înseamnă că am făcut asta de patru ori.

Nu trebuie să te gândești că este ceva îngrozitor. De obicei, utilizatorul poate instala cea mai recentă versiune, iar aceasta va funcționa fără probleme timp de un an. Dar imaginându-ne că, într-o funcție de procesare a bitmap-urilor, care a fost adăugată de colegii noștri chinezi, serverul se oprește atunci când sunt transmise argumente incorecte. Trebuie să corectăm asta. Vom lansa o nouă versiune patch, iar ClickHouse va deveni mai stabil.

Dacă ClickHouse-ul vostru funcționează în producție și apare o nouă versiune ClickHouse cu funcții suplimentare - de exemplu, 20.4.1 - nu vă grăbiți să o instalați în producție în prima zi. De ce este atât de necesară? Dacă încă nu folosiți ClickHouse, o puteți instala și, cel mai probabil, va fi totul bine. Dar dacă ClickHouse funcționează deja stabil, urmăriți patch-urile și actualizările - ce probleme corectăm.

Kirill Shvakov: Vreau să completez puțin despre mediile de testare. Toți se tem de mediile de testare și, dintr-un motiv oarecare, cred că, dacă aveți un cluster ClickHouse foarte mare, și mediul de testare trebuie să fie la fel de mare sau, cel puțin, de zece ori mai mic. Asta este complet greșit.

Pot spune din experiența mea. Am un proiect, iar acolo este ClickHouse. Mediul nostru de testare pentru el este o mică mașină virtuală în Hetzner, costând douăzeci de euro, unde este desfășurat absolut totul. Pentru a face asta, avem o automatizare completă în Ansible, așa că, în principiu, nu contează unde desfășurăm - pe servere fizice sau pur și simplu în mașini virtuale.

Ce poți face? Ar fi bine să adaugi în documentația ClickHouse un exemplu despre cum să îți configurezi un mic cluster – în Docker, în LXC, poate să creezi un playbook Ansible, pentru că fiecare are diverse modalități de desfășurare. Acest lucru ar simplifica multe. Atunci când poți configura un cluster în cinci minute, este mult mai ușor să încerci să înțelegi ceva. Este mult mai convenabil, pentru că a merge cu o versiune de producție pe care nu ai verificat-o – este un drum fără ieșire. Uneori funcționează, alteori nu. De aceea, a spera la succes – este o idee proastă.

Maxim Kotiakov, inginer backend senior la Avito: Voi adăuga puțin despre mediile de testare din seria problemelor companiilor mari. Avem un cluster de acceptanță ClickHouse complet, o copie exactă a datelor și configurației din producție. Acest cluster este desfășurat în containere destul de vechi, cu minimum de resurse. Scriem acolo un anumit procent din datele de producție, având posibilitatea de a replica fluxul în Kafka. Totul este sincronizat și scalat – atât din punct de vedere al puterii, cât și al fluxului, și, în teorie, în condiții de egalitate, ar trebui să se comporte similar cu producția în ceea ce privește metricile. Tot ceea ce este potențial exploziv este însărcinat mai întâi pe acest stand și stă acolo câteva zile până când devine gata. Dar, desigur, această soluție este costisitoare, grea și cu costuri de întreținere semnificative.

Alexey Milovidov: Voi povesti ce reprezintă mediu de testare pentru prietenii noștri de la «Yandex.Metrika». Un cluster avea peste 600 de servere, altul 360, și există și un al treilea, precum și câteva clustere. Mediul de testare pentru unul dintre ele – este pur și simplu două shard-uri cu câte două replici în fiecare. De ce două shard-uri? Ca să nu fie doar unul. Și replicile să fie și ele. O cantitate minimă pe care ți-o poți permite.

Acest mediu de testare permite verificarea funcționalității interogărilor și dacă nu s-a stricat ceva major. Dar adesea problemele apar dintr-un alt motiv, când totul funcționează, dar există anumite modificări minore în încărcare.

Voi da un exemplu. Am decis să instalăm o nouă versiune ClickHouse. Aceasta a fost lansată pe mediu de testare, au fost efectuate teste automatizate în cadrul «Yandex.Metrika», care compară datele din versiunea veche și cea nouă, trecând prin întregul proces. Și, desigur, am avut teste verzi în CI-ul nostru. Altfel nu am fi propus această versiune.

Totul este perfect. Începem să mergem în producție. Îmi vine un mesaj că încărcarea pe grafice a crescut de câteva ori. Facem rollback la versiune. Mă uit pe grafic și văd: încărcarea a crescut într-adevăr de câteva ori în timpul implementării, și a scăzut din nou când a fost implementată. Apoi am început să facem rollback la versiune. Și încărcarea a crescut exact la fel și a scăzut la fel de repede. Așadar, concluzia este că încărcarea a crescut în legătură cu implementarea, nimic surprinzător.

A fost greu să-i conving pe colegi să instaleze noua versiune. Le spun: „Totul este în regulă, implementați-o. Țineți degetele încrucișate, totul va merge bine. Acum încărcarea a crescut pe grafice, dar totul este în regulă. Rămâneți concentrați.” În general, așa am procedat și toată lumea — versiunea a fost implementată în producție. Dar aproape la fiecare implementare apar probleme asemănătoare.

Kill query ar trebui să oprească interogările, dar nu o face. De ce?

Un utilizator a venit la mine, un analist, și a creat o cerere care a blocat clusterul meu ClickHouse. O anumită nodă sau întregul cluster — în funcție de replica sau shard-ul în care a picat cererea. Văd că toate resursele CPU de pe acest server sunt pe roșu, totul este suprasolicitat. În același timp, ClickHouse răspunde la cereri. Și scriu: „Arată-mi, te rog, lista de procese, care cerere a generat acest haos.”

Găsesc această cerere și îi scriu kill. Și văd că nu se întâmplă nimic. Serverul meu este suprasolicitat, ClickHouse îmi dă în continuare comenzi, arată că serverul este activ și totul este bine. Dar am degradare în toate cererile utilizatorilor, încep să apară probleme în scrierea în ClickHouse, iar comanda mea kill nu funcționează. De ce? Credeam că kill query ar trebui să oprească cererile, dar acest lucru nu se întâmplă.

Acum va fi un răspuns destul de ciudat. Problema este că kill query nu oprește cererile.

Kill query setează un mic steag numit „vreau ca această cerere să fie oprită”. Și cererea, în timpul procesării fiecărui bloc, verifică acest steag. Dacă este setat, cererea încetează să funcționeze. Deci, nimeni nu oprește cererea, ea trebuie să verifice totul singură și să se oprească. Și asta ar trebui să funcționeze în toate cazurile când cererea se află în starea de procesare a blocurilor de date. Va procesa blocul următor de date, va verifica steagul și se va opri.

Acest lucru nu funcționează în cazurile în care cererea este blocată pentru o anumită operațiune. Totuși, cel mai probabil acesta nu este cazul dumneavoastră, deoarece, conform spuselor dumneavoastră, folosește o mulțime de resurse ale serverului. Este posibil ca acest lucru să nu funcționeze în cazul sortării externe și în câteva alte detalii. Dar în general, nu ar trebui să fie așa, este un bug. Și singurul lucru pe care îl pot sfătui este să actualizați ClickHouse.

Cum se calculează timpul de răspuns pentru o sarcină de citire?

Există un tabel în care sunt stocate agregate pe item - diferite contoare. Numărul de rânduri este de aproximativ o sută de milioane. Se poate conta pe un timp de răspuns previzibil dacă se generează 1K RPS pentru 1K iteme?

Judecând după context, este vorba despre o sarcină de citire, deoarece nu sunt probleme la scriere - se pot insera, fie câte o mie, fie câte o sută de mii, uneori chiar și câteva milioane de rânduri.

Există foarte multe tipuri de cereri de citire. În select 1, ClickHouse poate executa în jur de zeci de mii de cereri pe secundă, așadar chiar și cererile pe o singură cheie vor necesita anumite resurse. Iar astfel de cereri specifice vor fi mai complicate decât în bazele de date de tip key-value, deoarece pentru fiecare citire este necesar să se citească un bloc de date după index. Indexul nostru nu se referă la fiecare înregistrare, ci la fiecare interval. Așadar, va trebui să citiți întregul interval - aceasta este 8192 de rânduri în mod implicit. Și va trebui să decomprimați blocul de date comprimat de la 64 Kb la 1 Mb. De obicei, aceste cereri specifice durează câteva milisecunde. Dar acesta este cel mai simplu caz.

Să încercăm să facem o aritmetică simplă. Dacă înmulțim câteva milisecunde cu o mie, obținem câteva secunde. Ca și cum nu ai putea menține o mie de cereri pe secundă, dar de fapt se poate, deoarece avem mai multe nuclee de procesor. Așadar, în principiu, ClickHouse poate susține uneori 1000 RPS, dar la cereri scurte, adică specifice.

Dacă este necesar să scalați clusterul ClickHouse în funcție de numărul de cereri simple, atunci recomand cea mai simplă metodă - să creșteți numărul de replici și să trimiteți cererile la o replică aleatorie. Dacă o replică deține cinci sute de cereri pe secundă, ceea ce este complet real, atunci trei replici vor putea susține o mie și jumătate.

Uneori, desigur, poți configura ClickHouse pentru a obține un număr maxim de citiri punctuale. Ce trebuie să faci pentru asta? Primul pas este să reduci granularitatea indexului. Totuși, acest lucru nu trebuie să fie redus la unitate, ci în baza faptului că numărul de înregistrări din index va fi de câteva milioane sau zeci de milioane pe server. Dacă tabelul are o sută de milioane de rânduri, atunci granularitatea poate fi setată la 64.

Poți reduce dimensiunea blocului comprimat. Pentru asta, există setări min compress block size, max compress block size. Acestea pot fi reduse, datele pot fi reorganizate, iar atunci interogările punctuale vor fi mai rapide. Totuși, ClickHouse nu este o bază de date de tip key-value. Un număr mare de interogări mici este un antipattern de încărcare.

Kirill Shvakov: Îți voi da un sfat în cazul în care ai acolo conturi obișnuite. Este o situație destul de standard când în ClickHouse se stochează un fel de contor. Am un utilizator, el este dintr-o anumită țară, plus un alt câmp, și trebuie să crești incremental ceva. Ia-ți MySQL, creează o cheie unică - în MySQL este cheia duplicat, iar în PostgreSQL este conflict - și adaugă cu plus. Acest lucru va funcționa mult mai bine.

Când ai puține date, nu prea are sens să folosești ClickHouse. Există baze de date obișnuite, iar acestea se descurcă bine cu acest lucru.

Ce ar trebui să reglezi în ClickHouse pentru a avea mai multe date în cache?

Să presupunem că pe servere există 256 GB RAM, iar în rutina zilnică ClickHouse utilizează aproximativ 60-80 GB, la vârf — până la 130. Ce poți activa și regla pentru a avea mai multe date în cache și, în consecință, a reduce accesările pe disc?

În general, cache-ul de pagină al sistemului de operare se descurcă bine cu această sarcină. Dacă deschizi pur și simplu topul și te uiți la cached sau free — acolo este scris și cât este cache-uit — poți observa că toată memoria liberă este folosită pentru cache. Iar aceste date, la citire, vor fi citite nu de pe disc, ci din RAM. Îți pot spune că cache-ul este utilizat eficient, deoarece sunt cache-uite exact datele comprimate.

Cu toate acestea, dacă dorești să accelerezi și mai mult unele interogări simple, există posibilitatea de a activa în interiorul ClickHouse cache-ul pentru datele decompresate. Acesta se numește uncompressed cacheÎn fișierul de configurare config.xml, setați dimensiunea cache-ului necomprimat la valoarea dorită — recomand să nu depășească jumatate din memoria RAM disponibilă, deoarece restul va fi folosit pentru cache-ul de pagini.

În plus, există două setări la nivel de interogare. Prima setare este utilizarea cache-ului necomprimat — activează utilizarea acestuia. Se recomandă activarea pentru toate interogările, cu excepția celor complexe, care pot citi toate datele și astfel să golească acest cache. Iar a doua setare este ceva de genul numărului maxim de rânduri pentru utilizarea cache-ului. Aceasta limitează automat interogările mari astfel încât să ocolească cache-ul.

Cum putem configura storage_configuration pentru stocare în memorie?

În noua documentație ClickHouse, am citit o secțiune legată de stocarea datelor.În descriere este un exemplu cu SSD rapid.

Este interesant cum se poate configura același lucru cu memorie volumetrică rapidă. Și încă o întrebare. Cum funcționează select cu o astfel de organizare a datelor, va citi întreaga suită sau doar ceea ce se află pe disc, și dacă aceste date se comprimă în memorie? Și cum funcționează secțiunea prewhere cu o astfel de organizare a datelor?

Această setare influențează stocarea bucăților de date, iar formatul lor nu se schimbă.
Hai să examinăm mai în detaliu.

Poți configura stocarea datelor în memorie. Tot ce se configurează pentru disc este calea acestuia. Creezi o partiție tmpfs, care este montată pe un anumit drum în sistemul de fișiere. Indici această cale ca fiind calea pentru stocarea datelor pentru cea mai rapidă partiție, iar bucățile de date încep să fie primite și scrise acolo, totul fiind în regulă.

Dar nu recomand să faci asta din cauza fiabilității scăzute, deși, dacă ai cel puțin trei replici în centre de date diferite, atunci se poate. În caz contrar, datele vor fi restaurate. Să presupunem că serverul s-a oprit brusc și a fost repornit. Partiția a fost montată din nou, dar acolo este gol. Serverul ClickHouse, la pornire, observă că aceste bucăți lipsesc, deși, conform metadatelor ZooKeeper, ele ar trebui să fie. Se uită pe care replici există și le solicită, apoi le descarcă. Astfel, datele vor fi restaurate.

În acest sens, stocarea datelor în RAM nu se deosebește fundamental de stocarea lor pe disc, deoarece, la scrierea datelor pe disc, acestea ajung mai întâi în cache-ul de pagini și sunt scrise fizic ulterior. Acest lucru depinde de modul în care este montat sistemul de fișiere. Dar, pentru orice eventualitate, voi spune că ClickHouse nu face fsync la insert.

În același timp, datele din RAM sunt stocate în exact același format ca și pe disc. Comanda select la fel alege porțiuni care trebuie citite, selectează intervalele necesare de date și le citește. Și prewhere funcționează absolut la fel, indiferent dacă datele erau în RAM sau pe disc.

Până la ce număr de valori unice este eficient Low Cardinality?

Low Cardinality este ingenios proiectat. Acesta creează dicționare de date, dar acestea sunt locale. În primul rând, dicționarele sunt proprii fiecărei porțiuni, iar, în al doilea rând, chiar și în interiorul aceleași porțiuni, acestea pot fi diferite pentru fiecare interval. Când numărul valorilor unice atinge o limită - cred că un milion - dicționarul este pur și simplu amânat și se creează unul nou.

Răspunsul, în general: pentru fiecare interval local - de exemplu, pentru fiecare zi - unde sunt până la un milion de valori unice, Low Cardinality este eficient. După aceea, va fi pur și simplu un fallback, în care vor fi utilizate multe dicționare diferite, nu unul singur. Va funcționa aproximativ la fel ca o coloană de tip string, poate puțin mai puțin eficient, dar nu va exista o degradare semnificativă a performanței.

Care sunt cele mai bune practici pentru căutarea full-text într-un tabel cu cinci miliarde de rânduri?

Există diferite variante de răspuns. Prima - să spunem că ClickHouse nu este un sistem pentru căutare full-text. Pentru aceasta există sisteme speciale, de exemplu, Elasticsearch și Sphinx. Cu toate acestea, întâlnesc tot mai des oameni care spun că trec de la Elasticsearch la ClickHouse.

De ce se întâmplă acest lucru? Ei explică că Elasticsearch nu mai face față sarcinilor la anumite volume, începând cu problemele referitoare la construcția indexurilor. Indexurile devin prea voluminoase, iar, dacă se transferă pur și simplu datele în ClickHouse, rezultatul va fi că acestea sunt stocate de câteva ori mai eficient ca volum. În acest context, interogările de căutare au fost adesea diferite, în sensul că nu era necesar să se caute într-un volum mare de date o anumită expresie ținând cont de morfologie, ci cu totul altceva. De exemplu, a găsi în ultimele câteva ore în jurnale după o anumită subsecvnță de bytes.

În acest caz, în ClickHouse creați un index, al cărui prim câmp va fi data cu ora. Iar cea mai mare filtrare a datelor va fi tocmai pe baza intervalului de date. În interiorul intervalului de date selectat, de obicei, se poate efectua o căutare completă de text, chiar și prin metoda brute force, folosind like. Operatorul like din ClickHouse este cel mai eficient operator like pe care îl puteți găsi. Dacă găsiți unul mai bun, spuneți-mi.

Dar, totuși, like este un scan complet. Iar scanul complet poate fi lent nu doar pe CPU, ci și pe disk. Dacă, de exemplu, aveți un terabyte de date pe zi și căutați o anumită cuvânt într-o zi, va trebui să scanați un terabyte. Și acesta va fi, cu siguranță, pe discuri hard obișnuite, iar în cele din urmă vor fi atât de ocupate încât nu veți putea accesa acel server prin SSH.

În acest caz, sunt pregătit să propun încă o mică truc. Este din categoria experimentală - poate funcționa, poate nu. În ClickHouse există indecși de text complet sub formă de filtre Bloom trigram. Colegii noștri de la compania Arenadata au testat deja acești indecși, și adesea funcționează exact așa cum este destinat.

Pentru a-i folosi corect, este necesar să înțelegeți bine cum anume funcționează: ce reprezintă un filtru Bloom trigram și cum să alegi dimensiunea acestuia. Pot spune că vor ajuta pentru interogări pe niște fraze rare, substrings care apar rar în date. În acest caz, pe indecși vor fi selectate subintervale, și se va citi mai puțin date.

Recent, în ClickHouse au apărut funcții și mai avansate pentru căutarea textului complet. Asta, pe de o parte, căutarea mai multor substrings simultan într-o singură trecere, inclusiv opțiuni cu sensibilitate la majuscule, fără sensibilitate la majuscule, cu suport UTF-8 sau doar pentru ASCII. Alegeți cel mai eficient, de care aveți nevoie.

A apărut de asemenea căutarea mai multor expresii regulate într-o singură trecere. Nu trebuie să scrieți X like un substring sau X like un alt substring. Pur și simplu scrieți și totul se execută maxim eficient.

Al treilea - acum există căutare aproximativă a regex-urilor și căutare aproximativă a substrings. Dacă cineva a scris un cuvânt cu o greșeală de tipar, acesta va fi căutat după cea mai mare corespondență.

Cum organizăm mai bine accesul în ClickHouse pentru un număr mare de utilizatori?

Spuneți-ne cum să organizăm mai bine accesul pentru un număr mare de consumatori și analiști. Cum să formăm o coadă, să prioritizăm cererile max concurrent queries și ce instrumente să folosim?

Dacă clusterul este suficient de mare, o soluție bună ar fi să ridicăm încă două servere, care să devină puncte de acces pentru analiști. Adică să nu lăsăm analiștii să acceseze șardurile concrete ale clusterului, ci să creăm două servere goale, fără date, și să configurăm drepturile de acces pe acestea. În acest timp, setările utilizatorilor pentru cererile distribuite sunt transmise către serverele externe. Adică configurați totul pe aceste două servere, iar setările au efect asupra întregului cluster.

În principiu, aceste servere sunt fără date, dar volumul de RAM pe ele este foarte important pentru executarea cererilor. De asemenea, discul poate fi utilizat pentru date temporare, dacă agregarea externă sau sortarea externă este activată.

Este important să ne uităm la setările care sunt legate de toate limitele posibile. Dacă acum intru în clusterul „Yandex.Metrica” ca analist și fac o cerere select count from hits, atunci voi primi imediat o excepție că nu pot executa cererea. Numărul maxim de rânduri pe care am voie să le scanesc este de o sută de miliarde, în timp ce în cluster există cincizeci de trilioane într-o singură tabelă. Aceasta este prima limitare.

Să presupunem că elimin limita de număr de rânduri și execut cererea din nou. Atunci voi vedea următoarea excepție - setarea este activată force index by date. Nu pot executa cererea dacă nu am specificat intervalul de date. Nu trebuie să ne așteptăm ca analiștii să-l indice manual. Un caz tipic este că intervalul de date este scris where event date between săptămâna. Și apoi, poate din greșeală, s-a indicat paranteza greșită, iar în loc de and a ieșit or — or URL match. Dacă nu există limitare, aceasta va scana coloana URL și va consuma o grămadă de resurse.

În plus, ClickHouse are două setări de priorități. Din păcate, acestea sunt foarte primitive. Una se numește pur și simplu priority. Dacă prioritatea ≠ 0, și se execută cereri cu o anumită prioritate, dar în același timp se execută o cerere cu o prioritate care are o valoare mai mică, ceea ce înseamnă o prioritate mai înaltă, atunci cererea cu valoarea prioritară mai mare, care indică o prioritate mai mică, pur și simplu se suspendă și nu va funcționa deloc în acel interval de timp.

Aceasta este o configurare foarte brută și nu este potrivită pentru acele cazuri când pe cluster există o sarcină constantă. Dar dacă aveți cereri scurte și impulsive care sunt importante, iar în principal clusterul este inactiv, o astfel de configurare va funcționa.

Următoarea configurare a prioritatilor se numește prioritatea firului OS. Aceasta pur și simplu stabilește pentru toate firele de execuție a cererii valoarea nice pentru scheduler-ul Linux. Funcționează destul de bine, dar totuși funcționează. Dacă se setează cea mai mică valoare nice - aceasta este cea mai mare ca mărime și înseamnă cea mai mică prioritate - iar pentru cererile cu prioritate mare se setează -19, atunci CPU va consuma cereri de prioritate mică de aproximativ patru ori mai puțin decât cele de prioritate mare.

De asemenea, trebuie să setați timpul maxim de execuție a cererii - să zicem, cinci minute. Viteza minimă de execuție a cererii - aceasta este cea mai lipsită de riscuri. Această configurare există de mult timp și este necesară pentru a nu afirma doar că ClickHouse nu se blochează, ci pentru a forța acest lucru.

Imaginați-vă că setați: dacă vreo cerere procesează mai puțin de un milion de rânduri pe secundă - nu ar trebui să se facă așa. Aceasta ne denigrează buna reputație, baza noastră de date bună. Să interzicem acest lucru. Acolo, de fapt, există două configurări. Una se numește viteza minimă de execuție — în rânduri pe secundă, iar cealaltă se numește timpul de așteptare înainte de a verifica viteza minimă de execuție - implicit, cincisprezece secunde. Asta înseamnă că cincisprezece secunde sunt acceptabile, iar apoi, dacă este lent, pur și simplu trebuie să aruncați o excepție - să întrerupeți cererea.

De asemenea, trebuie să configurați cotele. În ClickHouse există o funcționalitate încorporată de cote care contabilizează consumul de resurse. Dar, din păcate, nu resursele fizice precum CPU, discuri, ci logice - numărul de cereri procesate, rânduri și bytes citite. Și puteți configura, de exemplu, un maximum de o sută de cereri în cinci minute și o mie de cereri pe oră.

De ce este important? Pentru că o parte din cererile de analiză vor fi efectuate manual direct din clientul ClickHouse. Și totul va fi bine. Dar dacă în compania dumneavoastră există analiști avansați, aceștia vor scrie un script, iar în script ar putea fi o eroare. Iar acea eroare va duce la executarea cererii într-un ciclu infinit. De aceasta trebuie să ne protejăm.

Este posibil să trimitem rezultatele unei interogări la zece clienți?

Avem câțiva utilizatori care iubesc să vină cu cereri foarte mari în același moment de timp. Cererea este mare, se execută în principiu rapid, dar din cauza că sunt multe astfel de cereri simultan, devine foarte dureros. Poate aceeași cerere, care a venit de zece ori la rând, să fie executată o dată, iar rezultatul să fie predat zece clienților?

Problema este că nu avem rezultate din cache sau cache de date intermediare. Există cache-ul de pagini al sistemului de operare, care va permite să nu citim datele de pe disc din nou, dar, din păcate, datele vor fi totuși decompressate, deserializate și procesate din nou.

Ne-am dori să evităm cumva acest lucru, fie prin cache-ul datelor intermediare, fie prin organizarea cererilor asemănătoare într-o coadă și adăugarea unui cache pentru rezultate. Acum avem în dezvoltare un pull request care adaugă cache pentru cereri, dar doar pentru subcereri în secțiunea in și join — adică soluția este incompletă.

Cu toate acestea, avem și noi această situație. Exemplul canonic este cererile cu paginare. Există un raport, are mai multe pagini și se face cererea limit 10. Apoi același lucru, dar limit 10,10. Apoi încă o pagină următoare. Și se întreabă, de ce calculăm toate acestea de fiecare dată? Dar în prezent nu există nicio soluție și nu se poate evita.

Există o soluție alternativă, care se instalează ca sidecar lângă ClickHouse — ClickHouse Proxy.

Kirill Shvakov: În ClickHouse Proxy există un limitator de rată încorporat și un cache de rezultate încorporat. S-au realizat foarte multe configurări, deoarece s-a rezolvat o problemă similară. Proxy-ul permite limitarea cererilor, organizându-le într-o coadă, și se poate configura cât timp durează cache-ul cererilor. Dacă cererile au fost într-adevăr identice, Proxy-ul le va returna de mai multe ori, dar va accesa ClickHouse doar o dată.

Nginx are, de asemenea, un cache în versiunea gratuită, și acesta va funcționa. Nginx are chiar și setări care, dacă sunt primite cereri simultane, va încetini alte cereri până când una este executată. Dar în ClickHouse Proxy, setarea este mult mai bine realizată. A fost creat exact pentru ClickHouse, pentru aceste cereri, deci este mai potrivit. Și se instalează ușor.

Ce să facem cu operațiile asincrone și cu vizualizările materializate?

Există o problemă, anume că operațiunile cu motorul de înlocuire sunt asincrone – mai întâi datele sunt scrise, apoi se face comprimarea acestora. Dacă sub tabel există un tabel materializat cu anumite agregate, dublările vor fi scrise în el. Și dacă nu există o logică complexă, datele vor fi duplicate. Ce se poate face în acest sens?

Există o soluție evidentă – implementarea unui trigger pentru un anumit tip de materializări în cadrul operației de comprimare asincronă. Există vreo «gloanțe argintii», planuri pentru implementarea unor astfel de funcționalități?

Este bine să înțelegem cum funcționează deduplicarea. Ceea ce voi povesti acum nu ține de întrebare, dar e bine să ne amintim de asta pentru orice eventualitate.

La inserarea în tabelul replicat există deduplicarea blocurilor întregi inserate. Dacă ați inserat din nou același bloc, conținând același număr de aceleași rânduri în aceeași ordine, datele sunt deduplicate. Veți primi „Ok” ca răspuns la inserare, dar de fapt va fi scris un singur bloc de date, iar acesta nu va fi duplicat.

Acest lucru este necesar pentru siguranță. Dacă în timpul inserării ați primit „Ok”, înseamnă că datele dumneavoastră au fost inserate. Dacă ați primit o eroare de la ClickHouse, înseamnă că ele nu au fost inserate și inserarea trebuie repetată. Dar dacă conexiunea s-a întrerupt în timpul inserării, nu știți dacă datele au fost inserate sau nu. Singura opțiune este să repetați inserția din nou. Dacă datele au fost deja inserate și le inserați din nou, există deduplicarea blocurilor. Aceasta este necesară pentru a evita duplicatele.

Și este important cum funcționează pentru vederile materializate. Dacă datele au fost deduplicitate la inserarea în tabelul principal, atunci acestea nu vor merge nici în vederea materializată.

Acum, referitor la întrebare. Aveți o situație mai complicată, deoarece înregistrați duplicate ale unor linii individuale. Adică, nu întreaga pachetă este duplicată, ci anume linii specifice, iar acestea se comprimă în fundal. Într-adevăr, datele se vor comprima în tabela principală, iar în reprezentarea materializată vor merge datele necomprimate, și la îmbinări nu se va întâmpla nimic cu reprezentările materializate. Pentru că reprezentarea materializată este nimic altceva decât un trigger pe inserare. În alte operațiuni, nimic suplimentar nu se întâmplă cu ea.

Și nu pot să ofer nicio bucurie aici. Trebuie doar să căutăm o soluție specifică pentru acest caz. De exemplu, se poate realiza un înlocuirea în reprezentarea materializată, iar modul de deduplicare poate funcționa la fel. Dar, din păcate, nu întotdeauna. Dacă este agregator, atunci nu va funcționa.

Kirill Shvakov: Și noi am avut, la vremea noastră, un fel de construcție ciudată. A existat problema că există afișări de reclame și există unele date pe care le putem arăta în timp real — acestea sunt doar afișări. Rareori se duplicatează, dar, dacă se întâmplă, le vom comprima oricum mai târziu. Și au existat lucruri care nu se pot dublă - clicurile și toată această poveste. Dar ne-ar fi plăcut să le afișăm cât mai repede.

Cum au fost realizate reprezentările materializate? Au fost reprezentări în care se scria direct - se înregistra în datele brute și se scria în vizualizări. Acolo, la un moment dat, datele nu sunt foarte corecte, se duplicatează și așa mai departe. Și există a doua parte a tabelei, unde acestea arată absolut la fel ca reprezentările materializate, adică din punct de vedere al structurii sunt absolut identice. O dată la un anumit interval, recalculăm datele, completăm datele fără duplicate, scriem în acele tabele.

Am mers prin API - manual în ClickHouse nu va funcționa. Și API-ul vede: când am data ultimei adăugări în tabelă, unde datele sunt garantat corecte și calculate, și face o solicitare la o tabelă și la alta. Din una aleg până la o anumită perioadă de timp, iar din cealaltă completează ceea ce încă nu a fost calculat. Și funcționează, dar nu cu un singur ClickHouse.

Dacă aveți o API, fie pentru analiști, fie pentru utilizatori, atunci, în principiu, acesta este un opțiune. Întotdeauna verificați, întotdeauna recalculați. Acest lucru poate fi făcut o dată pe zi sau în alt moment. Voi alegeți intervalul care nu este necesar și nu este critic pentru voi.

Există multe jurnale în ClickHouse. Cum pot vedea tot ce se întâmplă cu serverul în timp real?

În ClickHouse există o cantitate foarte mare de jurnale diverse, iar această cantitate crește. În versiunile noi, unele dintre ele sunt activate implicit, iar în versiunile mai vechi trebuie activate în timpul actualizării. Cu toate acestea, devin tot mai multe. Mi-ar plăcea să pot vedea în final ce se întâmplă acum cu serverul meu, poate pe un dashboard sumar.

Nu aveți în echipa ClickHouse sau în echipele prietenilor voștri care să susțină o anumită funcționalitate a dashboard-urilor gata făcute, care să afișeze aceste jurnale ca un produs deja gata? În final, a privi jurnalele în ClickHouse este grozav. Dar ar fi foarte fain dacă ar fi deja pregătite sub formă de dashboard. Aș aprecia asta.

Există dashboard-uri, dar ele nu sunt standardizate. La noi în companie, aproximativ 60 de echipe folosesc ClickHouse, iar cel mai ciudat este că multe dintre ele au dashboard-uri pe care le-au făcut singuri, și sunt puțin diferite. Unele echipe folosește o instalare internă a „Yandex.Cloud”. Acolo sunt câteva rapoarte gata făcute, deși nu toate cele necesare. Alte echipe au propriile lor.

Colegiile mele de la „Metrics” au un dashboard în Grafana, iar eu am unul pe clusterul lor. Vedeți lucruri de genul cache hit pentru cache-ul de marcaje. Și mai complicat este că folosim instrumente diferite. Mi-am creat dashboard-ul pe un instrument foarte vechi, numit Graphite-web. Este complet inestetic. Și așa continui să-l folosesc, deși Grafana ar fi, probabil, mai convenabilă și mai estetică.

Elementul de bază din dashboard-uri este același. Acestea sunt metricle sistemice pentru cluster: CPU, memorie, disc, rețea. Alte metrici sunt numărul de cereri simultane, numărul de fuziuni simultane, numărul de cereri pe secundă, numărul maxim de fragmente pentru partițiile tabelelor MergeTree, întârzierea replicării, dimensiunea cozii de replicare, numărul de rânduri inserate pe secundă, numărul de blocuri inserate pe secundă. Acestea sunt toate metricile care nu provin din loguri, ci din metricle.

Vladimir Kolobaev: Alexey, aș dori să corectez puțin. Există Grafana. Grafana are un datasource, care este ClickHouse. Adică pot face cereri direct în ClickHouse din Grafana. În ClickHouse există o tabelă cu loguri, care este aceeași pentru toată lumea. Vreau ca în Grafana să mă refer la această tabelă de loguri și să văd cererile trimise de serverul meu. Ar fi grozav să am un astfel de dashboard.

L-am creat eu însumi. Dar am o întrebare – dacă totul este standardizat, iar Grafana este utilizată de toată lumea, de ce nu există un astfel de dashboard oficial la „Yandex”?

Kirill Shvakov: De fapt, datasource-ul care se leagă de ClickHouse este acum susținut de Altinity. Și vreau doar să ofer o direcție pentru explorare și pentru a implica pe cineva. Se poate întreba pe ei, pentru că „Yandex” până la urmă dezvoltă ClickHouse, nu povestea din jurul său. Altinity este principala companie care promovează acum ClickHouse. Ei nu-l vor abandona, ci vor continua să-l susțină. Deoarece, în principiu, pentru a încărca un dashboard pe site-ul Grafana, trebuie doar să te înregistrezi și să-l încarci – nu sunt probleme deosebite.

Alexey Milovidov: În ultimul an, ClickHouse a adăugat multe funcții pentru profilarea cererilor. Există metricle pentru fiecare cerere în ceea ce privește utilizarea resurselor. Și recent a fost adăugat un profiler și mai la nivel de bază pentru cereri, pentru a vedea unde fiecare cerere petrece fiecare milisecundă. Dar pentru a folosi această funcționalitate, trebuie să deschid clientul de consolă și să tastez cererea pe care o uit constant. Am salvat-o undeva și uit mereu unde anume.

Mi-aș dori să existe un instrument care să arate pur și simplu - iată interogările tale complexe, grupate pe clase de interogări. Am apăsat pe una și mi s-ar spune că este complicată din acest motiv. În prezent, nu există o astfel de soluție. Și este cu adevărat ciudat că, atunci când oamenii mă întreabă: „Spuneți, există panouri gata făcute pentru Grafana?”, eu spun: „Mergeți pe site-ul Grafana, acolo este comunitatea „Panouri”, și acolo este un panou de la Dima, un panou de la Kostyan. Ce este acest lucru, nu știu, eu personal nu l-am folosit.”

Cum pot influența merge-urile astfel încât serverul să nu cadă în OOM?

Am o tabelă, iar în tabelă există doar o singură partiție, aceasta fiind ReplacingMergeTree. Am scris date în ea timp de patru ani. A trebuit să fac un alter și să șterg câteva date.

Am făcut asta, iar în timpul procesării acestei interogări toată memoria de pe serverele din cluster a fost consumată și toate serverele din cluster au căzut în OOM. Apoi, toate au reușit să se repornească, au început să efectueze merge-ul aceleași operații, acel bloc de date, și au căzut din nou în OOM. Apoi s-au repornit din nou și au căzut din nou. Și acest lucru nu s-a oprit.

Apoi s-a dovedit că, de fapt, acesta a fost un bug pe care băieții l-au reparat. Este foarte bine, mulțumesc mult. Dar a rămas o amărăciune. Și acum, când mă gândesc că trebuie să fac un merge în tabelă, am o întrebare - de ce nu pot cumva să influențez aceste merge-uri? De exemplu, să le limitez în funcție de cantitatea de memorie RAM necesară sau în general în funcție de numărul lor, care vor procesa strict această tabelă.

Am o tabelă numită „Metrici”, te rog să o procesezi pentru mine în două fire. Nu trebuie să creezi zece sau cinci merge-uri în paralel, fă-o în două. Cred că în două am suficientă memorie, dar pentru zece poate să nu îmi ajungă. De ce mi-e frică? Pentru că tabela crește și, cândva, mă voi confrunta cu situația că din principiu nu din cauza unui bug, ci din cauza că datele se vor schimba într-un număr atât de mare, încât pur și simplu nu voi avea memorie pe server. Atunci serverul va cădea în OOM în timpul merge-ului. Și pot anula mutația, dar merge-urile deja nu.

Știți, la fuziuni, serverul nu va cădea în OOM, deoarece pentru fuziune se utilizează doar o cantitate mică de memorie RAM pe un mic interval de date. Așadar, totul va fi bine, indiferent de volumul de date.

Vladimir Kolobaev: Bine. Aici este o problemă, că după ce am realizat corectura de bug, am descărcat o nouă versiune și pe o altă tabelă, mai mică, unde sunt multe partiții, am realizat o operațiune similară. Și în timpul fuziunii, pe server au fost consumati aproximativ 100 GB de memorie RAM. Aveam 150 ocupati, 100 a fost folosit, și mi-a mai rămas un spațiu de 50 GB, așa că nu am căzut în OOM.

Ce mă protejează în acest moment de a cădea în OOM, dacă acesta consumă cu adevărat 100 GB de memorie RAM? Ce ar trebui să fac în situația în care, dintr-o dată, memoria RAM se va termina în timpul fuziunilor?

Alexey Milovidov: Există o problemă, că consumul de memorie RAM pe fuziuni nu este limitat. Și o a doua problemă este că, dacă o fuziune a fost planificată, trebuie să fie executată, deoarece este înregistrată în jurnalul de replicare. Jurnalul de replicare sunt acele acțiuni necesare pentru a aduce replica într-o stare consistentă. Dacă nu se efectuează manevre manuale, care să revină înapoi în acest jurnal de replicare, fuziunea va trebui să fie efectuată, într-un fel sau altul.

Desigur, nu ar fi rău să existe o limitare a memoriei RAM, care "în caz de urgență" să protejeze împotriva OOM. Aceasta nu va ajuta fuziunea să fie executată, va începe din nou, va ajunge la un anumit prag, va arunca o excepție și apoi va începe din nou - nu va ieși nimic bun din aceasta. Dar, în principiu, ar fi util să se introducă această limitare.

Cum va decurge dezvoltarea driverului Golang pentru ClickHouse?

Driverul Golang, pe care l-a scris Kirill Shvakov, este acum oficial susținut de echipa ClickHouse. El se află în depozitul ClickHouse, acum este mare și adevărat.

O mică remarcă. Există un depozit minunat, apreciat de toată lumea, de forme normale de ordini infinite — acesta este Vertica. De asemenea, au un driver oficial pentru python, care este susținut de dezvoltatorii Vertica. Au fost de mai multe ori situații în care versiunile depozitului și versiunile driverului s-au desincronizat destul de mult, iar driverul a ajuns în acel moment să nu mai funcționeze. Și un alt aspect. Suportul pentru acest driver oficial, mi se pare, se desfășoară prin sistemul „niplu” — scrii o problemă și aceasta rămâne suspendată pentru totdeauna.

Am două întrebări. Acum, driverul lui Kiril pentru Golang este aproape modul standard de comunicare din Golang cu ClickHouse. Doar că cineva comunică în continuare prin interfața http, pentru că așa îi place. Cum va decurge dezvoltarea acestui driver? Se va sincroniza cu unii schimbări majore în depozit? Și care este procedura de examinare a problemelor?

Kirill Shvakov: Primul — cum este organizat totul din punct de vedere birocratic. Acest aspect nu a fost discutat, așa că nu am ce răspunde.

Pentru a răspunde la întrebarea despre probleme, este nevoie de o mică istorie a driverului. Am lucrat într-o companie care avea multe date. Era o platformă publicitară cu un număr uriaș de evenimente care trebuiau stocate undeva. Și la un moment dat a apărut ClickHouse. Am început să le trimitem date și, la început, totul mergea bine, dar apoi ClickHouse a căzut. La acea vreme, am decis că nu avem nevoie de el.

După un an, ne-am întors la ideea de a folosi ClickHouse și aveam nevoie să scriem date în el. Cerința inițială era aceasta — hardware-ul era foarte slab, resursele erau puține. Dar noi am muncit întotdeauna așa, așa că ne-am îndreptat atenția către protocolul nativ.

Fiindcă lucram pe Go, era clar că aveam nevoie de un driver pe Go. Am lucrat la el practic cu normă întreagă — aceasta era sarcina mea de muncă. Până într-un anumit moment, l-am finalizat, și, în principiu, nimeni nu se aștepta că altcineva în afară de noi îl va folosi. Apoi a venit CloudFlare cu exact aceeași problemă, și pentru o vreme am colaborat foarte bine cu ei, pentru că aveau aceleași sarcini. Și am făcut acest lucru atât în ClickHouse însăși, cât și în driver.

La un moment dat, am încetat să mă mai ocup cu ele, pentru că activitatea mea în ceea ce privește ClickHouse și modul în care lucrez s-a schimbat puțin. Prin urmare, problemele nu se rezolvă. Periodic, oameni comit în depozit pentru că au nevoie de ceva. Atunci mă uit la pull request și uneori chiar corectez ceva, dar asta se întâmplă rar.

Aș dori să revin la driver. Cu câțiva ani în urmă, când a început totul, ClickHouse era diferit și avea alte capabilități. Acum există o înțelegere despre cum să remodelăm driverul pentru a funcționa bine. Dacă va avea loc această schimbare, versiunea 2 va fi în orice caz incompatibilă din cauza soluțiilor improvizate acumulate.

Nu știu cum să organizez acest lucru. Personal, nu am atât de mult timp. Dacă cineva va contribui la dezvoltarea driverului, îi voi putea ajuta și le voi explica ce trebuie să facă. Dar participarea activă a „Yandex” în dezvoltarea proiectului nu a fost discutată până acum.

Alexey Milovidov: De fapt, nu există nicio birocrație cu privire la acești drivere. Singurul lucru este că au fost transferate într-o organizație oficială, ceea ce înseamnă că acest driver este recunoscut ca soluția oficială implicită pentru Go. Există și alte drivere, dar acestea sunt separate.

La noi, în interior, nu există nicio dezvoltare pentru acești drivere. Problema este dacă putem angaja o persoană separată, nu special pentru acest driver, ci pentru dezvoltarea tuturor driverelor comunității, sau dacă putem găsi pe cineva din exterior.

Dicționarul extern nu se încarcă după repornire cu setarea lazy_load activată. Ce ar trebui să fac?

Avem activată setarea lazy_load și după repornirea serverului, dicționarul nu se încarcă de la sine. Se încarcă doar după ce un utilizator accesează acest dicționar. Și la prima accesare generează o eroare. Se poate încărca dicționarul automat prin ClickHouse, sau trebuie să monitorizăm mereu disponibilitatea lor, astfel încât utilizatorii să nu primească erori?

Poate că avem o versiune mai veche de ClickHouse, de aceea dicționarul nu s-a încărcat automat. Este posibil?

În primul rând, dicționarele pot fi încărcate forțat printr-o interogare system reload dictionaries. În al doilea rând, în ceea ce privește eroarea — dacă dicționarul a fost deja încărcat, interogările vor funcționa pe datele care au fost încărcate. Dacă dicționarul nu a fost încărcat, atunci va fi încărcat în timpul interogării.

Pentru dicționare mari, acest lucru nu este foarte convenabil. De exemplu, trebuie să extragi un milion de rânduri din MySQL. Cineva face un simplu select, dar acest select va aștepta acele milioane de rânduri. Există două soluții aici. Prima – dezactivarea lazy_load. A doua – atunci când serverul se pornește, înainte de a-i aplica sarcina, să faci system reload dictionary sau să execuți pur și simplu o interogare care folosește dicționarul. Atunci dicționarul va fi încărcat. Trebuie să controlăm noi înșine disponibilitatea dicționarelor cu setarea lazy_load activată, pentru că ClickHouse nu le încarcă automat.

La ultima întrebare răspunsul este – fie versiunea este veche, fie trebuie să depanei.

Cum să procedez când reîncărcarea sistemului nu încarcă niciunul din numeroasele dicționare, dacă măcar unul dintre ele eșuează cu o eroare?

Mai este o întrebare despre system reload dictionaries. Avem două dicționare – unul nu se încarcă, iar celălalt se încarcă. System reload dictionaries în acest caz nu va încărca niciunul dintre dicționare, și trebuie să le încărcăm punctual pe cel specific prin numele acestuia folosind system reload dictionary. Este oare acest lucru legat de versiunea ClickHouse?

Vreau să te bucur. Acest comportament s-a schimbat. Așadar, dacă actualizezi ClickHouse, acesta se va schimba și el. Dacă nu ești mulțumit de comportamentul actual system reload dictionaries, actualizează-te și să sperăm că va evolua în direcția bună.

Există vreo modalitate de a configura credentialele în configurația ClickHouse, dar să nu le expunem în cazul erorilor?

Întrebarea următoare este despre erorile legate de dicționar, și anume detalii de conectare. Am specificat detaliile de conectare în configurația ClickHouse pentru dicționar, și în caz de eroare, aceste detalii și parola sunt returnate.

Am rezolvat această eroare prin mutarea detaliilor în configurația driverului ODBC. Există o modalitate de a configura detaliile în configurația ClickHouse, dar fără a le expune în caz de eroare?

Aici soluția este într-adevăr – să specifici aceste credentials în odbc.ini, iar în ClickHouse să specifici doar Numele Surselor de Date ODBC. Pentru celelalte surse de dicționare nu va exista așa ceva – nici pentru dicționarul cu MySQL, nici pentru celelalte nu ar trebui să vezi parola în mesajul de eroare. Pentru ODBC voi verifica și eu – dacă există așa ceva, trebuie pur și simplu eliminat.

Bonus: fundaluri pentru Zoom din întâlniri

La clic pe imagine, pentru cei mai perseverenți cititori se vor deschide fundaluri bonus de întâlniri. Stingem incendiul împreună cu mascoturile tehnologiilor Avito, discutăm cu colegii din camera administratorului de sistem sau dintr-un club de computere retro, și avem întâlniri sub un pod pe fundalul graffiti-urilor.

ClickHouse pentru utilizatori avansați în întrebări și răspunsuri

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster