Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

AnalĂŒĂŒtiline andmebaas ClickHouse töötleb mitmesuguseid stringe, tarbides ressursse. SĂŒsteemi töö kiirendamiseks lisatakse pidevalt uusi optimeerimisi. ClickHouse arendaja Nikolai Kočetov rÀÀgib stringi andmetĂŒĂŒbist, sealhulgas uuest tĂŒĂŒbist LowCardinality, ja selgitab, kuidas stringide töötlemist kiirendada.

Vaata videot

— Esiteks laskem mĂ”ista, kuidas stringe salvestada.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Meil on stringi andmetĂŒĂŒbid. String on vaikimisi hĂ€sti sobiv, seda tasub kasutada peaaegu alati. Sellel on vĂ€ike Overhead — 9 baiti ĂŒhe stringi kohta. Kui soovime, et stringide suurus oleks fikseeritud ja eelnevalt teada, tasub kasutada FixedStringi. Sellesse saab mÀÀrata soovitud byte'ide arvu, see on mugav nĂ€iteks IP-aadresside vĂ”i hash-funktsioonide jaoks.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Muidugi aeg-ajalt midagi peatud. Oletame, et teete pĂ€ringut tabelisse. ClickHouse loeb ĂŒsna suurt hulka andmeid, ĂŒtleme kiirusel 100 GB/s, samas töötleb see vĂ€he stringe. Meil on kaks tabelit, mis hoiavad peaaegu sama andmestikku. Teisest tabelist loeb ClickHouse andmeid suurema kiirusena, kuid stringe loetakse kolmandiku vĂ”rra vĂ€hem sekundis.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Kui vaatame tihendatud andmete suurust, siis selgub, et see on peaaegu sama. Tegelikult on tabelites kirjas samad andmed — esimene miljard numbrit — ainult et esimeses veerus on need kirjutatud vormis UInt64, samas kui teises veerus on need Stringina. SeetĂ”ttu loetakse teise pĂ€ringu andmed kettalt kauem ja need dekomprimeeritakse.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Siin on veel ĂŒks nĂ€ide. Oletame, et meil on ette teada kindel rida stringe, mis on piiratud konstantsiga 1000 vĂ”i 10 000 ja peaaegu kunagi ei muutu. Selleks sobib meile andmetĂŒĂŒp Enum, ClickHouse'is on neid kaks — Enum8 ja Enum16. Enum'i salvestamine kiirendab pĂ€ringute töötlemist.

ClickHouse'is on kiirus tÀiendavatele GROUP BY, IN, DISTINCT-le ja optimeerimine teatud funktsioonide jaoks, nÀiteks konstantse stringiga vÔrreldes. Loomulikult ei muundata numbreid stringiks, vaid vastupidi, konstantne string muudetakse Enum'i vÀÀrtuseks. PÀrast seda vÔrdlemine toimub kiiresti.

Aga on ka miinuseid. Isegi kui me teame tĂ€pselt rida stringe, peab see mĂ”nikord tĂ€iendama. Kui uus string saabus — peame tegema ALTER.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

ALTER Enum ClickHouse'is on optimaalselt rakendatud. Me ei kirjuta andmeid kettale ĂŒmber, kuid ALTER vĂ”ib veidi venida, kuna Enum'i struktuurid salvestatakse tabeli enda skeemi. SeetĂ”ttu peame ootama lugemisotsuseid tabelist, nĂ€iteks.

Tekkib kĂŒsimus, kas on vĂ”imalik paremini teha? TĂ”enĂ€oliselt jah. Enum'i struktuuri vĂ”iks sĂ€ilitada mitte tabeli skeemis, vaid ZooKeeper'is. Siiski vĂ”ivad tekkida sĂŒnkroonimisprobleemid. NĂ€iteks, kui ĂŒks replika sai andmed, siis teine ei saanud, ja kui tal on vana Enum, siis midagi puruneb. (ClickHouse'is oleme peaaegu lĂ”petanud mitteblokeerivad ALTER-pĂ€ringud. Kui me need tĂ€ielikult lĂ”petame, ei ole enam vaja oodata lugemispĂ€ringuid.)

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Kui te ei soovi ALTER Enum'iga tegelda, vÔite kasutada ClickHouse'i vÀliseid sÔnastikke. Kordan, et see on key-value andmestruktuur ClickHouse'is, mille kaudu saab andmeid vÀliseidest allikatest, nÀiteks MySQL tabelitest.

ClickHouse'i sÔnastikus hoiame me palju erinevaid ridu, ja tabelis on nende identifikaatorid numbrite kujul. Kui me peame saama rea, kutsume vÀlja funktsiooni dictGet ja töötame sellega. PÀrast seda ei tohiks me teha ALTERi. Kui soovime Enumisse midagi lisada, sisestame selle samasse MySQL tabelisse.

Kuid siin tekivad teised probleemid. Esiteks, ebamugav sĂŒntaks. Kui soovime saada rida, peame kutsuma vĂ€lja dictGet. Teiseks, teatud optimeerimiste puudumine. Muudatust konstandistringiga sĂ”nastike jaoks ei saa teha sama kiiresti.

Samuti vĂ”ivad tekkida probleemid vĂ€rskendamisega. Oletame, et kĂŒsisime rida vahemĂ€lu sĂ”nastikus, aga see ei jĂ”udnud vahemĂ€llu. Siis peame ootama, kuni andmed vĂ€lisest allikast laaditakse.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

MĂ”lema meetodi ĂŒldine puudus on see, et hoiame kĂ”ik vĂ”tmed ĂŒhes kohas ja sĂŒnkroniseerime need. Miks mitte hoida sĂ”nastikke kohalikult? Pole sĂŒnkroniseerimist — pole probleeme. SĂ”nastikku saab hoida kohalikult plaadi tĂŒkkide kaupa. Seega teeme Inserti, kirjutame sĂ”nastiku. Kui töötame andmetega mĂ€lu sees, saame sĂ”nastiku kirja panna kas andmeplokki, veerutĂŒkkidesse vĂ”i mĂ”nda vahemĂ€lu, et arvutusi kiirendada.

SÔnaraamatute kodeerimine

Nii jĂ”udsime ClickHouse'is uue andmetĂŒĂŒbi – LowCardinality – loomiseni. See on andmete salvestamise formaat: kuidas need kirjutatakse kettale ja kuidas neid loetakse, kuidas need on mĂ€lus esindatud ja nende töötlemise skeem.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Slaidil on kaks veergu. Paremal on read salvestatud tavalisel viisil, tĂŒĂŒbis String. NĂ€ha on, et need on mingid mobiiltelefonide mudelid. Vasakul on tĂ€pselt sama veerg, ainult tĂŒĂŒbiga LowCardinality. See koosneb sĂ”nastikust, kus on palju erinevaid reasse (parema veeru read), ja positsioonide loendist (rea numbrid).

Nende kahe struktuuriga saab taastada algse veeru. Samuti on olemas vastupidine indeks – rippmenĂŒĂŒ, mis aitab leida rea positsiooni sĂ”nastikus. Seda vajatakse mĂ”nede pĂ€ringute kiirendamiseks. NĂ€iteks, kui soovime vĂ”rrelda, otsida rida meie veerus vĂ”i neid omavahel ĂŒhendada.

LowCardinality on parameetriline andmetĂŒĂŒp. See vĂ”ib olla kas number, midagi, mis salvestatakse numbrina, rida vĂ”i nende Nullable variant.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

LowCardinality'i eelis on see, et see vĂ”ib salvestuda teatud funktsioonide jaoks. Slidil on nĂ€ide pĂ€ringust. Esimeses reas lĂ”in LowCardinality tĂŒĂŒpi veeru String'ist, nimetasime selle S-ks. Siis kĂŒsisin selle nime — ClickHouse ĂŒtles, et see on LowCardinality String'ist. KĂ”ik on korras.

Kolmas rida on peaaegu sama, lihtsalt kutsusime vĂ€lja length funktsiooni. ClickHouse'is tagastab length funktsioon andmetĂŒĂŒbi UInt64. Kuid nĂŒĂŒd saime LowCardinality UInt64. Mis selle tĂ€hendus on?

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

SĂ”nastikus olid mobiiltelefonide nimed, rakendasime length funktsiooni. NĂŒĂŒd on meil sarnane sĂ”naraamat, mis koosneb ainult numbritest — need on stringide pikkused. Positsioonide veerg ei ole muutunud. LĂ”ppkokkuvĂ”ttes töötasime vĂ€hemate andmetega, sÀÀstsime pĂ€ringu ajas.

VÔivad olla ka teised optimeerimised, nÀiteks lihtsa vahemÀlu lisamine. Funktsiooni vÀÀrtuse arvutamisel saab selle mÀletada ja koostada sama, mitte arvutada uuesti.

Samuti saab GROUP BY optimeerida, kuna meie sĂ”nastiku veerg on juba osaliselt koondatud — hĂ€shtfunktsioonide vÀÀrtusi saab kiiremini arvutada ja umbkaudu leida bucket, kuhu jĂ€rgmine rida paigutada. Samuti on vĂ”imalik spetsialiseerida mĂ”ningaid agregaatfunktsioone, nĂ€iteks uniq, sest sellesse saab saata ainult sĂ”nastiku, jĂ€ttes positsioonid puutumatuks — nii töötab kĂ”ik kiiremini. Need kaks optimeerimist on me juba ClickHouse'i lisanud.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Ent mis saab siis, kui loome veeru meie andmetĂŒĂŒbiga ja sisestame sellesse palju halbu erinevaid ridu? Kas meie mĂ€lu ei ĂŒletata? Ei, ClickHouse'is on selleks kaks spetsiaalset seadet. Esimene on low_cardinality_max_dictionary_size. See on maksimaalne sĂ”nastiku suurus, mis saab kettale salvestada. Sisestamine toimub jĂ€rgmiselt: kui me andmeid sisestame, tuleb meile ridade voog, millest me kujundame suure ĂŒhis sĂ”nastiku. Kui sĂ”nastik muutub suuremaks kui seadistuse vÀÀrtus, salvestame praeguse sĂ”nastiku kettale, samal ajal kui ĂŒlejÀÀnud read — jÀÀvad kuhugi „kĂ”rvale“, indeksite kĂ”rvale. LĂ”pptulemusena ei arvuta me kunagi suurt sĂ”nastikku uuesti ja ei saa mĂ€lu probleemidega kokku.

Teine seadistus kannab nime low_cardinality_use_single_dictionary_for_part. Kujutage ette, et eelmisel skeemil, kui me andmeid sisestasime, meie sĂ”nastik ĂŒletas piiri ja me salvestasime selle kettale. KĂŒsimus on, miks mitte luua nĂŒĂŒd veel ĂŒks tĂ€iesti sama sĂ”nastik?

Kui see jĂ€lle ĂŒle voolab, salvestame selle uuesti kettale ja hakkame kolmandat looma. See seadistus lĂŒlitab selle vĂ”imaluse vaikeolekus vĂ€lja.

Tegelikult vĂ”ivad palju sĂ”nastikke olla kasulikud, kui me tahame sisestada mingit rida, kuid juhuslikult sisestame "prĂŒgi". Ütleme, et kĂ”igepealt sisestasime halvad read ja siis head. Siis jaguneb sĂ”nastik paljudeks vĂ€ikesteks sĂ”nastikeks. Osad neist sisaldavad "prĂŒgi", kuid viimased sisaldavad hĂ€id ridu. Ja kui me lugedes, ĂŒtleme, ainult viimast granulaati, töötab kĂ”ik ka kiiresti.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Enne kui rÀÀgime LowCardinality eeliste kohta, ĂŒtlen kohe, et tĂ”enĂ€oliselt ei suuda me andmeid ketast vĂ€hendada (kuigi see vĂ”ib juhtuda), sest ClickHouse tihendab andmeid. Vaikimisi on olemas variant — LZ4. Samuti saab kasutada ZSTD tihendust. Kuid mĂ”lemad algoritmid rakendavad juba sĂ”naraamiga tihendust, seega meie vĂ€line sĂ”naraamat ClickHouse'is ei aita eriti.

Kuna ma ei soovi olla pĂ”hjendamatu, vĂ”tsin mĂ”ned andmed metrikast — String, LowCardinality(String) ja Enum — ning salvestasin need erinevatesse andmetĂŒĂŒpidesse. Saime kolm veergu, kus on ĂŒks miljard rida. Esimeses veerus, CodePage, on kokku 62 vÀÀrtust. Ja on nĂ€ha, et LowCardinality(String) tihendas neid paremini. String on veidi halvem, kuid see on tĂ”enĂ€oliselt tingitud sellest, et read on lĂŒhikesed, me hoiame nende pikkusi ja need vĂ”tavad palju ruumi, halvemini tihendatud.

Kui vĂ”tta PhoneModel, siis neid on 48 tuhat — juba rohkem, ja erinevused String ja LowCardinality(String) vahel on peaaegu olematud. URL-ide puhul sÀÀstsime samuti vaid 2 GB — arvan, et sellele ei tohiks toetuda.

Töökiirus hindamine

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja
Viide slaidilt

NĂŒĂŒd hindame töökiirust. Selle hindamiseks kasutasin New Yorgi taksireiside andmestikku. saadaval GitHubis. Seal on veidi ĂŒle miljardi sĂ”idu. Seal on nĂ€idatud asukoht, sĂ”idu algus- ja lĂ”ppaeg, makseviis, reisijate arv ja isegi takso tĂŒĂŒp — roheline, kollane ja Uber.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Esimene pĂ€ring, mille ma tegin, oli ĂŒsna lihtne — kĂŒsisin, kust sagedamini taksot tellitakse. Selleks tuleb vĂ”tta asukoht, kust takso telliti, kasutada GROUP BY-d ja lugeda funktsiooni count. ClickHouse annab midagi.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

KĂŒsimuse töötlemise kiirusest mÔÔtmiseks lĂ”in kolm tabelit ĂŒhesuguste andmetega, kuid kasutasin meie algasukoha jaoks kolme erinevat andmetĂŒĂŒpi — String, LowCardinality ja Enum. LowCardinality ja Enum osutusid viiekordselt kiiremaks kui String. Enum on kiirem, sest töötab numbritega. LowCardinality on samuti efektiivne, kuna on rakendatud GROUP BY optimeerimist.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Teeme pĂ€ringu veel keerulisemaks — kĂŒsime, kus asub kĂ”ige populaarsem park New Yorgis. JĂ€llegi mÔÔdame seda selle jĂ€rgi, kus sagedamini taksosid tellitakse, kuid filtreerime vĂ€lja ainult need asukohad, kus on sĂ”na „park“. Lisame ka funktsiooni like.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Vaadates aega, nĂ€eme, et Enum on Ă€kitselt hakanud aeglustuma. Tegelikult töötab see isegi aeglasemalt kui tavaline String andmetĂŒĂŒp. See juhtub, kuna funktsioon like ei ole Enum-i jaoks ĂŒldse optimeeritud. Peame meie enum stringid tavalistesse stringidesse konverteerima — see tĂ€hendab, et teeme rohkem tööd. LowCardinality(String) ei ole ka vaikimisi optimeeritud, kuid seal toimub like sĂ”nastiku peal, seega kiireneb pĂ€ring vĂ”rreldes Stringiga.

Enumiga töötamisel on veelgi globaalne probleem. Kui me tahame seda optimeerida, peame seda tegema igas koodikohas. Oletame, et oleme kirjutanud uue funktsiooni — peame kindlasti leidma optimeerimise Enum-i jaoks. LowCardinality on aga kĂ”ik vaikimisi optimeeritud.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Vaadakem viimast pĂ€ringut, mis on kunstlikum. Arvutame lihtsalt meie asukoha hash-funktsiooni. Hash-funktsioon — ĂŒsna aeglane pĂ€ring, seda arvutatakse pikka aega, seega kĂ”ik aeglustub umbes kolm korda.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

LowCardinality töötab endiselt kiiremini, kuigi siin ei ole filtreerimist. See juhtub, kuna meie funktsioonid töötavad ainult sĂ”nastiku peal. Hash-funktsioonil on ĂŒks argument — see saab töödelda vĂ€hem andmeid ja samuti vĂ”ib tagastada LowCardinality.

Stringide optimeerimine ClickHouse'is. Yandexi ettekandja

Meie globaalne plaan on saavutada töökiirus, mis ei oleks madalam kui String igas olukorras, ja sÀilitada kiirus. Ja vÔib-olla asendame kunagi Stringi LowCardinality'ga, te uuendate ClickHouse'i ja teil hakkab kÔik natuke kiiremini toimima.

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster