Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Budući da je ClickHouse specijaliziran sistem, važno je uzeti u obzir posebnosti njegove arhitekture kada ga koristite. U ovom izvještaju, Alexey će govoriti o primjerima tipičnih grešaka pri korištenju ClickHouse-a, koje mogu dovesti do neefikasnog rada. Koristeći praktične primjere, pokazat ćemo kako izbor jedne ili druge sheme obrade podataka može promijeniti performanse po redovima veličine.

Zdravo svima! Moje ime je Alexey, pravim ClickHouse.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Prvo, žurim da vas odmah zadovoljim, neću vam danas reći šta je ClickHouse. Da budem iskren, umoran sam od toga. Svaki put ti kažem šta je. I vjerovatno svi već znaju.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Umjesto toga, reći ću vam koji je mogući rake, odnosno kako se ClickHouse može zloupotrebiti. U stvari, ne treba da se plašite, jer mi razvijamo ClickHouse kao sistem koji je jednostavan, praktičan i radi iz kutije. Instalirao sve, nema problema.

Ali ipak, treba imati na umu da je ovaj sistem specijalizovan i lako možete naići na neobičan slučaj upotrebe koji će ovaj sistem izbaciti iz zone komfora.

Dakle, šta su grabulje? U osnovi ću govoriti o očiglednim stvarima. Svima je sve očigledno, svi sve razumeju i može im biti drago što su tako pametni, a ko ne razume naučiće nešto novo.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Prvi najjednostavniji primjer, koji se, nažalost, često javlja, je veliki broj umetaka sa malim serijama, odnosno veliki broj malih umetaka.

Ako uzmemo u obzir kako ClickHouse izvodi umetanje, onda možete poslati najmanje terabajt podataka u jednom zahtjevu. Nije problem.

I da vidimo kakva će biti tipična izvedba. Na primjer, imamo tabelu s podacima Yandex.Metrics. Hits. 105 neke kolone. 700 bajtova nekomprimovano. I ubacićemo na dobar način serije od milion redova.

Ubacujemo u MergeTree tabelu, dobija se pola miliona redova u sekundi. Odlično. U repliciranoj tabeli - to će biti malo manje, oko 400 redova u sekundi.

A ako uključite umetanje kvoruma, dobijate malo manje, ali i dalje pristojne performanse, 250 puta u sekundi. Umetanje kvoruma je nedokumentovana funkcija u ClickHouse*.

* od 2020. već dokumentovano.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Šta se dešava ako to uradite pogrešno? Ubacimo jedan red u MergeTree tabelu i dobijemo 59 redova u sekundi. Ovo je 10 puta sporije. U ReplicatedMergeTree - 000 redova u sekundi. A ako se kvorum uključi, onda se dobijaju 6 reda u sekundi. Po mom mišljenju, ovo je neka vrsta krajnje gluposti. Kako možeš tako usporiti? Čak i na mojoj majici piše da ClickHouse ne treba da usporava. Ali ipak se to ponekad dešava.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Zapravo, to je naš nedostatak. Mogli smo to učiniti sasvim dobro, ali nismo. A nismo, jer našem scenariju to nije trebalo. Već smo imali serije. Upravo smo primili serije na ulazu i bez problema. Uključite ga i sve radi kako treba. Ali, naravno, mogući su razni scenariji. Na primjer, kada imate gomilu servera na kojima se generiraju podaci. I ne ubacuju podatke tako često, ali i dalje dobijaju česte umetke. I ovo morate nekako izbjeći.

Sa tehničke tačke gledišta, suština je da kada umetnete u ClickHouse, podaci ne ulaze ni u jednu memtable. Nemamo čak ni pravu MergeTree strukturu dnevnika, već samo MergeTree, jer ne postoji ni log ni memTable. Samo odmah upisujemo podatke u sistem datoteka, već razložene na kolone. A ako imate 100 stupaca, tada će više od 200 datoteka trebati biti upisano u poseban direktorij. Sve ovo je veoma glomazno.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

I postavlja se pitanje: "Kako to učiniti kako treba?" Ako je takva situacija, još uvijek morate nekako napisati podatke u ClickHouse.

Metoda 1. Ovo je najlakši način. Koristite neku vrstu distribuiranog reda čekanja. Na primjer, Kafka. Samo izvadite podatke iz Kafke, mi ih skupljamo jednom u sekundi. I sve će biti u redu, snimate, sve radi kako treba.

Nedostaci su što je Kafka još jedan glomazan distribuirani sistem. Također razumijem ako već imate Kafku u svom društvu. Dobro je, zgodno je. Ali ako ga nema, onda biste trebali razmisliti tri puta prije nego uvučete drugi distribuirani sistem u svoj projekat. I zato je vrijedno razmotriti alternative.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Metoda 2. Evo takve alternative stare škole i istovremeno vrlo jednostavna. Da li imate neku vrstu servera koji generiše vaše logove. I samo zapisuje vaše dnevnike u datoteku. I jednom u sekundi, na primjer, preimenujemo ovu datoteku, otvaramo novu. I zasebna skripta ili cron ili neki demon uzima najstariji fajl i zapisuje ga u ClickHouse. Ako zapise pišete jednom u sekundi, onda će sve biti u redu.

Ali nedostatak ove metode je da ako je vaš server na kojem se generiraju zapisnici negdje nestao, onda će i podaci nestati.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Metoda 3. Postoji još jedan interesantan način, koji uopće nema privremenih datoteka. Na primjer, imate neku vrstu reklamnog spinnera ili nekog drugog zanimljivog demona koji generiše podatke. I možete akumulirati gomilu podataka direktno u RAM-u, u baferu. A kada prođe dovoljno vremena, stavite ovaj bafer na stranu, kreirate novi i ubacite ono što se već nakupilo u ClickHouse u posebnu nit.

S druge strane, podaci također nestaju sa kill -9. Ako vaš server padne, izgubit ćete ove podatke. I još jedan problem je da ako ne možete pisati u bazu podataka, onda će se vaši podaci akumulirati u RAM-u. I ili ponestane RAM memorije, ili jednostavno izgubite podatke.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Metoda 4. Još jedan zanimljiv način. Imate li neki serverski proces. I on može poslati podatke u ClickHouse odjednom, ali to radi u jednoj konekciji. Na primjer, poslao sam http zahtjev sa transfer-encodingom: chunked with insert. I generira komade ne previše rijetko, možete poslati svaki red, iako će biti dodatnih troškova za uokvirivanje ovih podataka.

Međutim, u ovom slučaju, podaci će odmah biti poslani ClickHouseu. I sam ClickHouse će ih baferovati.

Ali postoje i problemi. Sada ćete izgubiti podatke, uključujući kada je vaš proces ubijen i ako je proces ClickHouse ubijen, jer će to biti nepotpuni umetak. A u ClickHouse-u umetci su atomični do nekog određenog praga u veličini redova. U principu, ovo je zanimljiv način. Takođe se može koristiti.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Metoda 5. Evo još jednog zanimljivog načina. Ovo je neka vrsta servera koji je razvila zajednica za prikupljanje podataka. Nisam ga lično pogledao, tako da ne mogu ništa da garantujem. Međutim, ne postoje garancije za sam ClickHouse. Ovo je također open source, ali s druge strane, mogli biste se naviknuti na neki standard kvalitete koji mi pokušavamo pružiti. Ali za ovu stvar - ne znam, idi na GitHub, pogledaj kod. Možda su nešto dobro napisali.

*od 2020. takođe treba dodati u razmatranje Kitten House.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Metoda 6. Drugi način je korištenje Tabela bafera. Prednost ove metode je što je vrlo lako početi koristiti. Kreirajte tabelu bafera i umetnite je u nju.

Ali nedostatak je što problem nije u potpunosti riješen. Ako po stopi tipa MergeTree morate grupirati podatke za jednu seriju u sekundi, onda po stopi u tabeli međuspremnika, morate grupirati najmanje do nekoliko hiljada u sekundi. Ako ih ima više od 10 u sekundi, i dalje će biti loše. A ako ubacite u serijama, onda ste vidjeli da se tamo dobija sto hiljada linija u sekundi. A to je već na prilično teškim podacima.

Takođe, tabele bafera nemaju dnevnik. A ako nešto nije u redu sa vašim serverom, podaci će biti izgubljeni.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

I kao bonus, nedavno smo imali priliku prikupiti podatke od Kafke u ClickHouseu. Postoji stoni motor - Kafka. Vi jednostavno stvarate. I možete okačiti materijalizovane poglede na to. U tom slučaju, on će izvaditi podatke iz Kafke i umetnuti ih u tabele koje su vam potrebne.

A ono što je posebno drago u ovoj prilici je da je nismo uspjeli. Ovo je funkcija zajednice. A kad kažem "zajednička karakteristika", kažem to bez ikakvog prezira. Pročitali smo kod, uradili pregled, trebalo bi da radi dobro.

* od 2020. postoji slična podrška za Rabbit MQ.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Šta još može biti nezgodno ili neočekivano prilikom umetanja podataka? Ako napravite upit za umetanje vrijednosti i napišite neke izračunate izraze u vrijednostima. Na primjer, now() je također evaluirani izraz. I u ovom slučaju, ClickHouse je primoran da pokrene interpretator ovih izraza za svaki red, a performanse će pasti za redove veličine. Bolje je izbjegavati.

* trenutno je problem u potpunosti riješen, nema više regresije performansi kada se koriste izrazi u VRIJEDNOSTI.

Drugi primjer gdje može doći do nekih problema je kada vaši podaci na jednoj seriji pripadaju gomili particija. Po defaultu, ClickHouse particije po mjesecima. A ako umetnete grupu od milion redova, a postoje podaci za nekoliko godina, onda ćete tamo imati nekoliko desetina particija. A to je ekvivalentno činjenici da će postojati serije nekoliko desetina puta manje, jer se iznutra uvijek prvo dijele na particije.

* nedavno je u ClickHouse u eksperimentalnom modu dodana podrška za kompaktni format chunk-ova i chunk-ova u RAM-u sa zapisom unaprijed, što gotovo u potpunosti rješava problem.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Sada razmotrite drugu vrstu problema - tipkanje podataka.

Unos podataka može biti strog, a ponekad i niz. String - ovo je kada ste upravo uzeli i deklarirali da imate sva polja tipa string. Sranje. Ne morate to da radite.

Hajde da smislimo kako da to uradimo kako treba u slučajevima kada želite da kažete da imamo neko polje, niz, i neka ClickHouse to sam shvati, ali ja se neću kupati u parnom kupatilu. Ali ipak vrijedi uložiti malo truda.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Na primjer, imamo IP adresu. U jednom slučaju smo ga sačuvali kao string. Na primjer, 192.168.1.1. U suprotnom, to će biti broj tipa UInt32*. 32 bita je dovoljno za IPv4 adresu.

Prvo, koliko je čudno, podaci će biti komprimirani otprilike na isti način. Biće razlike, naravno, ali ne tako velike. Dakle, nema posebnih problema sa disk I/O.

Ali postoji ozbiljna razlika u CPU vremenu i vremenu izvršenja upita.

Hajde da izbrojimo broj jedinstvenih IP adresa ako su pohranjene kao brojevi. Ispada 137 miliona linija u sekundi. Ako je isto kao i linije, onda 37 miliona linija u sekundi. Ne znam zašto je došlo do ove slučajnosti. Ja sam lično uradio ove zahteve. Ali ipak oko 4 puta sporije.

A ako izračunate razliku u prostoru na disku, onda postoji i razlika. A razlika je oko jedne četvrtine, jer postoji dosta jedinstvenih IP adresa. A da su postojali redovi s malim brojem različitih vrijednosti, onda bi bili tiho komprimirani u rječniku u približno isti volumen.

A četvorostruka vremenska razlika nije ležanje na putu. Možda vas, naravno, nije briga, ali kada vidim takvu razliku, budem tužan.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Hajde da razmotrimo različite slučajeve.

1. Jedan slučaj kada imate nekoliko različitih jedinstvenih vrijednosti. U ovom slučaju koristimo jednostavnu praksu koju vjerojatno znate i koju možete koristiti za bilo koji DBMS. Sve ovo ima smisla ne samo za ClickHouse. Samo upišite numeričke identifikatore u bazu podataka. I možete konvertovati u nizove i nazad sa strane vaše aplikacije.

Na primjer, imate regiju. I pokušavate da ga sačuvate kao string. I tamo će biti napisano: Moskva i Moskovska oblast. A kad vidim da tamo piše "Moskva" onda je to još ništa, a kad je MO, nekako postane potpuno tužno. Toliko bajtova.

Umjesto toga, jednostavno zapišemo Ulnt32 broj i 250. Imamo 250 u Yandexu, ali vaš može biti drugačiji. Za svaki slučaj, reći ću da ClickHouse ima ugrađenu mogućnost rada sa geobazom. Jednostavno zapišete direktorij s regijama, uključujući i hijerarhijski, tj. bit će Moskva, Moskovska regija i sve što vam treba. I možete pretvoriti na nivou zahtjeva.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Druga opcija je otprilike ista, ali sa podrškom unutar ClickHouse-a. To je tip podataka Enum. Jednostavno upisujete sve vrijednosti koje su vam potrebne unutar enuma. Na primjer, tip uređaja i upišite tamo: desktop, mobilni, tablet, TV. Samo 4 opcije.

Nedostatak je što morate povremeno mijenjati. Dodana je samo jedna opcija. Izrađujemo alter table. U stvari, alter table u ClickHouseu je besplatna. Posebno besplatno za Enum jer se podaci na disku ne mijenjaju. Ali ipak, alter preuzima zaključavanje * na tabeli i mora čekati dok se svi odabiri ne završe. I tek nakon što će ova izmjena biti izvršena, tj. i dalje postoje neke neugodnosti.

* u novijim verzijama ClickHousea, ALTER je potpuno neblokirajući.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Druga opcija koja je prilično jedinstvena za ClickHouse je povezivanje vanjskih rječnika. Možete pisati brojeve u ClickHouse i držati svoje imenike u bilo kom sistemu koji vam odgovara. Na primjer, možete koristiti: MySQL, Mongo, Postgres. Možete čak i kreirati svoj mikroservis, koji će slati ove podatke putem http. A na ClickHouse nivou pišete funkciju koja će ove podatke pretvoriti iz brojeva u nizove.

Ovo je specijaliziran, ali vrlo efikasan način da se izvrši spajanje na vanjskom stolu. I postoje dvije opcije. U jednoj opciji, ovi podaci će biti u potpunosti keširani, potpuno prisutni u RAM-u i ažurirani u određenim intervalima. I u drugoj opciji, ako se ovi podaci ne uklapaju u RAM, možete ih djelomično keširati.

Evo primjera. Postoji Yandex.Direct. A tu je i reklamna kompanija i baneri. Vjerovatno postoje desetine miliona reklamnih kompanija. I otprilike stane u RAM. I postoje milijarde banera, oni ne odgovaraju. I mi koristimo keširani rječnik iz MySQL-a.

Jedini problem je što će keširani rečnik raditi dobro ako je stopa pogodaka blizu 100%. Ako je manji, tada će prilikom obrade zahtjeva za svaki paket podataka biti potrebno zapravo uzeti ključeve koji nedostaju i otići na preuzimanje podataka iz MySQL-a. Što se tiče ClickHouse-a, to još uvijek mogu garantirati - da, ne usporava, neću govoriti o drugim sistemima.

I kao bonus, rječnici su vrlo jednostavan način za ažuriranje podataka u ClickHouseu retroaktivno. Odnosno, imali ste izveštaj o reklamnim kompanijama, korisnik je jednostavno promenio reklamnu kompaniju i u svim starim podacima, u svim izveštajima, i ovi podaci su se promenili. Ako upišete redove direktno u tabelu, nećete ih moći ažurirati.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Drugi način kada ne znate gdje da dobijete identifikatore za svoje stringove. možete samo haširati. A najlakša opcija je uzeti 64-bitni hash.

Jedini problem je što ako je heš 64-bitni, onda ćete gotovo sigurno imati kolizije. Jer ako postoji milijarda linija, onda vjerovatnoća već postaje opipljiva.

I ne bi bilo dobro da se tako heširaju imena reklamnih kompanija. Ako se reklamne kampanje različitih kompanija pomiješaju, onda će biti nešto neshvatljivo.

I postoji jednostavan trik. Istina, nije baš pogodan za ozbiljne podatke, ali ako nešto nije jako ozbiljno, jednostavno dodajte još jedan identifikator klijenta ključu rječnika. I tada ćete imati kolizije, ali samo unutar jednog klijenta. I mi koristimo ovu metodu za mapu veza u Yandex.Metrici. Tamo imamo URL-ove, pohranjujemo hasheve. I znamo da ima sukoba, naravno. Ali kada se prikaže stranica, onda je vjerovatnoća da se na jednoj stranici za jednog korisnika neki urlovi zalijepe i da će se to primijetiti, onda se to može zanemariti.

Kao bonus, za mnoge operacije dovoljan je samo heš, a sami nizovi se ne mogu nigdje pohraniti.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Drugi primjer ako su stringovi kratki, kao što su domene web stranice. Mogu se čuvati onakvima kakvi jesu. Ili, na primjer, jezik pretraživača ru je 2 bajta. Naravno, žao mi je bajtova, ali ne brinite, 2 bajta nisu šteta. Molim vas da ostane kako jeste, ne brinite.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Drugi slučaj je kada, naprotiv, postoji mnogo nizova, a istovremeno u njima ima puno jedinstvenih, a čak je i skup potencijalno neograničen. Tipičan primjer su fraze za pretraživanje ili URL-ovi. Fraze za pretraživanje, uključujući greške u kucanju. Pogledajmo koliko jedinstvenih fraza za pretraživanje dnevno. I ispostavilo se da su oni skoro polovina svih događaja. I u ovom slučaju, možda mislite da trebate normalizirati podatke, prebrojati identifikatore, staviti ih u zasebnu tabelu. Ali ne morate to da radite. Samo zadržite ove linije kakve jesu.

Bolje - ne izmišljajte ništa, jer ako ga pohranite odvojeno, morat ćete izvršiti spajanje. A ovo spajanje je u najboljem slučaju slučajni pristup memoriji, ako još stane u memoriju. Ako se ne uklapa, onda će općenito biti problema.

A ako su podaci pohranjeni na mjestu, onda se jednostavno čitaju u pravom redoslijedu iz sistema datoteka i sve je u redu.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Ako imate URL-ove ili neki drugi složeni dugi niz, onda biste trebali razmisliti o tome da možete unaprijed izračunati stisak i napisati ga u zasebnu kolonu.

Za URL-ove, na primjer, možete zasebno pohraniti domenu. A ako vam zaista treba domena, samo koristite ovu kolonu i URL-ovi će lagati, a nećete ih ni dodirnuti.

Hajde da vidimo u čemu je razlika. ClickHouse ima specijaliziranu funkciju koja izračunava domenu. Vrlo je brz, optimizirali smo ga. I, da budem iskren, nije čak ni u skladu sa RFC-om, ali ipak uzima u obzir sve što nam treba.

A u jednom slučaju, jednostavno ćemo dobiti URL-ove i izračunati domen. Ispada 166 milisekundi. A ako uzmete gotovu domenu, ispada samo 67 milisekundi, odnosno gotovo tri puta brže. I brže, ne zato što treba da radimo neke proračune, već zato što čitamo manje podataka.

Iz nekog razloga, jedan zahtjev, koji je sporiji, dobija veću brzinu u gigabajtima u sekundi. Jer čita više gigabajta. Ovo su potpuno suvišni podaci. Čini se da zahtjev radi brže, ali traje duže da se završi.

A ako pogledate količinu podataka na disku, ispada da je URL 126 megabajta, a domen samo 5 megabajta. Ispada 25 puta manje. Međutim, upit je i dalje samo 4 puta brži. Ali to je zato što su podaci vrući. A da je hladno, vjerovatno bi bilo 25 puta brže zbog disk I/O.

Usput, ako procijenite koliko je domena manja od url-a, onda se ispostavi da je oko 4 puta, ali iz nekog razloga podaci na disku zauzimaju 25 puta manje. Zašto? Zbog kompresije. I url je komprimiran, a domena je komprimirana. Ali često URL sadrži gomilu smeća.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

I, naravno, vrijedi koristiti prave tipove podataka koji su posebno dizajnirani za prave vrijednosti ili koji odgovaraju. Ako ste u IPv4, pohranite UInt32*. Ako je IPv6, onda FixedString(16), jer je IPv6 adresa 128 bita, tj. pohraniti direktno u binarni format.

Ali šta ako ponekad imate IPv4 adrese, a ponekad IPv6? Da, možete zadržati oboje. Jedna kolona za IPv4, druga za IPv6. Naravno, postoji opcija mapiranja IPv4 u IPv6. Ovo će također funkcionirati, ali ako vam je često potrebna IPv4 adresa u vašim zahtjevima, onda bi bilo lijepo da je stavite u zasebnu kolonu.

* Sada ClickHouse ima zasebne IPv4, IPv6 tipove podataka koji pohranjuju podatke jednako efikasno kao brojevi, ali ih predstavljaju zgodno kao nizovi.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Također je važno napomenuti da je vrijedno unaprijed obraditi podatke. Na primjer, neki sirovi trupci vam stignu. I, možda, ne biste ih trebali odmah stavljati u ClickHouse, iako je vrlo primamljivo ne raditi ništa i sve će raditi. Ali još uvijek vrijedi izvršiti one proračune koji su mogući.

Na primjer, verzija pretraživača. U nekom susjednom odjelu, na koji ne želim da upirem prstom, verzija pretraživača je pohranjena ovako, odnosno kao string: 12.3. I onda, da bi napravili izvještaj, uzimaju ovaj niz i dijele nizom, a zatim prvim elementom niza. Naravno, sve se usporava. Pitao sam zašto to rade. Rekli su mi da ne vole preuranjenu optimizaciju. I ne volim preuranjeni pesimizam.

Dakle, u ovom slučaju bi bilo ispravnije podijeliti u 4 kolone. Ne bojte se ovdje, jer ovo je ClickHouse. ClickHouse je baza podataka stupaca. I što je urednijih malih kolona, ​​to bolje. Biće 5 BrowserVerzija, napravite 5 kolona. Ovo je u redu.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Sada razmislite šta da radite ako imate mnogo veoma dugih nizova, veoma dugih nizova. Oni uopće ne moraju biti pohranjeni u ClickHouse. Umjesto toga, možete pohraniti samo neke identifikatore u ClickHouse. I ovi dugi redovi ih guraju u neki drugi sistem.

Na primjer, jedna od naših analitičkih usluga ima neke parametre događaja. A ako mnogo parametara dođe do događaja, jednostavno sačuvamo prvi 512 koji naiđe, jer 512 nije šteta.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

A ako ne možete da odlučite o svojim tipovima podataka, onda možete i upisati podatke u ClickHouse, ali u privremenu tabelu tipa Log, koja je posebna za privremene podatke. Nakon toga možete analizirati kakvu distribuciju vrijednosti imate, šta je općenito i sastaviti ispravne tipove.

* Sada ClickHouse ima tip podataka Niska kardinalnost što vam omogućava efikasno pohranjivanje žica uz manje napora.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Sada razmotrite još jedan zanimljiv slučaj. Ponekad stvari funkcionišu na čudan način za ljude. Idem da vidim ovo. I odmah se čini da je to uradio neki vrlo iskusan, pametan admin koji ima veliko iskustvo u postavljanju MySQL verzije 3.23.

Ovdje vidimo hiljadu tabela, od kojih svaka sadrži ostatak dijeljenja nije jasno šta sa hiljadu.

U principu, poštujem tuđa iskustva, uključujući i razumijevanje kakve patnje se ovo iskustvo može steći.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

A razlozi su manje-više jasni. Ovo su stari stereotipi koji su se možda nakupili tokom rada sa drugim sistemima. Na primjer, MyISAM tablice nemaju klasterizirani primarni ključ. A ovaj način dijeljenja podataka može biti očajnički pokušaj da se dobije ista funkcionalnost.

Drugi razlog je taj što je teško izvršiti bilo kakve operacije promjene na velikim tablicama. Sve će biti blokirano. Iako u modernim verzijama MySQL-a, ovaj problem više nije tako ozbiljan.

Ili, na primjer, mikrosharding, ali o tome kasnije.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

U ClickHouseu to ne morate raditi, jer je, prvo, primarni ključ grupisan, podaci su poređani po primarnom ključu.

I ponekad me ljudi pitaju: „Kako se performanse upita opsega u ClickHouse-u mijenjaju s veličinom tablice?“. Ja kažem da se to uopšte ne menja. Na primjer, imate tabelu sa milijardu redova i čitate raspon od milion redova. Sve je uredu. Ako tabela ima trilion redova, a vi čitate milion redova, onda će biti skoro isto.

I, drugo, nisu potrebni bilo kakvi dijelovi poput ručnih pregrada. Ako uđete i pogledate šta je na sistemu datoteka, videćete da je tabela prilično ozbiljna stvar. A unutra je nešto poput pregrada. To jest, ClickHouse radi sve za vas i ne morate da patite.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Izmjena u ClickHouse-u je besplatna ako promijenite kolonu dodaj/ispusti.

I ne biste trebali praviti male tabele, jer ako imate 10 redova ili 10 redova u tabeli, onda to uopšte nije važno. ClickHouse je sistem koji optimizuje protok, a ne kašnjenje, tako da nema smisla obraditi 000 linija.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Ispravno je koristiti jedan veliki sto. Oslobodite se starih stereotipa, sve će biti u redu.

I kao bonus, u najnovijoj verziji, imamo mogućnost da napravimo proizvoljni ključ za particioniranje kako bismo izvršili sve vrste operacija održavanja na pojedinačnim particijama.

Na primjer, potrebno vam je mnogo malih tablica, na primjer, kada postoji potreba za obradom nekih međupodataka, dobijate komade i trebate izvršiti transformaciju na njima prije nego što upišete u konačnu tablicu. Za ovaj slučaj postoji prekrasan stolni stroj - StripeLog. To je kao TinyLog, samo bolje.

* Sada ClickHouse ima više unos funkcije tablice.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Još jedan anti-uzorak je mikrošarding. Na primjer, trebate podijeliti podatke i imate 5 servera, a sutra će biti 6 servera. I razmišljate kako da rebalansirate ove podatke. I umjesto toga, ne dijelite se na 5 komada, već na 1 komada. I onda mapirate svaku od ovih mikrošardova na poseban server. I uspjet ćete na jednom serveru, na primjer, 000 ClickHouse, na primjer. Odvojena instanca na odvojenim portovima ili odvojenim bazama podataka.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Ali u ClickHouseu to nije baš dobro. Zato što čak i jedna instanca ClickHouse pokušava da iskoristi sve dostupne resurse servera za obradu jednog zahteva. Odnosno, imate neku vrstu servera i tamo, na primjer, 56 procesorskih jezgri. Pokrećete upit koji traje jednu sekundu i koristit će 56 jezgara. A ako postavite 200 ClickHouses-a na jedan server tamo, ispada da će se pokrenuti 10 niti. Generalno, sve će biti jako loše.

Drugi razlog je taj što će distribucija posla na ovim instancama biti neravnomjerna. Neki će završiti ranije, neki će završiti kasnije. Ako bi se sve ovo dogodilo u jednom slučaju, onda bi ClickHouse sam shvatio kako pravilno distribuirati podatke među streamovima.

Drugi razlog je taj što ćete imati međuprocesorsku komunikaciju preko TCP-a. Podaci će se morati serijalizirati, deserializirati, a radi se o ogromnom broju mikrokrhotina. To jednostavno neće uspjeti.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Još jedan antiuzorak, iako se teško može nazvati antišablom. Ovo je velika količina prethodnog združivanja.

Općenito, preagregacija je dobra. Imali ste milijardu redova, agregirali ste to i postalo je 1 redova, a sada se upit izvršava trenutno. Sve je super. Tako ti to možeš. A za ovo, čak i ClickHouse ima poseban tip tablice AggregatingMergeTree koja vrši inkrementalno agregiranje kako se podaci ubacuju.

Ali postoje trenuci kada mislite da ćemo ovako agregirati podatke i ovakve podatke. A u nekom susednom odeljenju, ne želim da govorim ni u kom, koriste tabele SummingMergeTree za sumiranje po primarnom ključu, a 20 kolona se koristi kao primarni ključ. Za svaki slučaj, promijenio sam nazive nekih rubrika zbog zavjere, ali to je to.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

I takvi problemi nastaju. Prvo, količina podataka koju imate nije previše smanjena. Na primjer, smanjuje se za tri puta. Tri puta bi bila dobra cijena da priuštite neograničenu analitiku koja dolazi s posedovanjem neagregiranih podataka. Ako su podaci agregirani, onda umjesto analitike dobijate samo mizernu statistiku.

A šta je posebno dobro? Da ovi ljudi iz susednog odeljenja odu i traže ponekad da dodaju još jednu kolonu primarnom ključu. Odnosno, ovako smo agregirali podatke, a sada želimo malo više. Ali u ClickHouseu nema alter primarnog ključa. Stoga morate napisati neke skripte u C ++. I ne volim skripte, čak i ako su na C++.

A ako pogledate za šta je ClickHouse kreiran, onda su neagregirani podaci upravo scenario za koji je rođen. Ako koristite ClickHouse za neagregirane podatke, onda radite sve kako treba. Ako agregirate, to se ponekad može oprostiti.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Još jedan zanimljiv slučaj su zahtjevi u beskonačnoj petlji. Ponekad odem na neki produkcijski server i tamo pogledam show processlist. I svaki put otkrijem da se dešava nešto strašno.

Na primjer, evo ovo. Odmah je jasno da je sve bilo moguće uraditi u jednom zahtjevu. Samo upišite url i listu tamo.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Zašto je mnogo takvih zahtjeva u beskonačnoj petlji loše? Ako se indeks ne koristi, tada ćete imati mnogo prijelaza preko istih podataka. Ali ako se koristi indeks, na primjer, imate primarni ključ na ru i tamo pišete url = nešto. A vi mislite da će se jedan url tački čitati iz tabele, sve će biti u redu. Ali stvarno ne. Jer ClickHouse sve radi u serijama.

Kada treba da pročita neki opseg podataka, čita malo više, jer je indeks u ClickHouse-u oskudan. Ovaj indeks vam ne dozvoljava da pronađete jedan pojedinačni red u tabeli, već samo neku vrstu opsega. I podaci se komprimiraju u blokove. Da biste pročitali jedan red, morate uzeti cijeli blok i dekomprimirati ga. A ako pokrenete gomilu upita, imaćete mnogo preseka tih upita, i imaćete puno posla koji se obavlja iznova i iznova.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

I kao bonus, možete vidjeti da se u ClickHouseu ne biste trebali bojati prenijeti čak i megabajte, pa čak i stotine megabajta u IN sekciju. Sjećam se iz naše prakse da ako u MySQL-u prođemo gomilu vrijednosti u IN sekciju, na primjer, tamo prođemo 100 megabajta nekih brojeva, onda MySQL pojede 10 gigabajta memorije i ništa drugo se ne događa to, sve radi lose.

A druga stvar je da u ClickHouseu, ako vaši upiti koriste indeks, onda to uvijek nije sporije od potpunog skeniranja, tj. ako trebate pročitati skoro cijelu tabelu, ići će uzastopno i čitati cijelu tablicu. Generalno, on će to shvatiti.

Međutim, postoje određene poteškoće. Na primjer, taj IN s potupitom ne koristi indeks. Ali to je naš problem i moramo ga riješiti. Ovdje nema ničeg fundamentalnog. Uradimo to*.

I još jedna zanimljiva stvar je da ako imate jako dug zahtjev i u toku je distribuirana obrada zahtjeva, onda će ovaj vrlo dug zahtjev biti poslan na svaki server bez kompresije. Na primjer, 100 megabajta i 500 servera. I, shodno tome, 50 gigabajta će biti prebačeno preko mreže. Bit će preneseno i tada će sve biti uspješno izvršeno.

* već koristi; sve je popravljeno kako je obecano.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

I prilično je uobičajeno ako zahtjevi dolaze iz API-ja. Na primjer, napravili ste neku vrstu usluge. A ako nekome treba vaša usluga, onda ste otvorili API i bukvalno dva dana kasnije vidite da se dešava nešto neshvatljivo. Sve je preopterećeno i stižu neki strašni zahtjevi koji nikada nisu trebali biti.

I postoji samo jedno rješenje. Ako ste otvorili API, morat ćete ga izrezati. Na primjer, da unesete neke kvote. Nema drugih razumnih opcija. U suprotnom, odmah će napisati scenario i biće problema.

A ClickHouse ima posebnu funkciju - ovo je izračun kvota. Štaviše, možete prenijeti svoj ključ kvote. Ovo je, na primjer, interni korisnički ID. A kvote će se izračunavati nezavisno za svaku od njih.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Sada još jedna zanimljiva stvar. Ovo je ručna replikacija.

Znam mnogo slučajeva u kojima, uprkos tome što ClickHouse ima ugrađenu podršku za replikaciju, ljudi ručno repliciraju ClickHouse.

Šta je princip? Imate cjevovod za obradu podataka. I radi nezavisno, na primjer, u različitim podatkovnim centrima. Iste podatke pišete na isti način u ClickHouse, takoreći. Istina, praksa pokazuje da će se podaci i dalje razlikovati zbog nekih posebnosti u vašem kodu. Nadam se da u tvom.

I povremeno i dalje morate ručno sinhronizirati. Na primjer, jednom mjesečno administratori rade rsync.

U stvari, mnogo je lakše koristiti ugrađenu replikaciju u ClickHouse. Ali mogu postojati neke kontraindikacije, jer za to morate koristiti ZooKeeper. Neću reći ništa loše o ZooKeeperu, u principu sistem radi, ali se dešava da ga ljudi ne koriste zbog java-fobije, jer je ClickHouse tako dobar sistem napisan na C++ da možete koristiti i sve će biti u redu. I ZooKeeper u Javi. I nekako ne želite ni da gledate, ali onda možete koristiti ručnu replikaciju.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

ClickHouse je praktičan sistem. Uzima u obzir vaše potrebe. Ako imate ručnu replikaciju, tada možete kreirati distribuiranu tabelu koja gleda vaše ručne replike i vrši prebacivanje između njih. Postoji čak i posebna opcija koja vam omogućava da izbjegnete neuspjehe, čak i ako se vaše linije sistematski razlikuju.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Nadalje, može doći do problema ako koristite primitivne mašine za tablice. ClickHouse je takav konstruktor koji ima gomilu različitih mašina za tablice. Za sve ozbiljne slučajeve, kako piše u dokumentaciji, koristite tabele MergeTree porodice. A sve ostalo - to je tako, za pojedinačne slučajeve ili za testove.

U tabeli MergeTree, ne morate imati datum i vrijeme. Još uvijek možete koristiti. Ako nema datuma i vremena, napišite da je zadana vrijednost 2000. Radit će i neće zahtijevati resurse.

A u novoj verziji servera možete čak odrediti da imate prilagođeno particioniranje bez ključa particije. Bit će isto.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

S druge strane, mogu se koristiti primitivni stolni strojevi. Na primjer, jednom popunite podatke i pogledajte, uvrnite i izbrišite. Možete koristiti Log.

Ili pohranjivanje malih volumena za srednju obradu je StripeLog ili TinyLog.

Memorija se može koristiti ako postoji mala količina podataka i samo uvrnuti nešto u RAM-u.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

ClickHouse ne voli mnogo renormalizirane podatke.

Evo tipičnog primjera. Ovo je ogroman broj URL-ova. Stavljate ih u susedni sto. I onda smo odlučili da napravimo JOIN sa njima, ali to po pravilu neće raditi, jer ClickHouse podržava samo Hash JOIN. Ako nema dovoljno RAM-a za puno podataka sa kojima se može povezati, JOIN neće raditi *.

Ako su podaci visoke kardinalnosti, onda ne brinite, pohranite ih u denormaliziranom obliku, URL-ovi su direktno na mjestu u glavnoj tabeli.

* i sada ClickHouse ima i spajanje spajanja, i radi u uslovima u kojima se međupodaci ne uklapaju u RAM. Ali to je neefikasno i preporuka ostaje na snazi.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Još par primjera, ali već sumnjam da li su anti-obrasci ili ne.

ClickHouse ima jedan poznati nedostatak. Ne zna kako da ažurira *. U određenom smislu, ovo je čak i dobro. Ako imate neke važne podatke, na primjer, računovodstvo, onda ih niko neće moći poslati, jer nema ažuriranja.

* Podrška za ažuriranje i brisanje u batch modu je odavno dodana.

Ali postoje neki posebni načini koji omogućavaju da se ažuriranja pojavljuju u pozadini. Na primjer, tablice tipa ReplaceMergeTree. Oni vrše ažuriranja tokom spajanja u pozadini. Ovo možete forsirati pomoću optimizirane tablice. Ali nemojte to činiti prečesto, jer će to potpuno prepisati particiju.

Distribuirani JOIN-ovi u ClickHouse-u - ovo također loše obrađuje planer upita.

Loše, ali ponekad u redu.

Korištenje ClickHouse samo za čitanje podataka sa select*.

Ne bih preporučio korištenje ClickHousea za glomazne proračune. Ali to nije sasvim tačno, jer se već udaljavamo od ove preporuke. Nedavno smo dodali i mogućnost primjene modela strojnog učenja u ClickHouse - Catboost. I to me brine, jer mislim: „Kakav užas. Ovo je koliko ciklusa po bajtu ispada! Šteta mi je pokretati taktove na bajtovima.

Efikasno korištenje ClickHouse-a. Aleksej Milovidov (Yandex)

Ali ne bojte se, instalirajte ClickHouse, sve će biti u redu. Ako ništa drugo, mi imamo zajednicu. Inače, zajednica ste vi. A ako imate bilo kakvih problema, možete barem otići na naš chat, i nadam se da će vam pomoći.

Vaša pitanja

Hvala na izvještaju! Gdje se žaliti zbog pada ClickHousea?

Možete se odmah žaliti meni lično.

Nedavno sam počeo da koristim ClickHouse. Cli interfejs je odmah ispao.

Kakav rezultat.

Malo kasnije, ispustio sam server sa malim odabirom.

Imaš talenat.

Otvorio sam grešku na GitHub-u, ali je ignorisana.

Vidit ćemo.

Aleksej me je prevario da prisustvujem izveštaju, obećavajući da će mi reći kako uvlačite podatke unutra.

Vrlo je jednostavno.

To je ono što sam jučer shvatio. Više pojedinosti.

Nema strašnih trikova. To je samo kompresija blok po blok. Podrazumevano je LZ4, možete omogućiti ZSTD*. Blokira od 64 kilobajta do 1 megabajta.

* postoji i podrška za specijalizovane kodeke za kompresiju koji se mogu koristiti u lancu sa drugim algoritmima.

Da li su blokovi samo sirovi podaci?

Nije baš sirovo. Postoje nizovi. Ako imate numeričku kolonu, onda su brojevi u redu složeni u niz.

To je jasno.

Alexey, primjer koji je bio sa uniqExact-om preko IP-ova, tj. činjenica da je uniqExact-u potrebno više vremena da se broji po nizovima nego po brojevima, i tako dalje. Šta ako ušima nanesemo fintu i bacimo u trenutku lekture? Odnosno, čini se da ste rekli da se ne razlikuje mnogo na disku. Ako čitamo linije sa diska, bacamo, hoćemo li imati agregate brže ili ne? Ili tu još uvijek neznatno dobivamo? Čini mi se da ste ga testirali, ali iz nekog razloga to niste naveli u benčmarku.

Mislim da će to biti sporije nego bez bacanja. U ovom slučaju, IP adresa se mora raščlaniti iz niza. Naravno, u ClickHouse-u je optimizirana i analiza IP adresa. Trudili smo se jako, ali na istom mjestu imate napisane brojeve u desethiljaditom obliku. Veoma neprijatno. S druge strane, funkcija uniqExact će raditi sporije na nizovima, ne samo zato što su to stringovi, već i zato što je odabrana drugačija specijalizacija algoritma. Stringovi se jednostavno tretiraju drugačije.

A ako uzmemo primitivniji tip podataka? Na primjer, zapisali su korisnički ID koji imamo, zapisali ga kao liniju, a zatim ga bacili, hoće li biti zabavnije ili ne?

Sumnjam. Mislim da će biti još tužnije, jer je ipak raščlanjivanje brojeva ozbiljan problem. Čini mi se da je ovaj kolega čak imao izvještaj o tome kako je teško raščlaniti brojeve u desethiljaditom obliku, ali možda i nije.

Alexey, hvala puno na izvještaju! I puno vam hvala za ClickHouse! Imam pitanje u vezi planova. Postoji li u planovima mogućnost nepotpunog ažuriranja rječnika?

tj. djelomično ponovno pokretanje?

Da da. Poput mogućnosti da se tamo postavi MySQL polje, tj. ažuriranje nakon tako da se samo ovi podaci učitavaju ako je rječnik jako velik.

Vrlo zanimljiva karakteristika. I, čini mi se, neka osoba je to predložila u našem ćaskanju. Možda si čak i ti.

Mislim da nije.

Odlično, sad se ispostavilo da su dva zahtjeva. I možete početi to raditi polako. Ali želim vas odmah upozoriti da je ova funkcija prilično jednostavna za implementaciju. To jest, u teoriji, samo trebate upisati broj verzije u tablicu i nastaviti pisati: verzija je manja od takve i takve. A to znači da ćemo ga, najvjerovatnije, ponuditi entuzijastima. Jeste li entuzijasta?

Da, ali nažalost ne u C++.

Mogu li vaše kolege pisati na C++?

Naći ću nekoga.

Odlično*.

* funkcija je dodata dva mjeseca nakon izvještaja - razvio ju je autor pitanja i poslao je on pull zahtjev.

Hvala vam!

Zdravo! Hvala na izvještaju! Spomenuli ste da ClickHouse jako dobro troši sve resurse koji su mu dostupni. A govornik pored Luxofta je o svojoj odluci govorio za Rusku poštu. Rekao je da im se ClickHouse jako dopao, ali ga nisu koristili umjesto glavnog konkurenta upravo zato što je pojeo cijeli procesor. I nisu to mogli uklopiti u svoju arhitekturu, u svoj ZooKeeper sa dokerima. Da li je moguće nekako ograničiti ClickHouse tako da ne troši sve što mu postane dostupno?

Da, moguće je i vrlo lako. Ako želite da trošite manje jezgara, samo pišite set max_threads = 1. I to je sve, izvršit će zahtjev u jednom jezgru. Osim toga, možete odrediti različite postavke za različite korisnike. Dakle, nema problema. I recite svojim kolegama iz Luxofta da nije dobro što ovu postavku nisu našli u dokumentaciji.

Alexey, zdravo! Hteo bih da postavim ovo pitanje. Ovo nije prvi put da čujem da mnogi ljudi počinju da koriste ClickHouse kao spremište za dnevnike. U izvještaju ste rekli da to ne radite, odnosno da ne morate čuvati dugačke redove. Šta mislite o tome?

Prvo, zapisnici obično nisu dugi redovi. Postoje, naravno, izuzeci. Na primjer, neki servis napisan u Javi izbacuje izuzetak, on se evidentira. I tako u beskrajnoj petlji i ponestaje prostora na tvrdom disku. Rješenje je vrlo jednostavno. Ako su linije jako dugačke, onda ih odrežite. Šta znači dugo? Desetine kilobajta je loše *.

* u novijim verzijama ClickHousea omogućena je "prilagodljiva granularnost indeksa", što uglavnom uklanja problem pohranjivanja dugih nizova.

Da li je kilobajt normalan?

Normalno je.

Zdravo! Hvala na izvještaju! Već sam pitao o tome u chatu, ali se ne sjećam da li sam dobio odgovor. Postoji li plan za proširenje odjeljka WITH na CTE način?

Ne još. Odjeljak SA je pomalo neozbiljan. To je za nas kao mala karakteristika.

Razumijem. Hvala ti!

Hvala na izvještaju! Vrlo zanimljivo! globalno pitanje. Da li se planira uraditi, možda u vidu nekakvih stubova, modifikacija brisanja podataka?

Neophodno. Ovo je naš prvi zadatak u redu. Sada aktivno razmišljamo o tome kako sve učiniti kako treba. I trebali biste početi pritiskati tastaturu*.

* pritisnuli dugmad na tastaturi i sve je bilo gotovo.

Hoće li to nekako utjecati na performanse sistema ili ne? Hoće li umetanje biti brzo kao što je sada?

Možda će sama brisanja, sama ažuriranja biti vrlo teška, ali to ni na koji način neće utjecati na performanse odabira i performanse umetanja.

I još jedno malo pitanje. Na prezentaciji ste govorili o primarnom ključu. Shodno tome, imamo particioniranje, koje je po defaultu mjesečno, zar ne? A kada postavimo raspon datuma koji se uklapa u mjesec, onda čitamo samo ovu particiju, zar ne?

Da.

Pitanje. Ako ne možemo da izaberemo nijedan primarni ključ, da li je u redu da to uradimo tačno po polju „Datum“ kako bi u pozadini bilo manje restrukturiranje ovih podataka kako bi se urednije uklopili? Ako nemate upite za raspon i ne možete čak ni odabrati nijedan primarni ključ, vrijedi li staviti datum u primarni ključ?

Da.

Možda ima smisla staviti u primarni ključ polje po kojem će podaci biti bolje komprimirani ako se sortiraju po ovom polju. Na primjer, ID korisnika. Korisnik, na primjer, odlazi na istu stranicu. U tom slučaju unesite korisnički ID i vrijeme. I tada će vaši podaci biti bolje komprimirani. Što se tiče datuma, ako zaista nemate i nikada nemate upite o rasponu za datume, onda ne možete staviti datum u primarni ključ.

OK hvala puno!

izvor: www.habr.com

Dodajte komentar