ClickHouse for avancerede brugere i spørgsmål og svar

I april samlede Avitos ingeniører sig til online-møder med hovedudvikleren af ClickHouse, Alexey Milovidov, og Kirill Shvakov, en Golang-udvikler fra Integros. De diskuterede, hvordan vi bruger databasesystemet, og hvilke udfordringer vi oplever.

Som følge af mødet har vi samlet en artikel med ekspertudtalelser på vores og seernes spørgsmål om backups, resharding af data, eksterne ordbøger, Golang-driveren og opdatering af ClickHouse-versioner. Den kan være nyttig for udviklere, der allerede aktivt arbejder med Yandex' DBMS og er interesserede i dens nutid og fremtid. Som standard er svarene fra Alexey Milovidov, medmindre andet er angivet.

Pas på, der er meget tekst under klummen. Vi håber, at indholdet med spørgsmålene vil hjælpe dig med at orientere dig.

ClickHouse for avancerede brugere i spørgsmål og svar

Indhold

Hvis du ikke har lyst til at læse teksten, kan du se optagelsen af mødet. på vores YouTube-kanal. Tidskoderne er i den første kommentar under videoen.

ClickHouse opdateres konstant, mens vores data ikke gør. Hvad skal vi gøre ved det?

ClickHouse opdateres konstant, men vores data, som er blevet optimalt behandlet, opdateres ikke og ligger i backup.

Antag, at vi har haft et problem, og dataene blev tabt. Vi besluttede os for at gendanne, og det viste sig, at de gamle partitioner, der ligger gemt på backup-serverne, stærkt afviger fra den version af ClickHouse, vi bruger på nuværende tidspunkt. Hvad skal man gøre i en sådan situation, og er det muligt?

Situationen, hvor du gendanner data fra backup i et gammelt format, og de ikke kan forbindes i den nye version, er umulig. Vi sikrer, at dataformatet i ClickHouse altid forbliver bagudkompatibelt. Dette er meget vigtigere end bagudkompatibilitet med funktionalitet, hvis adfærden af en sjældent brugt funktion er ændret. Data, der er gemt på disk, skal den nye version af ClickHouse altid kunne læse. Det er en regel.

Hvad er de bedste praksisser i øjeblikket for backup af data fra ClickHouse?

Hvordan laver man backups med det i betragtning, at vi har optimise final operationer, en enorm database på terabyte, og data der opdateres, lad os sige, i løbet af de sidste tre dage, mens der ikke foregår andre procedurer med dem?

Vi kan lave vores eget løsning og skrive på Bash: indsamle backups på denne og denne måde. Måske skal der ikke laves noget specielt, og en cykel er allerede opfundet?

For at starte med de bedste praksisser. Mine kolleger anbefaler altid at minde om tjenesten ‘Yandex.Cloud’, hvor denne opgave allerede er løst. Så brug den, hvis du har mulighed for det.

Der findes ingen fuldstændige løsninger, der er hundrede procent indbygget i ClickHouse, til sikkerhedskopier. Der er nogle skabeloner, som man kan bruge. For at opnå en fuld løsning, skal man enten arbejde lidt manuelt eller lave indpakninger i form af scripts.

Jeg vil starte med de enkleste løsninger og afslutte med de mest avancerede, alt efter datamængden og størrelsen på klyngen. Jo større klyngen er, jo sværere bliver løsningen.

Hvis datatabellen kun fylder et par gigabyte, kan sikkerhedskopien laves sådan her:

  1. Gem tabeldefinitionerne, det vil sige metadataene — show create table.
  2. Lav en dump ved hjælp af ClickHouse-klienten — select * from table til en fil. Som standard får du en fil i TabSeparated-format. Hvis du ønsker det mere effektivt, kan du bruge Native-format.

Hvis datamængden er større, vil sikkerhedskopien tage længere tid og fylde mere. Dette kaldes en logisk sikkerhedskopi, og den er ikke bundet til ClickHouse-dataformatet. Hvis den findes, kan du i værste fald tage sikkerhedskopien og indlæse den i MySQL for genopretning.

For mere avancerede tilfælde er det muligt at oprette snapshots af partitioner i det lokale filsystem i ClickHouse. Denne mulighed er tilgængelig i form af forespørgslen alter table freeze partition. Eller blot alter table freeze — dette er et snapshot af hele tabellen.

Snapshotet vil blive oprettet konsistent for én tabel på én shard, hvilket betyder, at det ikke er muligt at skabe et konsistent snapshot af hele klyngen på denne måde. Men for de fleste opgaver er der ikke behov for det, og det er tilstrækkeligt at udføre forespørgslen på hver shard og få et konsistent snapshot. Det oprettes som hardlinks og derfor optager det ikke ekstra plads. Herefter kopierer du dette snapshot til backupserveren eller til det lager, du bruger til sikkerhedskopier.

Det er relativt let at gendanne en sådan backup. Først — opret tabellerne baseret på de eksisterende tabeldefinitioner. Derefter kopierer du de gemte snapshots af partitionerne til Directory-Detached for de pågældende tabeller og udfører forespørgslen attach partition. Denne løsning er helt passende til de mest alvorlige datamængder.

Nogle gange er der behov for noget endnu større - i tilfælde hvor du har titusinder eller endda hundreder af terabyte på hver server og hundreder af servere. Der findes en løsning, som jeg har fået inspiration til fra mine kolleger i "Yandex.Metrica". Jeg vil ikke anbefale det til alle - læs selv og afgør, om det er passende for dig.

Først skal du oprette flere servere med store diskhylder. Derefter skal du på disse servere opsætte flere ClickHouse-servere og konfigurere dem til at fungere som endnu en replik for de samme shards. Og brug derefter et filsystem eller et værktøj, der gør det muligt at oprette snapshots på disse servere. Der er to muligheder her. Den første mulighed er LVM snapshots, den anden mulighed er ZFS på Linux.

Efter dette skal du hver dag oprette et snapshot, som vil optage et bestemt plads. Hvis dataene ændrer sig, vil pladsen over tid naturligvis vokse. Dette snapshot kan hentes når som helst for at gendanne data, det er en lidt mærkelig løsning. Plus, det er også vigtigt at begrænse disse replikater i konfigurationen, så de ikke forsøger at blive ledere.

Kan der organiseres kontrolleret efterslæb af replikater i klyngen?

I år planlægger I at lave valser i ClickHouse. Er det muligt at organisere kontrolleret efterslæb for replikaterne i dem? Vi ønsker at bruge det til at beskytte os mod negative scenarier med alternativer og andre ændringer.

Er det muligt at lave nogen form for roll back for alternativer? For eksempel, i en eksisterende valse tage og sige, at indtil dette tidspunkt skal ændringer anvendes, men fra dette tidspunkt skal ændringer ikke længere anvendes?

Hvis der kommer et hold ind i vores klynge og ødelægger den, har vi en betinget replik med en efterslæb på en time, hvor vi kan sige, at lad os bruge netop denne på nuværende tidspunkt, men vi bruger ikke de sidste ti minutters ændringer i den?

Lad os først tale om kontrolleret efterslæb af replikater. Der har været en anmodning fra brugerne, og vi oprettede et issue på GitHub med anmodningen: "Hvis nogen har brug for det, giv et like, giv et hjerte". Ingen gav et like, og issue blev lukket. Ikke desto mindre er det nu muligt at få denne funktionalitet ved at konfigurere ClickHouse. Dog kun fra version 20.3.

ClickHouse kører konstant bag kulisserne en datasammenlægning - en merge. Når merge'en er udført, erstattes et sæt fragmenter med et større fragment. De tidligere fragmenter forbliver dog på disken i en periode.

For det første opbevares de, indtil der ikke længere er SELECT-forespørgsler, der bruger dem, for at sikre ikke-blokerende drift. SELECT-forespørgslerne læser roligt fra de gamle fragmenter.

For det andet er der også en tidsgrænse - de gamle fragmenter ligger på disken i otte minutter. Denne otte-minutters grænse kan justeres og endda forlænges til en dag. Dette vil kræve plads på disken: afhængigt af datatilstrømningen kan det ske, at dataene på den sidste dag ikke bare fordobles, men kan blive fem gange større. Men hvis der opstår alvorlige problemer, kan du stoppe ClickHouse-serveren og håndtere det hele.

Nu opstår spørgsmålet, hvordan dette beskytter mod alters. Her skal vi se dybere, da alters i gamle versioner af ClickHouse fungerede ved direkte at ændre fragmenter. Der er et datfragment med nogle filer, og vi laver for eksempel ALTER DROP COLUMN. Så fjernes denne kolonne fysisk fra alle fragmenter.

Men fra version 20.3 er alter-mekanismen blevet fuldstændig ændret, og nu er fragmenter altid immutable. De ændres slet ikke - alters fungerer nu omtrent som merges. I stedet for at ændre et fragment på stedet, opretter vi et nyt. I det nye fragment bliver filer, der ikke er ændret, hardlinks, og hvis vi fjerner en kolonne, vil den simpelthen være fraværende i det nye fragment. Det gamle fragment vil blive slettet som standard efter otte minutter, og her kan du justere indstillingerne, som nævnt tidligere.

Det samme gælder for alters af mutations-typen. Når du laver ALTER DELETE eller ALTER UPDATE, ændres fragmentet ikke, men et nyt oprettes. Det gamle slettes derefter.

Hvad skal vi gøre, hvis tabelstrukturen ændrer sig?

Hvordan gendanner man en backup, der blev udført med det gamle skema? Og et andet spørgsmål omkring snapshot cases og filsystemværktøjer. Er Btrfs passende i stedet for ZFS på Linux LVM?

Hvis du laver attach partition partitioner med en anden struktur, vil ClickHouse fortælle dig, at det ikke er muligt. Løsningen er som følger. Først - opret en midlertidig tabel af typen MergeTree med den gamle struktur, vedhæft dataene ved hjælp af attach, lav en alter-forespørgsel. Derefter kan du enten kopiere eller flytte disse data og gøre attach igen, eller bruge forespørgslen alter table move partition.

Nu til det andet spørgsmål - kan man bruge Btrfs. For det første, hvis du har LVM, er det tilstrækkeligt med LVM snapshots, og filsystemet kan være ext4, det har ingen betydning. Med Btrfs afhænger alt af din erfaring med at bruge det. Det er et modent filsystem, men der opstår alligevel nogle tvivl om, hvordan det hele vil fungere i praksis i et specifikt scenarie. Jeg ville ikke anbefale at bruge det, hvis du ikke har Btrfs i produktion.

Hvad er de nuværende bedste praksisser for reshaping af data?

Spørgsmålet om reshuffling er komplekst og mangesidet. Her kan man svare med flere forskellige muligheder. Man kan tage et perspektiv og sige, at der ikke er nogen indbygget mulighed for reshuffling i ClickHouse. Men jeg frygter, at dette svar ikke vil tilfredsstille nogen. Så man kan tage et andet perspektiv og sige, at der er mange måder at reshuffle data i ClickHouse.

Hvis pladsen på klyngen er ved at løbe tør, eller den ikke kan klare belastningen, tilføjer du nye servere. Men disse servere er som standard tomme, der er ingen data på dem, og der er ingen belastning. Du skal omfordele dataene, så de bliver jævnt fordelt på den nye, større klynge.

Den første metode til at gøre dette er at kopiere en del af partitionerne til de nye servere ved hjælp af forespørgslen alter table fetch partition. For eksempel, hvis du havde partitioner pr. måned, tager du den første måned i 2017 og kopierer den til den nye server, derefter kopierer du den tredje måned til en anden ny server. Og sådan gør du, indtil det bliver mere eller mindre jævnt.

Flytning kan kun udføres for de partitioner, der ikke ændres ved skrivning. For friske partitioner skal skrivning være deaktiveret, fordi deres flytning ikke er atomar. Ellers får du dubletter eller huller i dataene. Ikke desto mindre er denne metode praktisk og fungerer ret effektivt. De komprimerede partitioner er allerede klar til netværket, det vil sige, at dataene ikke dekomprimeres eller omkodes.

Denne metode har én ulempe, og det afhænger af sharding-skemaet; om du har planlagt dette sharding-skema og hvad din sharding-nøgle var. I dit eksempel for tilfælde med målinger er sharding-nøglen en hash af stien. Når du kører et select i den distribuerede tabel, går det straks til alle shards i klyngen og henter data derfra.

Det betyder, at det reelt set ikke betyder noget for dig, hvilke data der er på hvilken shard. Det vigtigste er, at dataene for én sti er på én shard, men hvilken præcis, er ikke væsentligt. I dette tilfælde passer det at flytte de færdige partitioner perfekt, fordi når du udfører select-forespørgsler vil du også - både før og efter resharding - få de komplette data; skemaet betyder ikke meget.

Men der er mere komplekse tilfælde. Hvis du på applikationsniveau planlægger et særligt sharding-skema, hvor denne klient er placeret på en bestemt shard, og forespørgslen kan sendes direkte der, i stedet for til den distribuerede tabel. Eller hvis du bruger en relativt ny version af ClickHouse og har aktiveret indstillingen optimize skip unused shards. I dette tilfælde vil udtrykket i where-sektionen blive analyseret under select-forespørgslen, og det vil blive beregnet, hvilke shards der skal tilgås i henhold til sharding-skemaet. Dette fungerer under betingelse af, at dataene faktisk er fordelt i overensstemmelse med dette sharding-skema. Hvis du har omorganiseret dem manuelt, kan overensstemmelsen ændre sig.

Så, dette er metode nummer ét. Jeg venter på dit svar; passer metoden, eller skal vi gå videre?

Vladimir Kolobaev, lead system administrator hos Avito: Alexey, den metode, som du nævnte, fungerer ikke særlig godt, når man skal fordele belastningen også på læsning. Vi kan tage partitionen, som er månedlig, og vi kan flytte den forrige måned til en anden node, men når der kommer en forespørgsel på disse data, vil vi kun belaste den. Det ville være godt at belaste hele klyngen, fordi ellers vil al læsebelastning et stykke tid blive behandlet af to shards.

Alexey Milovidov: Svaret her er underligt — ja, dårligt, men det kan måske lykkes. Jeg vil forklare, hvordan. Det er værd at se på belastningsscenariet, der følger med dine data. Hvis det er overvågningsdata, kan man næsten med sikkerhed sige, at størstedelen af anmodningerne går efter friske data.

Du har sat nye servere op, flyttet gamle partitioner, men også ændret måden, hvorpå friske data skrives. Og de friske data vil blive spredt ud over hele klyngen. Så allerede efter fem minutter vil anmodningerne for de sidste fem minutter jævnt belaste klyngen; efter en dag vil anmodningerne for de sidste 24 timer jævnt belaste klyngen. Men anmodningerne for den foregående måned vil desværre kun gå til en del af serverne i klyngen.

Men ofte vil du ikke have anmodninger specifikt for februar 2019. Sandsynligvis, hvis anmodningerne går ind i 2019, vil de være for hele 2019 — for et stort tidsrum og ikke for et lille interval. Og sådanne anmodninger kan også jævnt belaste klyngen. Men dit punkt er helt korrekt, at det er en slags ad hoc-løsning, der ikke spreder dataene fuldstændigt jævnt.

Jeg har nogle flere punkter til at svare på spørgsmålet. Et af dem handler om, hvordan man oprindeligt kan designe shardschemaet, så det bliver mindre smertefuldt at resharde. Det er ikke altid muligt.

For eksempel har du overvågningsdata. Overvågningsdata vokser af tre grunde. Den første er akkumulationen af historiske data. Den anden er trafikvækst. Og den tredje er en stigning i antallet af de ting, der falder ind under overvågning. Nye mikroservices og målinger dukker op, som skal gemmes.

Måske er den største vækst relateret til den tredje grund — det er en stigende anvendelse af overvågning. Og i det tilfælde er det værd at se på karakteren af belastningen, hvad de primære anmodninger om select er. De primære anmodninger om select vil sandsynligvis komme fra et bestemt subset af målinger.

For example, the use of CPU on certain servers by a specific service. It turns out that there is a subset of keys through which you retrieve this data. And the request for this data is most likely quite simple and is executed in tens of milliseconds. It is used for monitoring services and dashboards. I hope I understand this correctly.

Vladimir Kolobaev: The thing is, we often refer to historical data because we compare the current situation with historical data in real-time. It's essential for us to have quick access to a large volume of data, and ClickHouse handles this exceptionally well.

You are absolutely right; most read requests we experience are from the last day, just like any monitoring system. However, there is also considerable load on historical data. This mainly comes from the alerting system, which queries ClickHouse every thirty seconds, saying: "Give me the data for the last six weeks. Now build a moving average from it, and let’s compare the current value with the historical one."

I would like to say that we have a small additional table for such very fresh requests, where we store only two days of data, and major queries go there. We only send large historical queries to the larger sharded table.

Alexey Milovidov: Unfortunately, this is not well applicable to your scenario, but I will describe two poor and complex sharding schemes that should not be used but are used in my friends' service.

There is a main cluster with events from 'Yandex.Metrics'. Events refer to page views, clicks, and transitions. Most queries go to a specific website. You open the 'Yandex.Metrics' service, you have a website — avito.ru, you access the report, and a query is made for your website.

But there are also other requests—analytical and global—that are made by internal analysts. Just to note, internal analysts only query the 'Yandex' services. Nevertheless, even the 'Yandex' services account for a significant portion of all data. These are requests not for specific counters but for broader filtering.

Hvordan organiserer man data på en måde, så både én tæller fungerer effektivt, og globale forespørgsler også kan håndteres? Udfordringen er, at antallet af forespørgsler i ClickHouse på "Metrics"-klusteret er flere tusinde per sekund. Der er ikke-trivielle forespørgsler, for eksempel kan én server ClickHouse ikke håndtere flere tusinde forespørgsler per sekund.

