Strings optimisation in ClickHouse. Yandex report

AnalĂŒĂŒtiline andmebaas ClickHouse töötleb erinevaid stringe, tarbides ressursse. SĂŒsteemi töö kiirus on pidevalt paranenud tĂ€nu uutele optimeerimistele. ClickHouse arendaja Nikolai Kočetov rÀÀgib andmetĂŒĂŒbist string, sealhulgas uuest tĂŒĂŒbist LowCardinality, ja selgitab, kuidas kiirendada tööd stringidega.

MĂ€ngi videot

— Alustame sellest, kuidas stringe salvestada.

Strings optimisation in ClickHouse. Yandex report

Meil on stringi andmetĂŒĂŒbid. String sobib vaikimisi vĂ€ga hĂ€sti ja seda tuleks kasutada peaaegu alati. Sellel on vĂ€ike overhead — 9 baiti ĂŒhe stringi kohta. Kui soovime, et stringide suurus oleks fikseeritud ja eelnevalt teada, on parem kasutada FixedString. Sellesse saab mÀÀrata vajaliku bitti arvu, see sobib IP-aadresside vĂ”i hash-funktsioonide jaoks.

Strings optimisation in ClickHouse. Yandex report

Loomulikult vĂ”ib mĂ”nikord miski pidurdada. Oletame, et teete pĂ€ringu tabelisse. ClickHouse loeb ĂŒsna suurt andmemassi, ĂŒtleme kiirusel 100 GB/s, samas stringe töödeldakse vĂ€he. Meil on kaks tabelit, mis sisaldavad peaaegu samu andmeid. Teisest tabelist loeb ClickHouse andmeid suurema kiirusel, kuid stringide arvu loetakse kolm korda vĂ€hem sekundis.

Strings optimisation in ClickHouse. Yandex report

Kui vaatame kokku surutud andmete suurust, selgub, et see on peaaegu sama. Tegelikult on tabelites salvestatud samad andmed — esimesed miljard numbrit — lihtsalt esimeses veerus on nad salvestatud UInt64 kujul, teises aga String. Sel pĂ”hjusel loetakse teises pĂ€ringus andmed ketas pikemalt ja neid dekompressitakse.

Strings optimisation in ClickHouse. Yandex report

Siin on teine nĂ€ide. Oletame, et on eelnevalt teadaolev hulk stringe, mis on piiratud konstantsusega 1000 vĂ”i 10 000 ning praktiliselt kunagi ei muutu. Selle juhtumi jaoks sobib meile andmetĂŒĂŒp Enum, ClickHouse'is on neid kaks — Enum8 ja Enum16. Enum'st hoidmise tĂ”ttu töötleme pĂ€ringuid kiiresti.

ClickHouse'is on kiirus tÀiustused GROUP BY, IN, DISTINCT ja optimeerimised teatud funktsioonide jaoks, nÀiteks fikseeritud stringiga vÔrdlemiseks. Loomulikult ei muundata stringi numbreid, vaid vastupidi, fikseeritud string muudetakse Enum vÀÀrtuseks. PÀrast seda vÔrreldakse kÔike kiiresti.

Kuid on ka miinuseid. Isegi kui me teame stringide tÀpset hulka, peab see mÔnikord tÀiendama. Kui uus string tuleb, peame tegema ALTER.

Strings optimisation in ClickHouse. Yandex report

ALTER Enumi ClickHouse on optimaalne. Me ei kirjutame andmeid ketasse, kuid ALTER vĂ”ib aeglustuda, kuna Enum struktuurid on salvestatud tabeli endasse. SeetĂ”ttu peame ootama lugemis Đ·Đ°ĐżŃ€ĐŸŃŃ‹ tabelist, nĂ€iteks.

KĂŒsimus on, kas seda saab paremaks teha? TĂ”enĂ€oliselt jah. Enum struktuuri vĂ”ib salvestada mitte tabeli skeemi, vaid ZooKeeperisse. Kuid vĂ”ivad tekkida sĂŒnkroniseerimisega seotud probleemid. NĂ€iteks vĂ”ib ĂŒks replik saada andmed, teine mitte, ja kui sellel on vana Enum, siis midagi lĂ€heb katki. (ClickHouse'is oleme peaaegu lĂ”petanud blokeerimata ALTER pĂ€ringud. Kui saame need tĂ€ielikult valmis, ei pea ootama lugemis Đ·Đ°ĐżŃ€ĐŸŃŃ‹.)

Strings optimisation in ClickHouse. Yandex report

ALTER Enumiga vaevlemise vÀltimiseks saab kasutada ClickHouse'i vÀliseid sÔnastikke. Tuletan meelde, et see on key-value andmestruktuur ClickHouse'is, mille abil saab andmeid vÀlisest allikast, nÀiteks MySQL tabelitest.

ClickHouse'i sÔnastikus hoiame mitmeid erinevaid stringe, ja tabelis on nende identifikaatorid numbrite kujul. Kui peame saama stringi, kutsume vÀlja dictGet funktsiooni ja töötame sellega. SeejÀrel ei pea me tegema ALTER't. Kui midagi on Enumisse lisada, siis sisestame selle samasse MySQL tabelisse.

Kuid siin kerkivad esile teised probleemid. Esiteks, ebamugav sĂŒntaks. Kui soovime saada stringi, peame kutsuma dictGet'i. Teiseks, teatud optimeerimiste puudumine. Konstantse stringiga vĂ”rdlemine sĂ”nastikes ei ole nii kiire.

Samuti vĂ”ivad esineda probleemid vĂ€rskendamisega. Oletame, et kĂŒsisime stringi vahekĂ€esĂ”nastikust, kuid see ei jĂ”udnud vahekĂ€esse. Siis peame ootama, kuni andmed vĂ€lisest allikast laaditakse.

Strings optimisation in ClickHouse. Yandex report

MĂ”lema meetodi ĂŒldine puudus on see, et hoiame kĂ”ik vĂ”tmed ĂŒhes kohas ja sĂŒnkroniseerime neid. Nii et miks mitte hoida sĂ”nastikke kohalikult? Pole sĂŒnkroniseerimist — pole probleeme. SĂ”nastikku saab hoida lokaalselt ketta tĂŒkis. See tĂ€hendab, et tegime Insert, salvestasime sĂ”nastiku. Kui töötame andmetega mĂ€lus, vĂ”ime salvestada sĂ”nastiku kas andmeplokki, veeru tĂŒki vĂ”i mĂ”nda muu vahemĂ€lu, et arvutusi kiirendada.

Stringide sÔnastikukood

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

Strings optimisation in ClickHouse. Yandex report

Slaidil on kaks veergu. Paremal pool hoitakse read standardses vormingus, String tĂŒĂŒpi. Selgelt on nĂ€ha, et need on teatud mobiiltelefonide mudelid. Vasakul on tĂ€pselt sama veerg, kuid LowCardinality tĂŒĂŒbis. See koosneb sĂ”nastikust, kus on palju erinevaid stringe (paremal pool olevate veergude stringid) ja positsioonide nimekirjast (rea numbrid).

Nende kahe struktuuri abil on vĂ”imalik taastada algne veerg. Samuti on olemas tagurpidi indeks — hash-tabel, mis aitab leida rea positsiooni sĂ”nastikus. See on vajalik teatud pĂ€ringute kiirendamiseks. NĂ€iteks, kui tahame 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, string vĂ”i nende Nullable variant.

Strings optimisation in ClickHouse. Yandex report

LowCardinality eripĂ€ra on see, et see vĂ”ib mĂ”nedes funktsioonides salvestuda. Slaidil on nĂ€ha pĂ€ringu nĂ€ide. Esimeses reas lĂ”in LowCardinality String tĂŒĂŒbiga veeru ja nimetasime selle S-ks. SeejĂ€rel kĂŒsisin selle nime — ClickHouse ĂŒtles, et see on LowCardinality String. KĂ”ik on Ă”ige.

Kolmas rida on peaaegu sama, aga oleme kutsunud vĂ€lja length funktsiooni. ClickHouse'is tagastab length funktsioon andmetĂŒĂŒbi UInt64. Kuid meil on saanud LowCardinality UInt64. Mis on selle mĂ”te?

Strings optimisation in ClickHouse. Yandex report

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

VÔivad olla ka teised optimeerimised, nÀiteks lihtsa vahemÀlu lisamine. Funktsiooni vÀÀrtuse arvutamisel on vÔimalik see meelde jÀtta ja luua sarnane, mitte arvutada uuesti.

Samuti vĂ”ib olla tehtud GROUP BY optimeerimine, sest meie sĂ”nastiku veerg on juba osaliselt agreggeeritud — hĂ€shefunktsiooni vÀÀrtuste arvutamine ja sobiva bucket'i leidmine jĂ€rgmise rea jaoks on kiirem. Samuti saab spetsialiseerida mĂ”ningaid agregaatfunktsioone, nĂ€iteks uniq, kuna sellesse saab saata ainult sĂ”nastiku, samas kui positsioonid jÀÀvad puutumatuks — siis töötab kĂ”ik kiiremini. Esimesed kaks optimeerimist oleme juba ClickHouse'i lisanud.

Strings optimisation in ClickHouse. Yandex report

EntÀ, jos luomme sarakkeen omalle datatyypillemme ja syötÀmme siihen paljon erilaisia huonoja merkkijonoja? Eikö meidÀn muistimme lopu? Ei, ClickHouse:ssa on tÀhÀn kaksi erityistÀ asetusta. EnsimmÀinen on low_cardinality_max_dictionary_size. TÀmÀ on sanakirjan maksimikoko, joka voidaan tallentaa levylle. Tietojen syöttÀminen tapahtuu nÀin: kun syötÀmme tietoja, saamme virran merkkijonoja, joista muodostamme suuren yhteisen sanakirjan. Jos sanakirja kasvaa suuremmaksi kuin asetuksen arvo, tallennamme nykyisen sanakirjan levylle ja muut merkkijonot johonkin 'sivulle', indeksien viereen. TÀten emme koskaan laske suurta sanakirjaa uudelleen emmekÀ kohtaa muistiongelmia.

Toinen asetus on nimeltÀÀn low_cardinality_use_single_dictionary_for_part. Kuvittele, ettÀ edellisessÀ schema:ssa, kun syötimme tietoja, sanakirjamme tÀyttyi ja tallensimme sen levylle. HerÀÀ kysymys, miksi emme nyt voisi luoda vielÀ yhtÀ samanlaista sanakirjaa?

Kun se tÀyttyy, tallennamme sen taas levylle ja aloitamme kolmannen sanakirjan muodostamisen. TÀmÀ asetus estÀÀ juuri tÀmÀn mahdollisuuden oletusarvoisesti.

Itse asiassa monet sanakirjat voivat olla hyödyllisiÀ, jos haluamme syöttÀÀ tietyn mÀÀrÀn merkkijonoja, mutta vahingossa syötimme 'roskaa'. Sanotaan, ettÀ ensin syötimme huonoja merkkijonoja ja sitten hyviÀ. TÀllöin sanakirja jakautuu moneen pieneen sanakirjaan. Osa niistÀ sisÀltÀÀ 'roskaa', mutta viimeiset sisÀltÀvÀt hyviÀ merkkijonoja. Ja jos luemme vain viimeistÀ fragmenttia, kaikki toimii nopeasti.

Strings optimisation in ClickHouse. Yandex report

Ennen kuin puhumme LowCardinality:n eduista, sanon heti, ettĂ€ tuskin saamme aikaan vĂ€hennystĂ€ tiedoissa levyllĂ€ (vaikka se voi tapahtua), koska ClickHouse pakkaa tiedot. On oletusvaihtoehto — LZ4. Voimme myös pakata ZSTD:n avulla. Mutta molemmat algoritmit toteuttavat jo sanakirjapakkausta, joten ulkoinen sanakirjamme ClickHouse:ssa ei ole kovin hyödyllinen.

Kuna ei taha olla sĂ”naline, tĂ”in mĂ”ned andmed metrikast — String, LowCardinality(String) ja Enum — ning salvestasin need erinevatesse andmetĂŒĂŒpidesse. Saime kolm veergu, kus on kirjas miljard rida. Esimeses veerus, CodePage, on kokku 62 vÀÀrtust. On nĂ€ha, et LowCardinality(String) komprimeeris need paremini. Stringi tulemus on veidi kehvem, kuid see on tĂ”enĂ€oliselt tingitud lĂŒhikestest ridadest; me salvestame nende pikkusi ja need vĂ”tavad palju ruumi, seega komprimeeruvad halvasti.

Kui vĂ”tta PhoneModel, on neid 48 tuhat — juba rohkem, ja erinevused Stringi ja LowCardinality(String) vahel on peaaegu olematud. URL-i puhul sÀÀstsime ka vaid 2 GB — arvan, et sellele ei tasu loota.

Töökiirus

Strings optimisation in ClickHouse. Yandex report
Link slaidilt

NĂŒĂŒd hindame töökiirus. Selle hindamiseks kasutasin taksireiside andmestikku New Yorgis. See saadaval on GitHubis. Andmestikus on veidi ĂŒle miljardi reisi. Seal on kajastatud asukoht, sĂ”iduki algus- ja lĂ”ppaeg, makseviis, reisijate arv ja isegi taksotĂŒĂŒp — roheline, kollane ja Uber.

Strings optimisation in ClickHouse. Yandex report

Tegin esimese pĂ€ringu ĂŒsna lihtsaks — kĂŒsisin, kust tellitakse kĂ”ige rohkem taksosid. Selleks oli vaja vĂ”tta asukoht, kust taksot telliti, teha selle pĂ”hjal GROUP BY ja lugeda count-funktsiooni. Siin on ClickHouse midagi vĂ€lja andnud.

Strings optimisation in ClickHouse. Yandex report

KĂŒsimise töötlemise kiirusest mÔÔtmiseks lĂ”in kolm tabelit, kus on samad andmed, kuid kasutasin meie algasukoha jaoks kolme erinevat andmetĂŒĂŒpi — String, LowCardinality ja Enum. LowCardinality ja Enum osutusid viis korda kiiremateks kui String. Enum on kiire, kuna töötab numbritega. LowCardinality on kiire, kuna realizatsioon on optimeeritud GROUP BY jaoks.

Strings optimisation in ClickHouse. Yandex report

Komplektime pĂ€ringut veelgi keerulisemaks — kĂŒsime, kus asub New Yorgi kĂ”ige populaarsem park. Taaskord mÔÔdame seda selle pĂ”hjal, kust taksosid kĂ”ige sagedamini tellitakse, kuid filtrime ainult need asukohad, kus on sĂ”na "park". Samuti lisame funktsiooni like.

Strings optimisation in ClickHouse. Yandex report

Vaadates aega — nĂ€eme, et Enum hakkas ootamatult aeglustuma. TĂ”si, see töötab isegi aeglasemalt kui tavaline andmetĂŒĂŒp String. See juhtub seetĂ”ttu, et funktsioon like ei ole Enum'i jaoks ĂŒldse optimeeritud. Peame muundama meie Enum'i stringid tavalisteks stringideks — teeme rohkem tööd. LowCardinality(String) ei ole ka vaikimisi optimeeritud, kuid seal töötab like sĂ”naraamatute kallal, seetĂ”ttu kiireneb pĂ€ring vĂ”rreldes Stringiga.

Enumiga töötamisel on ĂŒks suurem probleem. Kui me soovime seda optimeerida, peame seda tegema igas koodikohtas. Oletame, et oleme kirjutanud uue funktsiooni — peame kindlasti vĂ€lja mĂ”tlema Enum'i optimeerimise. Madala kardinaalsusega on kĂ”ik vaikimisi optimeeritud.

Strings optimisation in ClickHouse. Yandex report

Vaatame viimast pĂ€ringut, mis on rohkem kunstlik. Me lihtsalt arvutame meie asukoha hash-funktsiooni. Hash-funktsioon on ĂŒsna aeglane pĂ€ring, see vĂ”tab kaua aega, seega kĂ”ik aeglustub kolm korda.

Strings optimisation in ClickHouse. Yandex report

Madala kardinaalsusega töötab endiselt kiiremini, kuigi siin ei ole filtreerimist. See juhtub seetĂ”ttu, et meie funktsioonid töötavad ainult sĂ”nastiku ĂŒle. Hash-funktsiooni arvutamise funktsioonil on ĂŒks argument — see saab töödelda vĂ€hem andmeid ja vĂ”ib samuti tagastada madala kardinaalsuse.

Strings optimisation in ClickHouse. Yandex report

Meie globaalne plaan on saavutada kiirus, mis pole madalam kui String igas olukorras, ja sÀilitada kiirus. Ja vÔib-olla asendame mingi pÀev String'i madala kardinaalsusega, vÀrskendate ClickHouse'i ja kÔik töötab natuke kiiremini.

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster