Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Da ClickHouse er et specialiseret system, er det vigtigt at tage højde for det særlige ved dets arkitektur, når du bruger det. I denne rapport vil Alexey fortælle om eksempler på typiske fejl ved brug af ClickHouse, som kan føre til ineffektivt arbejde. Ved hjælp af praktiske eksempler vil vi vise, hvordan valget af et eller andet databehandlingsskema kan ændre ydeevnen i størrelsesordener.

Hej alle! Mit navn er Alexey, jeg opretter ClickHouse.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

For det første skynder jeg mig at behage dig med det samme, jeg vil ikke fortælle dig i dag, hvad ClickHouse er. For at være ærlig, så er jeg træt af det. Jeg fortæller dig hver gang, hvad det er. Og det ved sikkert alle allerede.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

I stedet vil jeg fortælle dig, hvad den mulige rake er, altså hvordan ClickHouse kan misbruges. Faktisk skal du ikke være bange, for vi udvikler ClickHouse som et system, der er enkelt, bekvemt og fungerer ud af boksen. Installeret alt, intet problem.

Men alligevel skal det huskes på, at dette system er specialiseret, og du kan nemt falde over en usædvanlig use case, der vil tage dette system ud af sin komfortzone.

Så hvad er riverne? Grundlæggende vil jeg tale om de åbenlyse ting. Alt er indlysende for alle, alle forstår alt og kan glæde sig over, at de er så kloge, og de, der ikke forstår, vil lære noget nyt.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Det første simpleste eksempel, som desværre ofte forekommer, er et stort antal skær med små partier, altså et stort antal små skær.

Hvis vi overvejer, hvordan ClickHouse udfører en indsættelse, så kan du sende mindst en terabyte data i en anmodning. Det er ikke et problem.

Og lad os se, hvad den typiske præstation vil være. For eksempel har vi en tabel med Yandex.Metrics-data. Hits. 105 nogle kolonner. 700 bytes ukomprimeret. Og vi vil på en god måde indsætte partier på en million linjer.

Vi indsætter i MergeTree-tabellen, en halv million rækker per sekund opnås. Store. I en replikeret tabel - vil det være lidt mindre, omkring 400 rækker i sekundet.

Og hvis du slår quorum-indsatsen til, får du lidt mindre, men stadig anstændig præstation, 250 gange i sekundet. Quorum Insertion er en udokumenteret funktion i ClickHouse*.

* fra 2020, allerede dokumenteret.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Hvad sker der, hvis du gør det forkert? Vi indsætter en række i MergeTree-tabellen, og vi får 59 rækker i sekundet. Det er 10 gange langsomt. I ReplicatedMergeTree - 000 rækker i sekundet. Og hvis kvorumet tændes, opnås 6 linjer i sekundet. Efter min mening er dette en slags fuldstændig lort. Hvordan kan du sænke farten sådan? Der står endda på min T-shirt, at ClickHouse ikke må sætte farten ned. Men alligevel sker det nogle gange.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Faktisk er dette vores mangel. Vi kunne have fået det til at fungere fint, men det gjorde vi ikke. Og det gjorde vi ikke, for vores manuskript behøvede det ikke. Vi havde allerede partier. Vi har lige modtaget partier ved indgangen, og ingen problemer. Sæt den i og alt fungerer fint. Men selvfølgelig er alle mulige scenarier mulige. For eksempel når du har en flok servere, hvorpå der genereres data. Og de indsætter ikke data så ofte, men de får stadig hyppige indsættelser. Og du skal på en eller anden måde undgå dette.

Fra et teknisk synspunkt er bundlinjen, at når du laver en indsættelse i ClickHouse, kommer dataene ikke ind i nogen memtable. Vi har ikke engang en rigtig MergeTree-logstruktur, men blot et MergeTree, for der er hverken log eller memTable. Vi skriver bare straks dataene til filsystemet, allerede opdelt i kolonner. Og hvis du har 100 kolonner, skal mere end 200 filer skrives til en separat mappe. Alt dette er meget besværligt.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og spørgsmålet opstår: "Hvordan gør man det rigtigt?" Hvis en sådan situation er, skal du stadig på en eller anden måde skrive data til ClickHouse.

Metode 1. Dette er den nemmeste måde. Brug en form for distribueret kø. For eksempel Kafka. Du tager bare data ud af Kafka, vi batcherer dem en gang i sekundet. Og alt bliver fint, du optager, alt fungerer fint.

Ulemperne er, at Kafka er et andet besværligt distribueret system. Jeg forstår også, hvis du allerede har Kafka i dit firma. Det er godt, det er praktisk. Men hvis det ikke er der, så bør du tænke dig tre gange om, før du trækker endnu et distribueret system ind i dit projekt. Og derfor er det værd at overveje alternativer.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Metode 2. Her er sådan et old-school alternativ og samtidig meget simpelt. Har du en slags server, der genererer dine logfiler. Og den skriver bare dine logfiler til en fil. Og en gang i sekundet omdøber vi for eksempel denne fil, åbner en ny. Og et separat script enten af ​​cron eller en dæmon tager den ældste fil og skriver den til ClickHouse. Hvis du skriver logs en gang i sekundet, så vil alt være fint.

Men ulempen ved denne metode er, at hvis din server, som logfilerne genereres på, er forsvundet et sted, så forsvinder dataene også.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Metode 3. Der er en anden interessant måde, som er uden midlertidige filer overhovedet. For eksempel har du en form for reklamespinner eller en anden interessant dæmon, der genererer data. Og du kan akkumulere en masse data lige i RAM'en, i bufferen. Og når der går tilstrækkelig lang tid, lægger du denne buffer til side, opretter en ny og indsætter det, der allerede er akkumuleret i ClickHouse i en separat tråd.

På den anden side forsvinder dataene også med kill -9. Hvis din server går ned, vil du miste disse data. Og et andet problem er, at hvis du ikke kunne skrive til databasen, så vil dine data akkumuleres i RAM'en. Og enten løber RAM'en tør, eller også mister du bare data.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Metode 4. En anden interessant måde. Har du nogen serverproces. Og han kan sende data til ClickHouse på én gang, men gør det i én forbindelse. For eksempel sendte jeg en http-anmodning med transfer-encoding: chunked with insert. Og det genererer ikke alt for sjældent bidder, du kan sende hver linje, selvom der vil være en overhead til indramning af disse data.

Men i dette tilfælde vil dataene blive sendt til ClickHouse med det samme. Og ClickHouse selv vil buffere dem.

Men der er også problemer. Nu vil du miste data, herunder når din proces er dræbt, og hvis ClickHouse-processen er dræbt, fordi det vil være en ufuldstændig indsættelse. Og i ClickHouse er indsatser atomare op til en bestemt tærskel i størrelsen af ​​rækker. I princippet er dette en interessant måde. Kan også bruges.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Metode 5. Her er en anden interessant måde. Dette er en slags community-udviklet server til databatching. Jeg har ikke selv kigget på det, så jeg kan ikke garantere noget. Der er dog ingen garantier for ClickHouse selv. Dette er også open source, men på den anden side kan du vænne dig til en eller anden kvalitetsstandard, som vi forsøger at levere. Men for denne ting - jeg ved det ikke, gå til GitHub, se på koden. Måske har de skrevet noget godt.

*fra 2020, bør også tages i betragtning Killingehus.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Metode 6. En anden måde er at bruge buffertabeller. Fordelen ved denne metode er, at den er meget nem at komme i gang med. Opret en buffertabel og indsæt i den.

Men ulempen er, at problemet ikke er helt løst. Hvis du ved en hastighed af typen MergeTree skal gruppere data med én batch pr. sekund, så skal du gruppere mindst op til flere tusinde pr. sekund med en hastighed i en buffertabel. Hvis der er mere end 10 i sekundet, vil det stadig være dårligt. Og hvis du indsætter i batches, så du, at der opnås hundrede tusinde linjer i sekundet. Og dette er allerede på ret tunge data.

Og også buffertabeller har ikke en log. Og hvis der er noget galt med din server, så går dataene tabt.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og som en bonus havde vi for nylig mulighed for at indsamle data fra Kafka i ClickHouse. Der er en bordmotor - Kafka. Du skaber simpelthen. Og du kan hænge materialiserede synspunkter på det. I dette tilfælde vil han tage dataene ud fra Kafka og indsætte dem i de tabeller, du har brug for.

Og det, der er særligt glædeligt ved denne mulighed, er, at vi ikke nåede den. Dette er en fællesskabsfunktion. Og når jeg siger "fællesskabstræk", siger jeg det uden nogen foragt. Vi læste koden, lavede en anmeldelse, den burde fungere fint.

* fra og med 2020 er der lignende støtte til RabbitMQ.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Hvad ellers kan være ubelejligt eller uventet, når du indsætter data? Hvis du laver en indsæt værdiforespørgsel og skriver nogle beregnede udtryk i værdier. For eksempel er now() også et evalueret udtryk. Og i dette tilfælde er ClickHouse tvunget til at starte fortolkeren af ​​disse udtryk for hver linje, og ydeevnen vil falde i størrelsesordener. Bedre at undgå det.

* i øjeblikket er problemet fuldstændigt løst, der er ikke mere præstationsregression ved brug af udtryk i VALUES.

Et andet eksempel, hvor der kan være nogle problemer, er når dine data på en batch tilhører en masse partitioner. Som standard opdeles ClickHouse efter måned. Og hvis du indsætter en batch på en million rækker, og der er data i flere år, så vil du have flere dusin partitioner der. Og det svarer til, at der vil være partier flere titusinder mindre, for indeni er de altid først opdelt i skillevægge.

* for nylig i ClickHouse i eksperimentel tilstand tilføjet understøttelse af det kompakte format af bidder og bidder i RAM med skrive-ahead-log, som næsten fuldstændig løser problemet.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Overvej nu den anden type problem - dataindtastning.

Dataindtastning kan være streng, og nogle gange streng. String - det er, når du lige tog og erklærede, at du har alle felter af typen streng. Det stinker. Det behøver du ikke gøre.

Lad os finde ud af, hvordan du gør det rigtigt i tilfælde, hvor du vil sige, at vi har noget felt, en snor, og lad ClickHouse finde ud af det på egen hånd, men jeg vil ikke tage et dampbad. Men det er stadig værd at anstrenge sig.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

For eksempel har vi en IP-adresse. I et tilfælde gemte vi det som en streng. For eksempel 192.168.1.1. Ellers vil det være et antal af typen UInt32*. 32 bit er nok til en IPv4-adresse.

For det første vil dataene mærkeligt nok blive komprimeret omtrent det samme. Der vil selvfølgelig være en forskel, men ikke så stor. Så der er ingen særlige problemer med disk I/O.

Men der er en alvorlig forskel i CPU-tid og forespørgselsudførelsestid.

Lad os tælle antallet af unikke IP-adresser, hvis de er gemt som numre. Det viser sig 137 millioner linjer i sekundet. Hvis det er det samme som linjer, så 37 millioner linjer i sekundet. Jeg ved ikke, hvorfor denne tilfældighed skete. Jeg har selv lavet disse anmodninger. Men ikke desto mindre cirka 4 gange langsommere.

Og beregner man forskellen på diskplads, så er der også forskel. Og forskellen er omkring en fjerdedel, fordi der er en hel del unikke IP-adresser. Og hvis der var linjer med et lille antal forskellige værdier, så ville de stille og roligt være blevet komprimeret i ordbogen til omtrent samme bind.

Og den firedobbelte tidsforskel ligger ikke på vejen. Måske er du selvfølgelig ligeglad, men når jeg ser sådan en forskel, bliver jeg ked af det.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Lad os overveje forskellige tilfælde.

1. Et tilfælde, hvor du har få forskellige unikke værdier. I dette tilfælde bruger vi en simpel praksis, som du sikkert kender og kan bruge til enhver DBMS. Alt dette giver mening ikke kun for ClickHouse. Bare skriv de numeriske identifikatorer til databasen. Og du kan konvertere til strenge og tilbage på siden af ​​din applikation.

For eksempel har du en region. Og du forsøger at gemme den som en streng. Og det vil blive skrevet der: Moskva og Moskva-regionen. Og når jeg ser, at der står "Moskva", så er det her stadig ingenting, og når det er MO, bliver det på en eller anden måde helt trist. Det er hvor mange bytes.

I stedet skriver vi blot Ulnt32-nummeret og 250 ned. Vi har 250 i Yandex, men dit kan være anderledes. For en sikkerheds skyld vil jeg sige, at ClickHouse har en indbygget evne til at arbejde med en geobase. Du skriver blot en mappe med regioner ned, inklusive en hierarkisk, dvs. der vil være Moskva, Moskva-regionen og alt hvad du har brug for. Og du kan konvertere på anmodningsniveau.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Den anden mulighed er omtrent den samme, men med support inde i ClickHouse. Det er en Enum-datatype. Du skriver blot alle de værdier, du har brug for, inde i Enum. For eksempel, hvilken type enhed og skrive der: desktop, mobil, tablet, TV. Kun 4 muligheder.

Ulempen er, at du med jævne mellemrum skal ændre. Der er kun tilføjet én mulighed. Vi laver ændre bord. Faktisk er ændringstabel i ClickHouse gratis. Især gratis for Enum, fordi dataene på disken ikke ændres. Men ikke desto mindre får alter en lås * på bordet og må vente, indtil alle valg er gennemført. Og først efter at denne ændring vil blive udført, dvs. der er stadig nogle gener.

* i nyere versioner af ClickHouse er ALTER lavet fuldstændigt ikke-blokerende.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

En anden mulighed helt unik for ClickHouse er tilslutningen af ​​eksterne ordbøger. Du kan skrive numre i ClickHouse og opbevare dine telefonbøger i et hvilket som helst system, der passer dig. For eksempel kan du bruge: MySQL, Mongo, Postgres. Du kan endda oprette din egen mikrotjeneste, som sender disse data via http. Og på ClickHouse-niveau skriver du en funktion, der konverterer disse data fra tal til strenge.

Dette er en specialiseret, men meget effektiv måde at udføre en join på et eksternt bord. Og der er to muligheder. I en mulighed vil disse data være fuldt cachelagret, fuldt ud til stede i RAM'en og opdateret med nogle intervaller. Og i en anden mulighed, hvis disse data ikke passer ind i RAM'en, kan du delvist cache dem.

Her er et eksempel. Der er Yandex.Direct. Og der er et reklamefirma og bannere. Der er formentlig titusinder af reklamevirksomheder. Og passer nogenlunde i RAM'en. Og der er milliarder af bannere, de passer ikke. Og vi bruger en cachelagret ordbog fra MySQL.

Det eneste problem er, at den cachelagrede ordbog vil fungere fint, hvis hitraten er tæt på 100%. Hvis det er mindre, så vil det, når du behandler anmodninger for hver datapakke, være nødvendigt rent faktisk at tage de manglende nøgler og gå for at tage data fra MySQL. Om ClickHouse kan jeg stadig garantere, at - ja, det bremser ikke, jeg vil ikke tale om andre systemer.

Og som en bonus er ordbøger en meget nem måde at opdatere data i ClickHouse med tilbagevirkende kraft. Det vil sige, du havde en rapport om reklamevirksomheder, brugeren ændrede simpelthen reklamevirksomheden og i alle de gamle data, i alle rapporter, ændrede disse data sig også. Hvis du skriver rækker direkte til tabellen, vil du ikke kunne opdatere dem.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

En anden måde, når du ikke ved, hvor du skal hente identifikatorerne til dine strenge. du kan bare hash. Og den nemmeste mulighed er at tage en 64-bit hash.

Det eneste problem er, at hvis hashen er 64-bit, så vil du næsten helt sikkert have kollisioner. For hvis der er en milliard linjer, så er sandsynligheden allerede ved at blive håndgribelig.

Og det ville ikke være særlig godt at hash navnene på reklamevirksomheder på den måde. Hvis forskellige virksomheders reklamekampagner bliver blandet sammen, så vil der være noget uforståeligt.

Og der er et simpelt trick. Sandt nok er det heller ikke særlig velegnet til seriøse data, men hvis noget ikke er særlig seriøst, skal du blot tilføje en anden klient-id til ordbogsnøglen. Og så vil du have kollisioner, men kun inden for én klient. Og vi bruger denne metode til linkkortet i Yandex.Metrica. Vi har urls der, vi gemmer hash. Og vi ved, at der selvfølgelig er konflikter. Men når en side vises, så er sandsynligheden for, at det er på én side for én bruger, at nogle urls hænger sammen, og dette vil blive bemærket, så kan dette negligeres.

Som en bonus, for mange operationer, er kun hashes nok, og selve strengene kan ikke gemmes nogen steder.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Et andet eksempel, hvis strengene er korte, såsom webstedsdomæner. De kan opbevares som de er. Eller for eksempel browsersproget ru er 2 bytes. Jeg har selvfølgelig ondt af bytes, men bare rolig, 2 bytes er ikke synd. Hold det venligst som det er, bare rolig.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Et andet tilfælde er, når der tværtimod er mange strenge og samtidig er der mange unikke i dem, og selv sættet er potentielt ubegrænset. Et typisk eksempel er søgesætninger eller webadresser. Søgesætninger, herunder på grund af tastefejl. Lad os se, hvor mange unikke søgesætninger pr. dag. Og det viser sig, at de er næsten halvdelen af ​​alle arrangementer. Og i dette tilfælde tror du måske, at du skal normalisere dataene, tælle identifikatorerne, lægge dem i en separat tabel. Men det behøver du ikke gøre. Bare hold disse linjer, som de er.

Bedre - lad være med at opfinde noget, for hvis du opbevarer det separat, bliver du nødt til at lave en join. Og denne join er i bedste fald en tilfældig adgang til hukommelsen, hvis den stadig passer i hukommelsen. Hvis det ikke passer, så vil der være problemer generelt.

Og hvis dataene er gemt på plads, så læses de simpelthen i den rigtige rækkefølge fra filsystemet, og alt er i orden.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Hvis du har url'er eller en anden kompleks lang streng, så bør du tænke på, at du på forhånd kan beregne noget squeeze og skrive det i en separat kolonne.

For urls kan du for eksempel gemme domænet separat. Og hvis du virkelig har brug for et domæne, så brug bare denne kolonne, og url'erne vil lyve, og du vil ikke engang røre dem.

Lad os se, hvad forskellen er. ClickHouse har en specialiseret funktion, der beregner domænet. Det er meget hurtigt, vi har optimeret det. Og for at være ærlig, så overholder den ikke engang RFC, men ikke desto mindre tager den hensyn til alt, hvad vi har brug for.

Og i et tilfælde vil vi blot få url'erne og beregne domænet. Det viser sig 166 millisekunder. Og hvis du tager et færdiglavet domæne, viser det sig kun 67 millisekunder, det vil sige næsten tre gange hurtigere. Og hurtigere, ikke fordi vi skal lave nogle beregninger, men fordi vi læser mindre data.

Af en eller anden grund får en anmodning, som er langsommere, mere hastighed i gigabyte per sekund. Fordi den læser flere gigabyte. Dette er fuldstændig overflødige data. Anmodningen ser ud til at køre hurtigere, men tager længere tid at fuldføre.

Og hvis man ser på mængden af ​​data på disken, viser det sig, at URL'en er 126 megabyte, og domænet er kun 5 megabyte. Det viser sig 25 gange mindre. Forespørgslen er dog stadig kun 4 gange hurtigere. Men det er fordi dataene er varme. Og hvis det var koldt, ville det sandsynligvis være 25 gange hurtigere på grund af disk I/O.

Forresten, hvis du vurderer, hvor meget domænet er mindre end URL'en, så viser det sig at være omkring 4 gange. Men af ​​en eller anden grund tager dataene på disken 25 gange mindre. Hvorfor? På grund af kompression. Og url'en er komprimeret, og domænet er komprimeret. Men ofte indeholder url'en en masse affald.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og det er selvfølgelig værd at bruge de rigtige datatyper, der er specifikt designet til de rigtige værdier, eller som passer. Hvis du er i IPv4, så gem UInt32*. Hvis IPv6, så FixedString(16), fordi en IPv6-adresse er 128 bit, dvs. gemmer direkte i binært format.

Men hvad hvis du nogle gange har IPv4-adresser og nogle gange IPv6? Ja, du kan beholde begge dele. En kolonne for IPv4, en anden for IPv6. Selvfølgelig er der mulighed for at kortlægge IPv4 til IPv6. Dette vil også virke, men hvis du ofte har brug for en IPv4-adresse i dine anmodninger, så ville det være rart at sætte det i en separat kolonne.

* ClickHouse har nu separate IPv4, IPv6 datatyper, der gemmer data lige så effektivt som tal, men repræsenterer dem lige så bekvemt som strenge.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Det er også vigtigt at bemærke, at det er værd at forbehandle dataene på forhånd. For eksempel kommer nogle rå logfiler til dig. Og måske bør du ikke sætte dem ind i ClickHouse med det samme, selvom det er meget fristende at gøre ingenting, og alt vil fungere. Men det er stadig umagen værd at udføre de beregninger, der er mulige.

For eksempel browserversion. I en eller anden naboafdeling, som jeg ikke vil pege fingeren på, er browserversionen gemt der sådan her, altså som en streng: 12.3. Og så, for at lave en rapport, tager de denne streng og dividerer med et array og derefter med det første element i arrayet. Naturligvis går alt langsommere. Jeg spurgte, hvorfor de gør dette. De fortalte mig, at de ikke bryder sig om for tidlig optimering. Og jeg kan ikke lide for tidlig pessimisme.

Så i dette tilfælde ville det være mere korrekt at opdele i 4 kolonner. Vær ikke bange her, for dette er ClickHouse. ClickHouse er en kolonnedatabase. Og jo flere pæne små søjler, jo bedre. Der vil være 5 BrowserVersion, lav 5 kolonner. Det er fint.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Overvej nu, hvad du skal gøre, hvis du har mange meget lange strenge, meget lange arrays. De skal slet ikke opbevares i ClickHouse. I stedet kan du kun gemme nogle identifikatorer i ClickHouse. Og disse lange linjer skubber dem ind i et andet system.

For eksempel har en af ​​vores analysetjenester nogle hændelsesparametre. Og hvis der kommer mange parametre til begivenheder, gemmer vi simpelthen de første 512, der støder på. For 512 er ikke synd.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og hvis du ikke kan bestemme dig for dine datatyper, så kan du også skrive data til ClickHouse, men til en midlertidig tabel af typen Log, som er speciel for midlertidige data. Derefter kan du analysere, hvilken slags fordeling af værdier du har der, hvad der generelt er der og lave de korrekte typer.

* Nu har ClickHouse en datatype Lav kardinalitet som giver dig mulighed for effektivt at opbevare strenge med mindre indsats.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Overvej nu en anden interessant sag. Nogle gange fungerer tingene på en mærkelig måde for folk. Jeg går og ser det her. Og det ser umiddelbart ud til, at dette blev gjort af en meget erfaren, smart admin, som har stor erfaring med at opsætte MySQL version 3.23.

Her ser vi tusind tabeller, som hver indeholder resten af ​​at dividere det er ikke klart hvad med tusind.

I princippet respekterer jeg andres erfaringer, herunder at forstå hvilken slags lidelse denne oplevelse kan opnå.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og årsagerne er mere eller mindre klare. Det er gamle stereotyper, der kan have ophobet sig, mens de arbejdede med andre systemer. For eksempel har MyISAM-tabeller ikke en klynget primærnøgle. Og denne måde at dele data på kan være et desperat forsøg på at få den samme funktionalitet.

En anden grund er, at det er svært at foretage ændringer på store borde. Alt vil blive blokeret. Selvom i moderne versioner af MySQL, er dette problem ikke længere så alvorligt.

Eller for eksempel microsharding, men mere om det senere.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

I ClickHouse behøver du ikke at gøre dette, for for det første er den primære nøgle klynget, dataene er sorteret efter den primære nøgle.

Og nogle gange spørger folk mig: "Hvordan ændres ydeevnen af ​​rækkeforespørgsler i ClickHouse med størrelsen af ​​tabellen?". Jeg siger, at det ikke ændrer sig overhovedet. For eksempel har du en tabel med en milliard rækker, og du læser et interval på en million rækker. Alt er fint. Hvis bordet har en billion rækker, og du læser en million rækker, så vil det være næsten det samme.

Og for det andet er stykker som manuelle partitioner ikke påkrævet. Hvis du går ind og ser på, hvad der er på filsystemet, vil du se, at en tabel er en ret seriøs ting. Og der indeni er der noget som skillevægge. Det vil sige, at ClickHouse gør alt for dig, og du behøver ikke lide.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Ændre i ClickHouse er gratis, hvis ændre tilføj/slip kolonne.

Og du skal ikke lave små tabeller, for hvis du har 10 rækker eller 10 rækker i din tabel, så betyder det slet ikke noget. ClickHouse er et system, der optimerer gennemløbet, ikke latency, så det giver ingen mening at behandle 000 linjer.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Det er korrekt at bruge ét stort bord. Slip af med de gamle stereotyper, alt vil være godt.

Og som en bonus har vi i den seneste version mulighed for at lave en vilkårlig partitioneringsnøgle for at udføre alle mulige vedligeholdelsesoperationer på individuelle partitioner.

For eksempel har du brug for mange små tabeller, for eksempel, når der er behov for at behandle nogle mellemliggende data, modtager du bidder, og du skal udføre en transformation på dem, før du skriver til den endelige tabel. Til dette tilfælde er der en vidunderlig bordmotor - StripeLog. Det er ligesom TinyLog, kun bedre.

* Nu har ClickHouse mere tabelfunktion input.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Et andet anti-mønster er microsharding. For eksempel skal du sønderdele data, og du har 5 servere, og i morgen vil der være 6 servere. Og du tænker, hvordan man rebalancerer disse data. Og i stedet deler du ikke i 5 skår, men i 1 skår. Og så kortlægger du hver af disse mikroshards til en separat server. Og du vil lykkes på én server, for eksempel 000 ClickHouse, for eksempel. Separat instans på separate porte eller separate databaser.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Men i ClickHouse er dette ikke særlig godt. Fordi selv en forekomst af ClickHouse forsøger at bruge alle tilgængelige serverressourcer til at behandle én anmodning. Det vil sige, at du har en slags server og der for eksempel 56 processorkerner. Du kører en forespørgsel, der tager et sekund, og den vil bruge 56 kerner. Og hvis du placerede 200 ClickHouses på én server der, så viser det sig, at 10 tråde starter. Generelt vil alt være meget dårligt.

En anden grund er, at fordelingen af ​​arbejdet på tværs af disse instanser vil være ujævn. Nogle bliver færdige tidligere, nogle bliver færdige senere. Hvis alt dette skete i ét tilfælde, så ville ClickHouse selv have fundet ud af, hvordan man korrekt distribuerede dataene mellem strømmene.

Og en anden grund er, at du vil have interprocessorkommunikation over TCP. Dataene skal serialiseres, deserialiseres, og dette er et stort antal mikroshards. Det vil bare ikke virke.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Endnu et antimønster, selvom det næppe kan kaldes et antimønster. Dette er en stor mængde præ-aggregering.

Generelt er præaggregation god. Du havde en milliard rækker, du samlede det, og det blev til 1 rækker, og nu udføres forespørgslen med det samme. Alt er fantastisk. Sådan kan du gøre det. Og til dette har selv ClickHouse en speciel AggregatingMergeTree tabeltype, der foretager trinvis aggregering, efterhånden som data indsættes.

Men der er tidspunkter, hvor du tror, ​​at vi vil samle data som denne og aggregerede data som denne. Og i nogle tilstødende afdelinger vil jeg heller ikke sige hvilken, de bruger SummingMergeTree-tabeller til at summere op efter primærnøglen, og 20 kolonner bruges som primærnøgle. For en sikkerheds skyld ændrede jeg navnene på nogle kolonner for konspiration, men det er det hele.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og sådanne problemer opstår. For det første reduceres mængden af ​​data, du har, ikke for meget. For eksempel reduceres det med tre gange. Tre gange ville være en god pris for at have råd til den ubegrænsede analyse, der følger med at have ikke-aggregerede data. Hvis data er aggregeret, så får du kun elendige statistikker i stedet for analyser.

Og hvad er særligt godt? At disse personer fra den næste afdeling, går og beder nogle gange om at tilføje en kolonne mere til den primære nøgle. Det vil sige, at vi har samlet dataene sådan her, og nu vil vi gerne have lidt mere. Men der er ingen alter primær nøgle i ClickHouse. Derfor skal du skrive nogle scripts i C++. Og jeg kan ikke lide scripts, selvom de er i C++.

Og hvis du ser på, hvad ClickHouse blev skabt til, så er ikke-aggregerede data præcis det scenarie, som de blev født til. Hvis du bruger ClickHouse til ikke-aggregerede data, så gør du alt rigtigt. Hvis du samler, så er dette nogle gange tilgiveligt.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Et andet interessant tilfælde er anmodninger i en uendelig løkke. Jeg går nogle gange til en eller anden produktionsserver og ser på vis proceslisten der. Og hver gang opdager jeg, at der sker noget frygteligt.

For eksempel, her er dette. Det er umiddelbart klart, at det var muligt at gøre alt på én anmodning. Bare skriv url'en og listen der.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Hvorfor er mange sådanne anmodninger i en uendelig løkke dårlige? Hvis indekset ikke bruges, så vil du have mange passeringer over de samme data. Men hvis der for eksempel bruges et indeks, har du en primær nøgle på ru og du skriver url = noget der. Og du tror, ​​at en url vil blive læst punktvis fra tabellen, alt vil være i orden. Men egentlig nej. Fordi ClickHouse gør alt i batches.

Når han skal læse en række data, læser han lidt mere, fordi indekset i ClickHouse er sparsomt. Dette indeks tillader dig ikke at finde en individuel række i tabellen, kun en form for rækkevidde. Og dataene er komprimeret i blokke. For at læse en linje skal du tage hele blokken og komprimere den. Og hvis du kører en masse forespørgsler, vil du have en masse skæringspunkter af sådanne, og du vil få lavet en masse arbejde igen og igen.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og som en bonus kan du se, at du i ClickHouse ikke skal være bange for at overføre endda megabyte og endda hundredvis af megabyte til IN-sektionen. Jeg husker fra vores praksis, at hvis vi passerer en masse værdier i IN-sektionen i MySQL, for eksempel, sender vi 100 megabyte af nogle tal der, så spiser MySQL 10 gigabyte hukommelse, og der sker ikke andet med det, alt fungerer dårligt.

Og den anden ting er, at i ClickHouse, hvis dine forespørgsler bruger et indeks, så er det altid ikke langsommere end en fuld scanning, dvs. hvis du har brug for at læse næsten hele tabellen, vil den gå sekventielt og læse hele tabellen. Generelt vil han finde ud af det.

Der er dog nogle vanskeligheder. For eksempel at IN med en underforespørgsel ikke bruger indekset. Men det er vores problem, og vi skal løse det. Der er ikke noget grundlæggende her. Lad os gøre det*.

Og en anden interessant ting er, at hvis du har en meget lang forespørgsel, og distribueret anmodningsbehandling foregår, så vil denne meget lange anmodning blive sendt til hver server uden komprimering. For eksempel 100 megabyte og 500 servere. Og følgelig vil 50 gigabyte blive overført over netværket. Det vil blive overført, og derefter vil alt blive udført med succes.

* allerede bruger; alt blev ordnet som lovet.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Og det er ret almindeligt, hvis anmodningerne kommer fra API'et. For eksempel har du lavet en form for service. Og hvis nogen har brug for din service, så åbnede du API'en og bogstaveligt talt to dage senere ser du, at der sker noget uforståeligt. Alt er overbelastet, og der kommer nogle frygtelige anmodninger ind, som aldrig burde have været.

Og der er kun én løsning. Hvis du har åbnet API'en, bliver du nødt til at klippe den. For eksempel at indtaste nogle kvoter. Der er ingen andre rimelige muligheder. Ellers skriver de straks et manuskript, og der vil opstå problemer.

Og ClickHouse har en speciel funktion - dette er beregningen af ​​kvoter. Desuden kan du overføre din kvotenøgle. Dette er for eksempel et internt bruger-id. Og kvoter vil blive beregnet uafhængigt for hver af dem.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Nu en anden interessant ting. Dette er manuel replikering.

Jeg kender mange tilfælde, hvor folk, på trods af at ClickHouse har indbygget replikeringsunderstøttelse, replikerer ClickHouse manuelt.

Hvad er princippet? Du har en databehandlingspipeline. Og det fungerer uafhængigt, for eksempel i forskellige datacentre. Du skriver så at sige de samme data på samme måde til ClickHouse. Det er rigtigt, praksis viser, at dataene stadig vil afvige på grund af nogle ejendommeligheder i din kode. Det håber jeg i din.

Og med jævne mellemrum skal du stadig synkronisere manuelt. For eksempel udfører administratorer rsync én gang om måneden.

Det er faktisk meget nemmere at bruge ClickHouses indbyggede replikering. Men der kan være nogle kontraindikationer, for til dette skal du bruge ZooKeeper. Jeg vil ikke sige noget dårligt om ZooKeeper, i princippet virker systemet, men det sker, at folk ikke bruger det på grund af java-fobi, fordi ClickHouse er så godt et system skrevet i C++, som man kan bruge og alt vil være godt. Og ZooKeeper i java. Og på en eller anden måde vil du ikke engang kigge, men så kan du bruge manuel replikering.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

ClickHouse er et praktisk system. Det tager højde for dine behov. Hvis du har manuel replikering, kan du oprette en distribueret tabel, der ser på dine manuelle replikaer og foretager en failover mellem dem. Og der er endda en særlig mulighed, der giver dig mulighed for at undgå flops, selvom dine linjer er systematisk divergerende.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Yderligere kan der være problemer, hvis du bruger primitive bordmotorer. ClickHouse er sådan en konstruktør, der har en masse forskellige bordmotorer. For alle alvorlige tilfælde, som skrevet i dokumentationen, skal du bruge tabeller fra MergeTree-familien. Og alt det andet - det er sådan, for individuelle tilfælde eller for test.

I en MergeTree-tabel behøver du ikke have nogen dato og tid. Du kan stadig bruge. Hvis der ikke er nogen dato og tid, så skriv at standard er 2000. Det vil virke og vil ikke kræve ressourcer.

Og i den nye version af serveren kan du endda angive, at du har tilpasset partitionering uden en partitionsnøgle. Det bliver det samme.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

På den anden side kan primitive bordmotorer bruges. Udfyld for eksempel dataene én gang og se, drej og slet. Du kan bruge Log.

Eller lagring af små mængder til mellembehandling er StripeLog eller TinyLog.

Hukommelse kan bruges, hvis der er en lille mængde data og bare sno noget i RAM'en.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

ClickHouse bryder sig ikke så meget om renormaliserede data.

Her er et typisk eksempel. Dette er et stort antal webadresser. Du lægger dem i det tilstødende bord. Og så besluttede vi at gøre JOIN med dem, men dette vil som regel ikke fungere, fordi ClickHouse kun understøtter Hash JOIN. Hvis der ikke er nok RAM til en masse data at forbinde med, så virker JOIN ikke *.

Hvis dataene er af høj kardinalitet, så fortvivl ikke, gem dem i en denormaliseret form, URL'erne er direkte på plads i hovedtabellen.

* og nu har ClickHouse også en merge join, og den fungerer under forhold, hvor de mellemliggende data ikke passer ind i RAM'en. Men dette er ineffektivt, og anbefalingen forbliver gyldig.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Et par eksempler mere, men jeg tvivler allerede på, om de er anti-mønstre eller ej.

ClickHouse har en kendt ulempe. Han ved ikke, hvordan han opdaterer *. I en vis forstand er dette endda godt. Hvis du har nogle vigtige data, for eksempel regnskab, så vil ingen kunne sende dem, for der er ingen opdateringer.

* Understøttelse af opdatering og sletning i batch-tilstand er længe blevet tilføjet.

Men der er nogle specielle måder, hvorpå opdateringer kan vises i baggrunden. For eksempel tabeller af typen ReplaceMergeTree. De laver opdateringer under baggrundsfusioner. Du kan gennemtvinge dette med optimeretabel. Men gør det ikke for ofte, for det vil fuldstændigt overskrive partitionen.

Distribuerede JOINs i ClickHouse - dette håndteres også dårligt af forespørgselsplanlæggeren.

Dårligt, men nogle gange OK.

Bruger ClickHouse bare til at læse data tilbage med select*.

Jeg vil ikke anbefale at bruge ClickHouse til omfangsrige beregninger. Men det er ikke helt rigtigt, for vi er allerede på vej væk fra denne anbefaling. Og vi har for nylig tilføjet muligheden for at anvende maskinlæringsmodeller i ClickHouse - Catboost. Og det bekymrer mig, for jeg tænker: ”Sikke en rædsel. Dette er hvor mange cyklusser pr. byte det bliver! Det er en skam for mig at starte urcyklusser på bytes.

Effektiv brug af ClickHouse. Alexey Milovidov (Yandex)

Men vær ikke bange, installer ClickHouse, alt vil være fint. Om noget, så har vi et fællesskab. I øvrigt er fællesskabet dig. Og hvis du har problemer, kan du i hvert fald gå ind på vores chat, og jeg håber, at du vil blive hjulpet.

R'RѕRїSЂRѕSЃS <

Tak for rapporten! Hvor kan man klage over ClickHouse-krakket?

Du kan klage til mig personligt lige nu.

Jeg er for nylig begyndt at bruge ClickHouse. Slap straks cli-grænsefladen.

Hvilken score.

Lidt senere droppede jeg serveren med et lille udvalg.

Du har talent.

Jeg åbnede en GitHub-fejl, men den blev ignoreret.

Vi får se.

Aleksey narrede mig til at deltage i rapporten og lovede at fortælle mig, hvordan du klemmer dataene ind.

Meget simpelt.

Dette er hvad jeg indså i går. Flere detaljer.

Der er ingen forfærdelige tricks. Det er bare blok-for-blok-komprimering. Standarden er LZ4, du kan aktivere ZSTD*. Blokerer fra 64 kilobyte til 1 megabyte.

* der er også understøttelse af specialiserede komprimeringscodecs, der kan bruges i kæde med andre algoritmer.

Er blokkene kun rådata?

Ikke ligefrem rå. Der er arrays. Hvis du har en numerisk kolonne, er tallene i en række stablet i en matrix.

Det er klart.

Alexey, et eksempel der var med uniqExact over IP'er, altså det faktum at uniqExact tager længere tid at tælle med strenge end med tal, og så videre. Hvad hvis vi lægger en finte med ørerne og kaster i korrekturlæseøjeblikket? Det vil sige, du lader til at have sagt, at det ikke adskiller sig meget på disken. Hvis vi læser linjer fra disken, kaster, vil vi så have aggregater hurtigere eller ej? Eller vinder vi stadig marginalt her? Det forekommer mig, at du har testet det, men af ​​en eller anden grund ikke har angivet det i benchmark.

Jeg tror, ​​det vil være langsommere end ingen rollebesætning. I dette tilfælde skal IP-adressen parses fra strengen. I ClickHouse er parsing af IP-adresser naturligvis også optimeret. Vi prøvede meget, men samme sted har du tallene skrevet på ti tusinde. Meget ubehageligt. Til gengæld vil uniqExact-funktionen arbejde langsommere på strenge, ikke kun fordi der er tale om strenge, men også fordi der er valgt en anden specialisering af algoritmen. Strenge håndteres bare anderledes.

Og hvis vi tager en mere primitiv datatype? For eksempel skrev de det bruger-id ned, som vi har i, skrev det ned som en linje, og castede det så, bliver det sjovere eller ej?

Jeg tvivler. Jeg tror, ​​det bliver endnu mere trist, for når alt kommer til alt er parsing af tal et alvorligt problem. Det forekommer mig, at denne kollega endda havde en rapport om, hvor svært det er at parse tal i ti tusinde form, men måske ikke.

Alexey, mange tak for rapporten! Og mange tak for ClickHouse! Jeg har et spørgsmål om planerne. Er der en funktion i planerne for ufuldstændig opdatering af ordbøger?

altså delvis genstart?

Ja Ja. Ligesom muligheden for at sætte et MySQL-felt der, altså opdatere efter, så kun disse data indlæses, hvis ordbogen er meget stor.

Meget interessant funktion. Og, det forekommer mig, en person foreslog det i vores chat. Måske var det endda dig.

Det tror jeg ikke.

Fantastisk, nu viser det sig, at to anmodninger. Og du kan begynde at gøre det langsomt. Men jeg vil med det samme advare dig om, at denne funktion er ret enkel at implementere. Det vil sige, at du i teorien bare skal skrive versionsnummeret ind i tabellen og så skrive: versionen er mindre end sådan og sådan. Og det betyder, at vi højst sandsynligt vil tilbyde det til entusiaster. Er du entusiast?

Ja, men desværre ikke i C++.

Kan dine kolleger skrive i C++?

Jeg finder nogen.

Store*.

* funktionen blev tilføjet to måneder efter rapporten - den blev udviklet af forfatteren af ​​spørgsmålet og indsendt af hans Træk anmodning.

Tak!

Hej! Tak for rapporten! Du nævnte, at ClickHouse bruger alle de tilgængelige ressourcer meget godt. Og taleren ved siden af ​​Luxoft fortalte om sin beslutning for den russiske post. Han sagde, at de virkelig godt kunne lide ClickHouse, men de brugte det ikke i stedet for deres hovedkonkurrent, netop fordi det spiste hele processoren. Og de kunne ikke passe det ind i deres arkitektur, i deres ZooKeeper med havnearbejdere. Er det muligt på en eller anden måde at begrænse ClickHouse, så det ikke forbruger alt, hvad der bliver tilgængeligt for det?

Ja, det er muligt og meget nemt. Hvis du vil forbruge færre kerner, så skriv bare set max_threads = 1. Og det er alt, det vil udføre anmodningen i én kerne. Desuden kan du angive forskellige indstillinger for forskellige brugere. Så intet problem. Og fortæl dine kollegaer fra Luxoft, at det ikke er godt, at de ikke fandt denne indstilling i dokumentationen.

Alexey, hej! Jeg vil gerne stille dette spørgsmål. Det er ikke første gang, jeg hører, at mange mennesker begynder at bruge ClickHouse som et lager for logfiler. Ved betænkningen sagde du, at du ikke skulle gøre dette, det vil sige, at du ikke behøver at opbevare lange køer. Hvad synes du om det?

For det første er logs normalt ikke lange linjer. Der er selvfølgelig undtagelser. For eksempel kaster en eller anden tjeneste skrevet i java en undtagelse, den bliver logget. Og så i en endeløs løkke, og løber tør for harddiskplads. Løsningen er meget enkel. Hvis linjerne er meget lange, så klip dem. Hvad betyder lang? Titusvis af kilobytes er dårligt *.

* i nyere versioner af ClickHouse er "adaptive index granularity" aktiveret, hvilket fjerner problemet med at gemme lange strenge for det meste.

Er en kilobyte normalt?

Det er normalt.

Hej! Tak for rapporten! Jeg har allerede spurgt om dette i chatten, men jeg kan ikke huske, om jeg modtog et svar. Er der nogen plan om at udvide WITH-sektionen på en CTE-måde?

Ikke endnu. MED sektionen er noget useriøs. Det er ligesom en lille funktion for os.

Jeg forstår. Tak skal du have!

Tak for rapporten! Meget interessant! globalt spørgsmål. Er det planlagt at foretage en ændring af sletning af data, måske i form af en eller anden form for stubs?

Nødvendigvis. Dette er vores første opgave i vores kø. Vi overvejer nu aktivt, hvordan vi gør alt rigtigt. Og du bør begynde at trykke på tastaturet*.

* trykkede på knapperne på tastaturet og alt var gjort.

Vil det på en eller anden måde påvirke systemets ydeevne eller ej? Vil indsatsen være så hurtig, som den er nu?

Måske selve sletningerne, selve opdateringerne vil være meget tunge, men dette vil ikke påvirke ydeevnen af ​​udvalgte og indsatsens ydeevne på nogen måde.

Og endnu et lille spørgsmål. Ved præsentationen talte du om den primære nøgle. Derfor har vi partitionering, som er månedlig som standard, ikke? Og når vi sætter et datointerval, der passer ind i en måned, så læser vi kun denne partition, ikke?

Ja.

Et spørgsmål. Hvis vi ikke kan vælge nogen primærnøgle, er det så rigtigt at gøre det nøjagtigt ved "Dato"-feltet, så der i baggrunden sker en mindre omstrukturering af disse data, så de passer på en mere overskuelig måde? Hvis du ikke har intervalforespørgsler, og du ikke engang kan vælge en primær nøgle, er det så værd at sætte en dato i den primære nøgle?

Ja.

Måske giver det mening at indsætte et felt i den primære nøgle, som dataene bliver bedre komprimeret efter, hvis de sorteres efter dette felt. For eksempel bruger-id. Brugeren går for eksempel til det samme websted. I dette tilfælde skal du angive bruger-id og tid. Og så bliver dine data bedre komprimeret. Med hensyn til datoen, hvis du virkelig ikke har og aldrig har intervalforespørgsler på datoer, så kan du ikke sætte datoen i den primære nøgle.

Ok mange tak!

Kilde: www.habr.com

Tilføj en kommentar