Størrelsen på klusteret er lidt over seks hundrede servere. Hvis man blot trækker en Distributed-tabel over dette kluster og sender flere tusinde forespørgsler derhen, vil det blive endnu værre end at sende dem til én enkelt server. På den anden side kan vi hurtigt forkaste muligheden for, at dataene er jævnt fordelt, og at vi henter forespørgsler fra alle servere.

Der er en diametralt modsat mulighed. Forestil dig, hvis vi shard'er dataene efter websites, så en forespørgsel for ét website går til én shard. Nu kan klusteret håndtere ti tusinde forespørgsler per sekund, men på én shard vil en bestemt forespørgsel være meget langsom. Den vil ikke længere kunne skaleres i takt med båndbredden. Især hvis det er et website som avito.ru. Jeg afslører ikke nogen hemmelighed, hvis jeg siger, at Avito er en af de mest besøgte hjemmesider i det russiske internet. At behandle det på én shard ville være galskab.

Derfor er sharding-schemata designet mere smart. Hele klusteret er opdelt i et antal mindre klustre, som vi kalder lag. Inden i hvert lille kluster er der fra ti til flere dusin shards. Der er i alt sådan treogtredive klustre.

Hvordan skalerer det hele? Antallet af klustre ændrer sig ikke - som der var treogtredive for flere år siden, så er der stadig det samme antal. Men inden i hvert af dem øger vi gradvist antallet af shards i takt med dataakkumulationen. Og sharding-schemata er overordnet set sådan, at opdelingen i disse klustre sker efter websites, og for at forstå, hvilket website der hører til hvilket kluster, anvendes der en særlig metabase i MySQL. Ét website – på ét kluster. Og inden i det sker sharding efter besøgendes identifikatorer.

Når vi optager, opdeler vi dem efter resten fra divisionen af besøgsidentifikatoren. Men når vi tilføjer en ny shard, ændrer sharding-skemaet sig; vi fortsætter med at opdele, men efter resten fra divisionen med et andet tal. Dette betyder, at én besøgende alligevel er placeret på flere servere, og det kan man ikke stole på. Dette er gjort udelukkende for at dataene kan komprimeres bedre. Ved forespørgsler går vi til Distributed-tabellen, som ser på klyngen og henvender sig til dusinvis af servere. Sådan en besværlig skema.

Men min fortælling ville være ufuldstændig, hvis jeg ikke nævnte, at vi har opgivet dette skema. I det nye skema har vi ændret alt, og alle data er kopieret ved hjælp af clickhouse-copier.

I det nye skema deles alle hjemmesider op i to kategorier - store og små. Jeg ved ikke, hvordan tærsklen er valgt der, men resultatet er, at store hjemmesider gemmes på en klynge med 120 shards, hvor der er tre replikaer i hver - det vil sige 360 servere. Og sharding-skemaet er således, at enhver forespørgsel går direkte til alle shards. Hvis du nu åbner en hvilken som helst side af rapporten for avito.ru i 'Yandex.Metrica', vil forespørgslen gå til 120 servere. Der er ikke mange store hjemmesider i Runet. Og antallet af forespørgsler er ikke tusind i sekundet, men endda mindre end hundrede. Alt dette håndteres uden problemer af Distributed-tabellen, som hver af dem behandles af 120 servere.

Den anden klynge er til små hjemmesider. Her er sharding-skemaet baseret på hjemmesideidentifikatoren, og hver forespørgsel går præcist til én shard.

Der er et værktøj i ClickHouse kaldet clickhouse-copier. Kan du fortælle lidt om det?

Jeg vil straks sige, at denne løsning er mere omfattende og lidt mindre effektiv. Fordelen er, at den fuldstændig fordeler dataene i henhold til det skema, du angiver. Men ulempe ved værktøjet er, at det slet ikke udfører resharding. Det kopierer data fra et klynge-schema til et andet klynge-schema.

Dette betyder, at for at det skal fungere, skal du have to klynger. De kan være placeret på de samme servere, men alligevel vil dataene ikke blive flyttet inkrementelt, men vil blive kopieret.

For example, there were four servers, and now there are eight. You create a new Distributed table on all the servers, new local tables, and launch clickhouse-copier, specifying the operation scheme, indicating that it should read from there, accept the new sharding scheme, and move the data there. You will need one and a half times more space on the old servers than you currently have because the old data must remain there, and on top of that, half of this same old data will arrive. If you thought in advance about the need to reshuffle the data and have space available, then this method will work.

How does clickhouse-copier work internally? It divides the entire workload into a set of tasks for processing one partition of one table on one shard. All of these tasks can be performed in parallel, and clickhouse-copier can be started on different machines in several instances, but what it does for one partition is nothing more than an insert select. Data is read, decompressed, reshuffled, then compressed again, written somewhere, and resorted. This is a heavier solution.

I har haft et pilotprojekt, som hed reshaping. Hvad er status på det?

You had a pilot project back in 2017 called resharding. There is even an option in ClickHouse. I understand it didn’t take off. Can you explain why that happened? It seems quite relevant.

The problem is that when it’s necessary to reshuffle data, it requires quite complex synchronization to do it atomically. When we started looking at how this synchronization is arranged, it became clear that there are fundamental problems. And these fundamental problems are not just theoretical, but they immediately began to manifest in practice as something that can be simply explained — nothing works.

Er det muligt at sammensmelte alle datasæt, før de flyttes til langsomme diske?

A question about TTL with the move to slow disk option in the context of merges. Is there a way, besides using cron, to merge all parts into one before moving to slow disks?

The answer to the question of whether it’s possible to automatically merge all pieces into one before their transfer is — no. I don’t think it’s necessary. You can choose not to merge all parts into one and simply rely on the fact that they will be transferred to the slow disks automatically.

Vi har to kriterier for overførselsregler. Det første er baseret på udfyldning. Hvis der på det nuværende lager er mindre end en vis procentdel ledig plads, vælger vi en del og overfører den til et langsommere lager. Snarere ikke et langsommere, men det næste — afhængigt af hvordan du konfigurerer det.

Det andet kriterium er baseret på størrelse. Det drejer sig om overførsel af store dele. Du kan justere tærsklen for ledig plads på den hurtige disk, og dataene vil blive overført automatisk.

Hvordan skal man opgradere til nye versioner af ClickHouse, hvis man ikke har mulighed for at testes for kompatibilitet på forhånd?

Dette emne diskuteres regelmæssigt i ClickHouse Telegram-chatten , med hensyn til forskellige versioner, og alligevel. Hvor sikkert er det at opgradere fra version 19.11 til 19.16 og for eksempel fra 19.16 til 20.3? Hvordan er det bedst at migrere til nye versioner, når man ikke har mulighed for at teste kompatibiliteten på forhånd i et sandkassemiljø?

Her er der flere 'gyldne' regler. Den første er at læse changelog. Den er stor, men der er specifikke punkter om bagudkompatible ændringer. Man bør ikke betragte disse punkter som et rødt flag. Typisk er dette små inkompatibiliteter, der er relateret til noget kantfunktionalitet, som du sandsynligvis ikke bruger.

Den anden er — hvis der ikke er mulighed for at teste kompatibilitet i sandkassen, og du ønsker at opdatere direkte i produktionen, er anbefalingen således — lad være med at gøre det. Opret først et sandkassemiljø og test. Hvis du ikke har et testmiljø, så er din virksomhed sandsynligvis ikke særlig stor, og dermed kan du kopiere en del data til din bærbare computer og sørge for, at alt fungerer korrekt. Du kan endda sætte flere replikater op lokalt på din maskine. Eller du kan oprette en ny version et sted i nærheden og overføre en del af dataene — det vil sige at lave et improviseret testmiljø.

En anden regel er — ikke at opdatere i en uge efter udgivelsen af en version på grund af fejlretning i produktionen og efterfølgende hurtige rettelser. Lad os kigge på versioneringen af ClickHouse for ikke at blive forvirrede.

Der er version 20.3.4. Tallet 20 angiver året for udgivelsen - 2020. Set fra et indholdsmæssigt synspunkt er det ikke af nogen betydning, så det vil vi ikke fokusere på. Dernæst er der 20.3. Det andet tal - i dette tilfælde 3 - øger vi hver gang vi udgiver en version med ny funktionalitet. Hvis vi ønsker at tilføje en funktion i ClickHouse, er vi nødt til at øge dette tal. Det betyder, at i version 20.4 vil ClickHouse fungere endnu bedre. Det tredje tal - 20.3.4. Her er 4 antallet af patch-udgivelser, hvor vi ikke har tilføjet nye funktioner, men har rettet nogle fejl. Og 4 betyder, at vi har gjort det fire gange.

Du skal ikke tro, at det er noget forfærdeligt. Normalt kan brugeren installere den nyeste version, og den vil fungere uden problemer med oppetid i et år. Men forestil dig, at der i en funktion til behandling af bitmapper, som blev tilføjet af vores kinesiske kolleger, serveren crasher, når der sendes forkerte argumenter. Vi er nødt til at rette dette. Vi vil udgive en ny patch-version, og ClickHouse bliver mere stabil.

Hvis du har ClickHouse, der kører i produktion, og der kommer en ny version af ClickHouse med ekstra funktioner - for eksempel 20.4.1 - så skynd dig ikke med at installere den i produktionen den første dag. Hvad er den overhovedet nødvendig for? Hvis du endnu ikke bruger ClickHouse, kan du installere den, og det vil sandsynligvis gå godt. Men hvis ClickHouse allerede kører stabilt, skal du holde øje med patches og opdateringer - hvilke problemer vi retter.

Kirill Shvakov: Jeg vil gerne supplere lidt om testmiljøer. Alle er meget bange for testmiljøer og mener underligt nok, at hvis du har et meget stort ClickHouse-kluster, så skal testmiljøet også være lige så stort eller i det mindste ti gange mindre. Det er slet ikke tilfældet.

Jeg kan fortælle ud fra min egen erfaring. Jeg har et projekt, og der er ClickHouse. Vores testmiljø til det er en lille virtuel maskine i Hetzner til tyve euro, hvor alt er oprettet. For at gøre dette har vi fuld automatisering i Ansible, og derfor er der i princippet ikke nogen forskel på, om det skal køre på fysiske servere eller bare oprettes i virtuelle maskiner.

Hvad kan man gøre? Det ville være godt at lave et eksempel i dokumentationen for ClickHouse om, hvordan man opretter en lille klynge selv — i Docker, i LXC, eller måske skabe et Ansible playbook, fordi forskellige mennesker har forskellige deployment. Det ville gøre mange ting lettere. Når du kan oprette en klynge på fem minutter, er det meget nemmere at forsøge at forstå noget. Det er meget mere praktisk, fordi det at køre en produktionsversion, som du ikke har testet — er vejen til ingenting. Nogle gange fungerer det, og andre gange ikke. Så at håbe på succes — er ikke godt.

Maxim Kotyakov, senior backend engineer hos Avito: Jeg vil supplere lidt om testmiljøer som en del af problemerne hos store virksomheder. Vi har en fuldt fungerende acceptklynge af ClickHouse, der er en nøjagtig kopi af, hvad vi har i produktionen med hensyn til datastrukturen og indstillingerne. Denne klynge er oprettet i ret slidte containere med minimal ressourcer. Vi skriver en vis procent af produktionsdataene derover, heldigvis er der mulighed for at replikere strømmen i Kafka. Alt er synkroniseret og skaleret — både hvad angår kapacitet og flow, og i teorien, under ligestillede forhold, bør det opføre sig som produktionen. Alt potentielt eksplosivt rulles først ud på denne stand og står i flere dage, indtil det er klar. Men det er naturligvis en dyr, tung løsning med ikke-nul støtteomkostninger.

Alexey Milovidov: Lad mig fortælle, hvad testmiljøet for vores venner fra 'Yandex.Metrica' ser ud. Én klynge bestod af over 600 servere, en anden af 360, og der er også en tredje og flere klynger. Testmiljøet for en af dem består ganske enkelt af to shards med to replikaer i hver. Hvorfor to shards? For at have mere end én. Og replikaer for at have nogle af dem. Bare et minimum antal, som man kan tillade sig.

Dette testmiljø gør det muligt at kontrollere, om forespørgslerne fungerer, og om noget ikke er gået fuldstændig galt. Men ofte opstår problemer af en helt anden karakter, når alt fungerer, men der er nogle små ændringer med belastningen.

Jeg vil give et eksempel. Vi besluttede at installere en ny version af ClickHouse. Den blev lagt ud i testmiljøet, automatiserede tests blev gennemført i 'Yandex.Metrica', som sammenlignede data i den gamle version og den nye ved at køre hele pipeline. Og naturligvis vores CI's grønne tests. Ellers ville vi ikke engang have tilbudt denne version.

Det hele er fantastisk. Vi begynder at rulle ud i produktion. Jeg får en besked om, at belastningen på graferne er steget flere gange. Vi ruller versionen tilbage. Jeg ser på grafen og bemærker: belastningen er faktisk steget flere gange under udrulningen, og den faldt tilbage, da vi rullede ud. Så begyndte vi at rulle versionen tilbage. Og belastningen steg på samme måde og faldt tilbage på samme måde. Så konklusionen er, at belastningen steg i forbindelse med udrulningen, ikke noget overraskende.

Det blev svært at overbevise kollegerne om at installere den nye version alligevel. Jeg siger: "Det er fint, rulle ud. Kryds fingre, det hele vil fungere. Lige nu er belastningen steget på graferne, men det er normalt. Hold ud." Kort sagt, vi gjorde det, og alle - versionen er blevet lagt i produktion. Men næsten ved hver udrulning opstår der lignende problemer.

Kill query skal dræbe forespørgsler, men det gør det ikke. Hvorfor?

En bruger, en eller anden analytiker, kom til mig og lavede en forespørgsel, som belastede mit ClickHouse-kluster. Det kunne være en node eller hele klusteret - afhængig af hvilken replik eller shard forespørgslen ramte. Jeg kan se, at alle ressourcerne på CPU'en på denne server er fyldt op, alt er rødt. Samtidig svarer ClickHouse stadig på forespørgslerne. Så jeg skriver: "Vis mig venligst proceslisten, hvilken forespørgsel forårsagede dette vanvid."

Jeg finder denne forespørgsel og skriver kill. Og jeg kan se, at der ikke sker noget. Min server er fyldt op, ClickHouse fortsætter med at give mig nogle kommandoer og viser, at serveren lever, og alt er fint. Men jeg oplever nedgang i alle brugerforespørgslerne, nedgangen starter ved skrivning til ClickHouse, og min kill-forespørgsel virker ikke. Hvorfor? Jeg troede, at en kill-forespørgsel skulle dræbe forespørgsler, men det sker ikke.

Nu kommer der et ret mærkeligt svar. Sagen er, at kill-forespørgslen ikke dræber forespørgsler.

Kill-forespørgslen sætter et lille flag kaldet "jeg vil have, at denne forespørgsel skal dræbes." Selve forespørgslen ser ved behandling af hver blok på dette flag. Hvis det er sat, stopper forespørgslen med at arbejde. Det viser sig, at ingen dræber forespørgslen, den skal selv kontrollere det hele og stoppe. Dette skal virke i alle tilfælde, hvor forespørgslen er i behandling af datablokke. Den behandler den næste datablok, tjekker flaget og stopper.

Det fungerer ikke i tilfælde, hvor en anmodning blokeres ved en operation. Sandsynligvis er dette dog ikke din situation, da du siger, at det bruger en masse serverressourcer. Det kan være, at det ikke fungerer i tilfælde af ekstern sortering og et par andre detaljer. Men generelt burde det ikke ske, det er en bug. Det eneste, jeg kan anbefale, er at opdatere ClickHouse.

Hvordan beregner man svartid under læsebelastning?

Der er en tabel, som gemmer aggregeringer per item - forskellige tællere. Antallet af rækker er cirka hundrede millioner. Kan man forvente en forudsigelig svartid, hvis man presser 1K RPS gennem 1K items?

Baseret på konteksten handler det om læsebelastning, fordi der ikke er nogen problemer med skrivning - man kan indsætte tusinde, hundrede tusinde eller endda flere millioner rækker.

Læsende forespørgsler kommer i mange former. I select 1 kan ClickHouse udføre omkring ti tusinde forespørgsler per sekund, så selv forespørgsler på én nøgle vil kræve nogle ressourcer. Og sådanne punktforespørgsler vil være mere komplicerede end i nogle nøgle-værdi databaser, fordi man skal læse en datablok ved hjælp af indekset for hver læsning. Indekset adresserer ikke hver optegnelse, men hver række af intervaller. Det vil sige, at man skal læse hele intervallet - det er 8192 rækker som standard. Og man skal dekomprimere den komprimerede datablok fra 64 Kb til 1 Mb. Normalt tager sådanne punktforespørgsler fra et par millisekunder. Men det er den enkleste mulighed.

Lad os prøve at lave noget simpel aritmetik. Hvis man ganger et par millisekunder med tusind, får man et par sekunder. Det er som om man ikke kan håndtere tusind forespørgsler per sekund, men i virkeligheden kan man, fordi vi har flere processor kerner. Så i princippet kan 1000 RPS ClickHouse nogle gange holde, men på korte forespørgsler, specifikt punktforespørgsler.

Hvis man skal skalere ClickHouse-klusteret efter antallet af enkle forespørgsler, anbefaler jeg det enkleste - at øge antallet af replikater og sende forespørgsler til en tilfældig replika. Hvis en replika holder fem hundrede forespørgsler per sekund, hvilket er helt realistisk, så vil tre replikater holde halvandet tusinde.

Nogle gange kan man selvfølgelig også konfigurere ClickHouse til det maksimale antal punktaflæsninger. Hvad skal der til for dette? Først - reducer granulariteten af indekset. Når man gør det, skal man ikke reducere det til en enhed, men med henblik på, at antallet af poster i indekset vil være flere millioner eller titusindvis af millioner på serveren. Hvis tabellen har hundrede millioner rækker, kan granulariteten indstilles til 64.

Man kan reducere størrelsen af den komprimerede blok. Der er indstillinger til dette. min compress block size, max compress block size. Disse kan reduceres, og ved at omfordele data, vil punktafgørelserne være hurtigere. Men ClickHouse er stadig ikke en key-value database. Et stort antal små forespørgsler er et antipattern for belastning.

Kirill Shvakov: Jeg vil give et råd til tilfælde, hvor der er almindelige tællere. Det er en ret standard situation, hvor ClickHouse gemmer en slags tæller. Jeg har en bruger, han er fra et bestemt land, plus et tredje felt, og der er behov for at inkrementere noget. Tag MySQL, lav en unik nøgle - i MySQL er det en duplikatnøgle, og i PostgreSQL er det en konflikt - og tilføj det med et plustegn. Dette vil fungere meget bedre.

Når du har lidt data, er der ikke meget mening i at bruge ClickHouse. Der er almindelige databaser, og de klarer det godt.

Hvad kan man tune i ClickHouse for at få flere data i cachen?

Forestil dig situationen - serverne har 256 GB RAM, i den daglige drift bruger ClickHouse cirka 60-80 GB, i spidsen op til 130. Hvad kan man aktivere og tune for at få flere data i cachen, og dermed færre opkald til disken?

Som regel håndterer operativsystemets page cache denne opgave godt. Hvis du åbner bare toplisten, ser der cached eller free - der står også, hvor meget der er cached - så kan man bemærke, at al den frie hukommelse er brugt til cache. Og disse data vil blive læst fra RAM, ikke fra disken. Jeg kan sige, at cachen bruges effektivt, fordi det netop er de komprimerede data, der caches.

Hvis du derimod ønsker at accelerere nogle enkle forespørgsler endnu mere, er der mulighed for at aktivere en cache inden i ClickHouse for de udpakkede data. Dette kaldes uncompressed cache. I konfigurationsfilen config.xml angiver du uncompressed cache size til det ønskede værd, jeg anbefaler ikke mere end halvdelen af den ledige RAM, fordi det resterende går til page cache.

Derudover er der to indstillinger på forespørgselsniveau. Den første indstilling — use uncompressed cache — aktiverer brugen af det. Det anbefales at aktivere det for alle forespørgsler undtagen tunge, der kan læse alle data og rydde denne cache. Og den anden indstilling — det er noget der ligner det maksimale antal rækker der må bruges til cachen. Den begrænser automatisk store forespørgsler for at de går uden om cachen.

Hvordan kan man konfigurere storage_configuration til opbevaring i RAM?

I den nye ClickHouse dokumentation læste jeg afsnittet relateret til data storage. I beskrivelsen er der et eksempel med hurtige SSD'er.

Det er interessant, hvordan man kan konfigurere det samme med volume hot memory. Og et spørgsmål mere. Hvordan fungerer select med en sådan datastruktur, læser det hele datasættet eller kun det der ligger på disken, og bliver disse data komprimeret i hukommelsen? Og hvordan fungerer prewhere sektionen med en sådan datastruktur?

Denne indstilling påvirker opbevaringen af datablokke, og deres format ændrer sig ikke.
Lad os se nærmere på det.

Det er muligt at konfigurere opbevaringen af data i RAM. Alt hvad der konfigureres for disken — det er dens sti. Du opretter en tmpfs partition, der er monteret på en bestemt sti i filsystemet. Angiv denne sti som stien for opbevaring af data for selve den varme partition, hvor blokkene begynder at komme ind og blive skrevet, alt er godt.

Men jeg anbefaler ikke at gøre det på grund af lav pålidelighed, selvom, hvis du har mindst tre replikaer i forskellige datacentre, så er det muligt. Hvis der er problemer, vil dataene blive gendannet. Lad os forestille os, at serveren pludselig slukkes og tændes igen. Partitionen monteres igen, men der er tomt. ClickHouse serveren ser ved opstart, at den har disse blokke, der mangler, selvom metadataene i ZooKeeper siger, at de burde være der. Den kontrollerer, på hvilke replikaer de er, anmoder om dem og downloader dem. På den måde vil dataene blive gendannet.

I denne forstand adskiller datalagring i RAM sig principielt ikke fra lagring på disk, fordi data, der skrives til disk, også først går ind i page cache og fysisk skrives udskudt. Dette afhænger af, hvordan filsystemet er monteret. Men for en sikkerheds skyld vil jeg sige, at ClickHouse ikke udfører fsync ved insert.

Dataene i RAM opbevares i præcist det samme format som på disk. En select-forespørgsel vælger på nøjagtig samme måde de stykker, der skal læses, vælger de nødvendige dataintervaller i stykkerne, og læser dem. Og prewhere fungerer præcist på samme måde, uanset om dataene var i RAM eller på disk.

Op til hvor mange unikke værdier er Low Cardinality effektivt?

Low Cardinality er smart konstrueret. Den danner datadictionaryer, men de er lokale. For det første har hver chunk sine egne dictionaryer, for det andet kan de endda være forskellige inden for en enkelt chunk for hvert interval. Når antallet af unikke værdier når en tærskelværdi - sådan jeg husker det, en million - bliver dictionaryen simpelthen udskudt, og en ny oprettes.

Svaret generelt: for hvert lokalt interval - lad os sige, for hver dag - hvor der er op til en million unikke værdier, er Low Cardinality effektiv. Derefter vil der blot være en fallback, hvor der bruges mange forskellige dictionaryer i stedet for én. Det vil fungere på omtrent samme måde som en almindelig kolonne af typen string, måske lidt mindre effektivt, men der vil ikke ske en alvorlig forringelse af ydeevnen.

Hvad er de bedste praksisser for fuldtekst-søgning i en tabel med fem milliarder rækker?

Der er forskellige svarmuligheder. Det første er at sige, at ClickHouse ikke er et system til fuldtekst søgning. Til det findes der specialiserede systemer som f.eks. Elasticsearch og Sphinx. Ikke desto mindre møder jeg i stigende grad folk, der siger, at de skifter fra Elasticsearch til ClickHouse.

Hvorfor sker dette? De forklarer, at Elasticsearch begynder at lide under belastningen med visse mængder, især når det kommer til indeksopbygning. Indeksene bliver for omfangsrige, og hvis man blot overfører dataene til ClickHouse, viser det sig, at de opbevares meget mere effektivt i volumen. Desuden var forespørgslerne ofte ikke sådan, at man skal finde en sætning i hele datamængden under hensyntagen til morfologien, men helt anderledes. For eksempel at finde i logs de seneste par timer for en bestemt sekvens af bytes.

I dette tilfælde opretter du en indeks i ClickHouse, hvor det første felt vil være dato med tid. Det største datasnit vil netop være baseret på datoområdet. Inden for det valgte datoområde kan man normalt udføre en fuldtekst søgning endda ved hjælp af brute force-metoden med like. Like-operatoren i ClickHouse er den mest effektive like-operator, du kan finde. Hvis du finder en bedre, så sig det til mig.

Men stadig er like en fuld scanning. Og fuld scanning kan være langsom ikke kun i CPU, men også på disken. Hvis du pludselig har en terabyte data om dagen, og du leder efter et ord i løbet af en dag, så skal du scanne en terabyte. Og det vil helt sikkert være på almindelige hårde diske, og i sidste ende vil de være så belastede, at du ikke kan få adgang til denne server via SSH.

I dette tilfælde er jeg klar til at foreslå endnu et lille trick. Det er af eksperimentel slags - det kan lykkes, eller det kan ikke. I ClickHouse er der fuldtekstindekser i form af trigram Bloom-filtre. Vore kolleger fra Arenadata har allerede testet disse indekser, og ofte fungerer de præcis som tiltænkt.

For korrekt at bruge dem, er det vigtigt at forstå, hvordan de egentlig fungerer: hvad trigram Bloom-filteret er, og hvordan man vælger dets størrelse. Jeg kan sige, at de vil hjælpe med forespørgsler om sjældne sætninger og substrings, som sjældent findes i dataene. I dette tilfælde vil indeksene vælge underområder, og der vil blive læst mindre data.

For nylig er der kommet endnu mere avancerede funktioner til fuldtekst søgning i ClickHouse. For det første kan man nu søge efter flere substrings ad gangen i ét gennemløb, inklusive varianter med registrering, uden registrering, med støtte til UTF-8 eller kun for ASCII. Vælg den mest effektive, som du har brug for.

Der er også kommet mulighed for at søge efter flere regulære udtryk i ét gennemløb. Du behøver ikke at skrive X like én substring or X like en anden substring. Du skriver rygende og det hele udføres maksimalt effektivt.

For det tredje - nu er der en nærheds søgning af regulære udtryk og en nærheds søgning af substrings. Hvis nogen har skrevet et ord med en tastefejl, vil det blive søgt efter maksimal overensstemmelse.

Hvordan organiserer man bedst adgangen til ClickHouse for et stort antal brugere?

Fortæl, hvordan man bedre organiserer adgang for et stort antal forbrugere og analytikere. Hvordan man organiserer en kø, prioriterer forespørgsler maksimale samtidige forespørgsler, og hvilke værktøjer der skal bruges?

Hvis klyngen er stor nok, kan det være en god løsning at opsætte yderligere to servere, der bliver indgangspunkt for analytikere. Det vil sige, at analytikere ikke skal have adgang til de specifikke shards i klyngen, men blot oprette to tomme servere, uden data, og på dem konfigurere adgangsrettigheder. Indstillingerne for brugere ved distribuerede forespørgsler overføres til de fjerne servere. Så du konfigurerer alt på disse to servere, og indstillingerne gælder for hele klyngen.

I princippet er disse servere uden data, men mængden af RAM på dem er meget vigtig for udførelsen af forespørgsler. Disken kan også bruges til midlertidige data, hvis ekstern aggregering eller ekstern sortering er aktiveret.

Det er vigtigt at se på indstillingerne, der er relateret til alle mulige grænser. Hvis jeg nu går ind i klyngen "Yandex.Metrica" som analytiker og stiller en forespørgsel select count from hits, så vil jeg straks få en undtagelse om, at jeg ikke kan udføre forespørgslen. Det maksimale antal rækker, jeg må scanne, er hundrede milliarder, mens der i alt er femoghalvfjerds trillioner i én tabel i klyngen. Dette er den første grænse.

Lad os antage, at jeg fjerner grænsen for antallet af rækker og udfører forespørgslen igen. Så vil jeg se den følgende undtagelse - indstillingen force index by date. Jeg kan ikke udføre forespørgslen, hvis jeg ikke angiver datoperioden. Man skal ikke regne med, at analytikere vil angive det manuelt. Et typisk tilfælde er, at datoperioden er skrevet where event date between uge. Og så blot ikke angivet parentesen korrekt, og i stedet for and blev der or – or URL match. Hvis der ikke er nogen grænse, vil det begynde at scanne URL-kolonnen og bruge masser af ressourcer.

Desuden er der to indstillingsprioriteter i ClickHouse. Desværre er de meget primitive. Den ene hedder bare priority. Hvis priority ≠ 0, og der behandles anmodninger med en eller anden prioritet, men der samtidig behandles en anmodning med en prioritet, som har en værdi, der er lavere, hvilket betyder en højere prioritet, så bliver anmodningen med en højere prioritet, som repræsenterer en lavere prioritet, simpelthen sat på pause og vil ikke køre i det timeframe.

Dette er en meget grov indstilling, og den er ikke egnet til situationer, hvor der er konstant belastning på klyngen. Men hvis du har korte, impulsive anmodninger, der er vigtige, og klyngen stort set står stille, så vil en sådan indstilling fungere.

Den næste indstilling af prioriteter kaldes OS thread priority. Den sætter blot for alle tråde, der behandler anmodninger, nice-værdien for Linux-scheduler. Den fungerer ikke helt optimalt, men den fungerer stadig. Hvis du indstiller den mindste nice-værdi — den er den største i størrelse, og dermed den laveste prioritet — og for anmodninger med høj prioritet sætter -19, så vil CPU'en forbruge lavprioriterede anmodninger cirka fire gange mindre end højprioriterede.

Der skal også indstilles en maksimal behandlingstid for anmodninger — lad os sige, fem minutter. Den mindste udførelseshastighed for anmodninger — det er det mest imponerende. Denne indstilling har eksisteret i lang tid, og den er nødvendig for ikke blot at påstå, at ClickHouse ikke er langsom, men for at tvinge det.

Forestil dig, at du indstiller: hvis en anmodning behandler mindre end én million rækker pr. sekund — så skal det ikke gøres. Det beskytter vores gode navn, vores gode database. Lad os simpelthen forbyde det. Der er faktisk to indstillinger. Den ene kaldes min execution speed — i rækker pr. sekund, og den anden kaldes timeout before checking min execution speed — som som standard er femten sekunder. Altså femten sekunder kan lade sig gøre, og derefter, hvis det er langsomt, så kastes der bare en undtagelse — stop anmodningen.

Der skal også indstilles kvoter. ClickHouse har en indbygget funktion til kvoter, der tæller ressourceforbruget. Men desværre ikke for fysiske ressourcer som CPU, disk, men logiske — antallet af behandlede anmodninger, rækker og læste bytes. Og man kan f.eks. indstille maksimum hundrede anmodninger i løbet af fem minutter og tusinde anmodninger i timen.

Hvorfor er det vigtigt? Fordi en del af analytiske forespørgsler vil blive udført manuelt direkte fra ClickHouse-klienten. Og alt vil være godt. Men hvis der er avancerede analytikere i dit firma, vil de skrive et script, og der kan være en fejl i scriptet. Denne fejl kan føre til, at forespørgslen udføres i en uendelig løkke. Det er det, vi skal beskytte os imod.

Er det muligt at sende resultaterne af en forespørgsel til ti kunder?

Vi har flere brugere, der godt kan lide at komme med meget store forespørgsler på samme tid. Forespørgslen er stor, ville generelt blive udført hurtigt, men fordi der er mange af disse forespørgsler samtidig, bliver det meget smertefuldt. Er det muligt at udføre den samme forespørgsel, som kom ti gange i træk, én gang og returnere resultatet til ti kunder?

Problemet er, at vi ikke har cache-resultater eller caching af mellemdata. Der er en page cache i operativsystemet, som gør, at data ikke skal læses fra disken igen, men desværre vil data stadig blive udpakket, deserialiseret og behandlet igen.

Vi ville gerne finde en måde at undgå dette på, enten ved at cache mellemdata eller ved at stille lignende forespørgsler i en kø og tilføje en resultatcache. Lige nu har vi en pull request under udvikling, der tilføjer forespørgselscache, men kun for underforespørgsler i 'in' og 'join' sektionerne — så løsningen er ikke fuldstændig.

Alligevel har vi også en sådan situation. Især det klassiske eksempel er forespørgsler med pagination. Der er en rapport med flere sider, og der bliver sendt en forespørgsel med limit 10. Så det samme igen, men med limit 10,10. Derefter den næste side. Og spørgsmålet er, hvorfor vi beregner det hele hver gang? Men der er i øjeblikket ingen løsning, og det kan ikke undgås.

Der er en alternativ løsning, der placeres som et sidekick ved siden af ClickHouse — ClickHouse Proxy.

Kirill Shvakov: I ClickHouse Proxy er der en indbygget rate limiter og en indbygget resultatcache. Der er mange indstillinger, fordi der blev løst en lignende opgave. Proxy'en gør det muligt at begrænse forespørgsler, ved at stille dem i kø og indstille, hvor længe forespørgselscachen skal leve. Hvis forespørgslerne faktisk var ens, vil Proxy'en returnere dem mange gange, men gå til ClickHouse kun én gang.

Der er også cache i den gratis version af Nginx, og det vil også fungere. Nginx har endda indstillinger, der betyder, at hvis anmodninger kommer samtidig, vil det bremse de andre, indtil én er gennemført. Men i ClickHouse Proxy er indstillingen meget bedre lavet. Den er lavet specifikt til ClickHouse, netop til disse anmodninger, så det passer bedre. Og det installeres let.

Hvordan skal man håndtere asynkrone operationer og materialiserede visninger?

Der er et problem med, at operationer med replikeringsmotoren er asynkrone – først skrives dataene, derefter sker der en komprimering. Hvis der under tabellen er en materialiseret tabel med nogle aggregationer, vil dubletter skrive sig ind i den. Og hvis der ikke er nogen kompleks logik, vil dataene blive duplikeret. Hvad kan der gøres ved dette?

Der er en åbenlys løsning – at implementere en trigger for en bestemt klasse af materialiserede visninger ved asynkrone komprimeringsoperationer. Er der nogen 'sølvkugler', planer for implementering af sådanne funktionaliteter?

Det er vigtigt at forstå, hvordan deduplisering fungerer. Det, jeg nu vil fortælle, vedrører ikke spørgsmålet, men det er værd at huske på.

Når der indsat i en replikeret tabel, er der deduplisering af de indsatte blokke. Hvis du indsætter den samme blok igen, der indeholder det samme antal af identiske rækker i samme rækkefølge, vil dataene blive deduplikeret. Du får "Ok" som svar på indsat, men faktisk vil én pakke data blive skrevet, og den vil ikke blive duplikeret.

Dette er nødvendigt for klarhedens skyld. Hvis du fik "Ok" under indsættelsen, betyder det, at dine data er indsat. Hvis du fik en fejl fra ClickHouse, betyder det, at de ikke blev indsat, og indsættelsen skal gentages. Men hvis forbindelsen blev afbrudt under indsættelsen, ved du ikke, om dataene er indsat eller ej. Den eneste løsning er at gentage indsættelsen. Hvis dataene faktisk blev indsat, og du indsatte dem igen, findes der deduplisering af blokke. Dette er nødvendigt for at undgå dubletter.

Og det er også vigtigt, hvordan det fungerer for materialiserede visninger. Hvis dataene blev deduplikeret ved indsættelsen i hovedtabellen, vil de heller ikke gå ind i den materialiserede visning.

Nu til spørgsmålet. I har en mere kompleks situation, fordi I optager dubletter af individuelle linjer. Det vil sige, at det ikke er en hel bunke, der bliver dubleret, men specifikke linjer, og de kollapser i baggrunden. Faktisk vil dataene blive kollapset i den primære tabel, mens de ikke-kollapsede går til den materialiserede visning, og der vil ikke ske noget med de materialiserede visninger under sammensætninger. Fordi den materialiserede visning ikke er andet end en trigger på insert. Ved andre operationer sker der ingenting yderligere med den.

Og jeg kan desværre ikke glæde mig over dette. Vi skal kun finde en konkret løsning til denne situation. For eksempel, er det muligt at lave en erstatning i den materialiserede visning, og måske vil metoden til deduplication også fungere. Men desværre er det ikke altid tilfældet. Hvis den er aggregerende, går det ikke.

Kirill Shvakov: Vi havde også vores eget besvær på et tidspunkt. Der var et problem med visninger af annoncer, og der var nogle data, som vi kunne vise i realtid — det var bare visningerne. De bliver sjældent dubleret, men hvis det sker, kollapser vi dem stadig. Og der var ting, som man ikke kunne dubleres — klik og hele den historie. Men man ville også gerne vise dem næsten med det samme.

Hvordan blev de materialiserede visninger lavet? Der var visninger, hvor dataene skrives direkte — der er skrivning til rå data, og de skrives til visningerne. Der var på et tidspunkt, hvor dataene ikke var helt korrekte, de blev dubleret osv. Og der er en anden del af tabellen, der ser identisk ud som de materialiserede visninger, det vil sige, strukturen er helt den samme. En gang i mellem genberegner vi dataene, tæller dataene uden dubletter og skriver til de tabeller.

Vi gik gennem API'et — at gøre dette manuelt i ClickHouse vil ikke fungere. Og API'et ser: når jeg har datoen for den sidste tilføjelse i tabellen, hvor der allerede er garanteret korrekte data, og den laver en forespørgsel til en tabel og til en anden tabel. Fra den ene tabel vælger den op til et bestemt tidspunkt, og fra den anden vælges det, der endnu ikke er beregnet. Og det fungerer, men ikke ved hjælp af én ClickHouse.

Hvis du har et API — til analytikere, til brugere — så er det i princippet en mulighed. Du tæller altid, du genberegner altid. Dette kan gøres en gang om dagen eller på et andet tidspunkt. Du vælger selv det interval, som ikke er nødvendigt og ikke kritisk for dig.

I ClickHouse er der mange logs. Hvordan kan jeg se alt, hvad der sker med serveren, i realtid?

Der er et meget stort antal forskellige logs i ClickHouse, og dette antal stiger. I de nye versioner er nogle af dem endda aktiveret som standard, mens de i de gamle versioner skal aktiveres ved opdatering. Ikke desto mindre bliver der flere og flere. Jeg vil gerne se, hvad der sker nu med min server, måske på et samlet dashboard.

Har du ikke nogen i dit ClickHouse-team, eller i dine venners teams, der understøtter en eller anden funktionalitet til færdige dashboards, som ville vise disse logs i form af et færdigt produkt? I sidste ende, at se logs i ClickHouse er fint. Men det ville være fantastisk, hvis det allerede var forberedt i form af et dashboard. Jeg ville virkelig nyde det.

Der er dashboards, men de er ikke standardiserede. I vores virksomhed bruger omkring 60 teams ClickHouse, og det mærkelige er, at mange af dem har dashboards, som de selv har lavet, og de er lidt forskellige. Nogle teams bruger en intern installation af 'Yandex.Cloud'. Der er nogle færdige rapporter, selvom ikke alle nødvendige er tilgængelige. Andre har deres egne.

Mine kolleger fra 'Metrics' har deres eget dashboard i Grafana, og jeg har mit på deres kluster. Jeg ser ting som cache hit for cache-målinger. Og det bliver endnu mere kompliceret, fordi vi bruger forskellige værktøjer. Jeg har lavet mit dashboard med et meget gammelt værktøj, der hedder Graphite-web. Det er helt grimt. Og jeg bruger det stadig, selvom Grafana nok ville være mere bekvemt og pænere.

Den grundlæggende ting i dashboards er ens. Det er systemmetrikker for klyngen: CPU, hukommelse, disk, netværk. Andre inkluderer antallet af samtidige forespørgsler, antallet af samtidige merger, antallet af forespørgsler pr. sekund, det maksimale antal stykker til partitioner i MergeTree-tabeller, replikeringslag, størrelsen på replikeringskøen, antallet af indsatte rækker pr. sekund, antallet af indsatte blokke pr. sekund. Det er alt, hvad der ikke kommer fra logs, men fra metrikker.

Vladimir Kolobaev: Alexey, jeg ville gerne justere lidt. Der er Grafana. Grafana har en datakilde, som er ClickHouse. Så jeg kan lave forespørgsler direkte i ClickHouse fra Grafana. I ClickHouse er der en tabel med logs, som er ens for alle. Jeg vil i resultatet fra Grafana henvise til denne logtabel og se de forespørgsler, som min server laver. Det ville være fantastisk at have et sådant dashboard.

Jeg har selv skabt det. Men jeg har et spørgsmål - hvis det hele er standardiseret, og Grafana bruges af alle, hvorfor findes der så ikke et officielt dashboard i 'Yandex'?

Kirill Shvakov: Faktisk er datakilden, der er til ClickHouse, aktuelt understøttet af Altinity. Og jeg vil bare give en retningslinje for, hvor man skal grave og hvem man skal presse. Man kan spørge dem, fordi 'Yandex' trods alt laver ClickHouse, ikke historien omkring det. Altinity er det hovedfirma, der i øjeblikket promoverer ClickHouse. De vil ikke opgive det, men vil støtte det. For i princippet kræver det kun at registrere sig og uploade for at få dashboardet på Grafana-webstedet - der er ikke særlige problemer.

Alexey Milovidov: I det sidste år er der tilføjet mange muligheder til ClickHouse til forespørgselsprofiler. Der er metrikker for hver forespørgsel i forhold til ressourceforbrug. For nylig er der også blevet tilføjet en mere lavniveau profileringsværktøj til forespørgsler, så man kan se, hvor hver enkelt millisekund bruges. Men for at kunne udnytte denne funktionalitet, er jeg nødt til at åbne klientkonsollen og skrive den forespørgsel, som jeg konstant glemmer. Jeg har gemt den et sted, men jeg glemmer konstant, hvor præcist.

Jeg ville ønske, at der fandtes et værktøj, som simpelthen sagde – her er dine tunge forespørgsler, grupperet efter forespørgselsklasser. Jeg trykker på en af dem, og jeg får at vide, at den er tung af denne grund. Der findes i øjeblikket ikke en sådan løsning. Det er virkelig ret mærkeligt, at når folk spørger mig: "Sig mig, findes der nogen færdige dashboards til Grafana?", siger jeg: "Gå ind på Grafana-webstedet, der er fællesskabet 'Dashboards', og der er et dashboard fra Dimka, der er et dashboard fra Kostyan. Hvad det er, ved jeg ikke, jeg har ikke selv brugt det."

Hvordan kan jeg påvirke merge-processer, så serveren ikke falder i OOM?

Jeg har en tabel, der kun har én partition, den er ReplacingMergeTree. Jeg skriver data i den i fire år. Jeg skulle lave et alter og slette nogle data.

Jeg gjorde det, og under behandlingen af denne forespørgsel blev al hukommelsen på alle servere i klyngen brugt, og alle servere i klyngen gik i OOM. Så kom de alle sammen op, begyndte at udføre merge af den samme operation, denne datasæt, og faldt igen i OOM. Så kom de op igen og faldt igen. Og denne situation stoppede ikke.

Så viste det sig, at det faktisk var en bug, som drengene fik fikset. Det er rigtig godt, tusind tak. Men der er stadig en rest. Og nu når jeg tænker på, hvad jeg skal gøre ved at lave et merge i tabellen, opstår spørgsmålet – hvorfor kan jeg ikke på en eller anden måde påvirke disse merges? For eksempel begrænse dem i forhold til den nødvendige hukommelse, eller i det hele taget begrænse antallet, der vil behandle netop denne tabel.

Jeg har en tabel, der hedder 'Metrics', venligst behandl den for mig i to tråde. Lad være med at lave ti eller fem merges parallelt, gør det i to. Jeg tror, at jeg har tilstrækkelig hukommelse til to, men måske ikke til ti. Hvorfor forbliver frygten? Fordi tabellen vokser, og en dag vil jeg stå over for en situation, hvor det ikke længere er på grund af en bug, men på grund af at dataene ændrer sig i så stor mængde, at jeg simpelthen ikke har nok hukommelse på serveren. Og så vil serveren falde i OOM under merge. Jeg kan annullere mutationer, men merges kan jeg ikke.

Du ved, at serveren ikke vil falde i OOM ved merges, fordi der kun bruges en lille mængde RAM til et lille datainterval under mergeprocessen. Så alt vil være i orden, uanset datamængden.

Vladimir Kolobaev: Godt. Her er det sådan, at efter jeg lavede en bugfix, downloadede jeg den nye version, og på et andet, mindre tabel, hvor der er mange partitioner, foretog jeg en lignende operation. Og under mergeprocessen blev der brugt omkring 100 GB RAM på serveren. Jeg havde 150 GB optaget, 100 GB blev brugt, og der var et vindue på 50 GB, så jeg faldt ikke i OOM.

Hvad beskytter mig i øjeblikket mod at falde i OOM, hvis det virkelig bruger 100 GB RAM? Hvad skal man gøre, hvis RAM'en pludselig løber tør under merges?

Alexey Milovidov: Der er et problem, at RAM-forbruget under merges ikke er begrænset. Og det andet problem er, at hvis en merge er planlagt, skal den udføres, fordi den er skrevet i replikeringsloggen. Replikeringsloggen er de handlinger, der er nødvendige for at bringe replikationen i en konsistent tilstand. Hvis der ikke udføres manuelle manipulationer, som ruller denne replikeringslog tilbage, skal merge’en, på den ene eller anden måde, udføres.

Selvfølgelig ville det ikke være en dårlig idé at have en begrænsning på RAM, der "for en sikkerheds skyld" beskytter mod OOM. Det vil ikke hjælpe merge-processen til at gennemføres, den vil starte igen, nå til et bestemt punkt, kaste en undtagelse og så starte igen - der vil ikke komme noget godt ud af det. Men principielt ville det være nyttigt at indføre en sådan begrænsning.

Hvordan vil udviklingen af Golang-driveren til ClickHouse forløbe?

Golang-driveren, som Kirill Shvakov har skrevet, understøttes nu officielt af ClickHouse-teamet. Den er i ClickHouse-repositoriet, den er nu stor og rigtig.

En lille bemærkning. Der er et fantastisk og elsket lager til normale former for uendelig orden — det er Vertica. De har også deres officielle Python-driver, som vedligeholdes af Vertica-udviklerne. Flere gange har det været sådan, at versionerne af lagret og driveren har været meget forskellige, og driveren har i en periode ophørt med at fungere. Og et andet punkt. Supporten for denne officielle driver, mener jeg, drives gennem et system kaldet «nipling» — du skriver dem et issue, og det hænger for evigt.

Jeg har to spørgsmål. Lige nu er Kirills Golang-driver næsten den standardmetode, der bruges til at kommunikere fra Golang til ClickHouse. Måske er der nogen, der stadig kommunikerer via http-grænsefladen, fordi de foretrækker det. Hvordan vil udviklingen af denne driver finde sted? Vil den blive synkroniseret med eventuelle breaking changes i selve lageret? Og hvad er proceduren for behandling af issues?

Kirill Shvakov: For det første — hvordan det hele fungerer bureaukratisk. Dette punkt er ikke blevet diskuteret, så jeg har ikke noget svar.

For at besvare spørgsmålet om issues, er der behov for en lille historie om driveren. Jeg arbejdede i et firma, hvor der var mange data. Det var en reklame-generator med en enorm mængde begivenheder, der skulle opbevares et sted. Og på et tidspunkt dukkede ClickHouse op. Vi fyldte data i det, og i starten var alt godt, men så faldt ClickHouse. På det tidspunkt besluttede vi, at det ikke var noget for os.

Et år senere vendte vi tilbage til idéen om at bruge ClickHouse, og vi skulle finde en måde at skrive data til det på. Indgangen var sådan — hårdvaren var meget svag, ressourcerne var få. Men vi havde altid arbejdet på den måde, så vi kiggede mod den native protokol.

Da vi arbejdede med Go, var det klart, at vi havde brug for en driver til Go. Jeg arbejdede praktisk talt på det fuld tid — det var min opgave. Indtil et vist punkt fik vi det færdigt, og i princippet forventede ingen, at nogen andre ville bruge det. Så kom CloudFlare med nøjagtigt det samme problem, og i et stykke tid arbejdede vi ret tæt sammen, fordi de havde de samme opgaver. Vi arbejdede faktisk både i ClickHouse selv og i driveren.

På et tidspunkt stoppede jeg bare med at arbejde med dem, fordi min aktivitet i forhold til ClickHouse og mit arbejde ændrede sig en smule. Derfor bliver problemerne ikke løst. Af og til committer folk til repositoryet, fordi de har brug for noget. Så kigger jeg på pull-requests og nogle gange retter jeg lidt, men det sker sjældent.

Jeg vil gerne vende tilbage til driveren. For flere år siden, da det hele startede, var ClickHouse også anderledes og havde andre muligheder. Nu er der forståelse for, hvordan man kan redesigne driveren, så den fungerer godt. Hvis det sker, så vil version 2 i hvert fald være inkompatibel på grund af de akkumulerede løsninger.

Hvordan jeg skal organisere dette, ved jeg ikke. Jeg har ikke så meget tid selv. Hvis nogle folk vil forbedre driveren, kan jeg hjælpe dem og fortælle, hvad de skal gøre. Men aktiv deltagelse fra Yandex i udviklingen af projektet er endnu ikke blevet diskuteret.

Alexey Milovidov: Der er faktisk ikke nogen bureaukrati omkring disse drivere endnu. Det eneste er, at de er blevet overført til en officiel organisation, så denne driver er anerkendt som en officiel løsning for Go. Der er andre drivere, men de kommer separat.

Vi har ikke nogen udvikling af disse drivere internt. Spørgsmålet er, om vi kan ansætte en person, ikke specifikt til denne driver, men til udviklingen af alle community-drivere, eller om vi kan finde nogen udefra.

Det eksterne ordbog bliver ikke indlæst efter genstart med indstillingen lazy_load aktiveret. Hvad skal vi gøre?

Vi har aktiveret indstillingen lazy_load, og efter servergenstart bliver ordbogen ikke indlæst automatisk. Den indlæses kun, når en bruger tilgår denne ordbog. Og ved den første adgang giver det en fejl. Kan vi på en eller anden måde automatisk indlæse ordbøgerne med ClickHouse, eller skal vi altid selv kontrollere deres tilgængelighed, så brugerne ikke får fejl?

Måske har vi en gammel version af ClickHouse, så ordbogen blev ikke indlæst automatisk. Kan det være tilfældet?

For det første kan ordbøgerne indlæses tvangsvis ved hjælp af forespørgslen system reload dictionaries. For det andet, med hensyn til fejlen — hvis ordbogen allerede er indlæst, så vil forespørgslerne virke med de data, der er indlæst. Hvis ordbogen endnu ikke er blevet indlæst, vil den blive indlæst samtidig med forespørgslen.

For heavy dictionaries, this is not very convenient. For example, you may need to pull a million rows from MySQL. Some people simply do a select, but that select will wait for those million rows. Here are two solutions. The first is to turn off lazy_load. The second is, when the server is started, before putting any load on it, to make system reload dictionary or simply execute a query that uses the dictionary. Then the dictionary will be loaded. You need to control the availability of dictionaries with the lazy_load setting enabled because ClickHouse does not automatically pull them.

To the last question, the answer is either the version is old, or we need to debug it.

Hvad skal man gøre, når systemet ikke indlæser nogen af de mange ordbøger, hvis blot én af dem fejler?

There is another question regarding system reload dictionaries. We have two dictionaries—one does not load, the other does. System reload dictionaries in this case does not load any dictionary, and we have to manually load a specific one by its name using system reload dictionary. Is this also related to the ClickHouse version?

I want to bring you good news. This behavior has changed. So if you update ClickHouse, it will change as well. If you are not satisfied with the current behavior system reload dictionaries, update, and let’s hope it will change for the better.

Er der en måde at konfigurere legitimationsoplysninger i ClickHouse-konfigurationen uden at afsløre dem ved fejl?

The next question is about errors related to the dictionary, specifically credentials. We wrote the connection credentials in the ClickHouse config for the dictionary, and in the event of an error, we get these credentials and the password in the response.

We resolved this error by moving the credentials to the ODBC driver config. Is there any way to configure the credentials in the ClickHouse config without exposing these credentials during errors?

Here the solution is indeed to specify these credentials in odbc.ini, and in ClickHouse itself to specify only the ODBC Data Source Name. For other dictionary sources, this won't be the case—neither for the MySQL dictionary nor others should you see the password in the error message. For ODBC, I will also check—if there is such a case, we simply need to remove it.

Bonus: baggrunde til Zoom fra møderne

By clicking on the image, the most persistent readers will unlock bonus backgrounds from gatherings. We extinguish the fire together with Avito technology mascots, discuss with colleagues from the sysadmin room, or an old-school computer club, and hold daily meetings under the bridge against a backdrop of graffiti.

ClickHouse for avancerede brugere i spørgsmål og svar

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster