Përshëndetje, Habr.
Quo në emër, unë jam Misha Butrimov, dhe do të doja të flas pak për Cassandra. Tregimi im do të jetë i dobishëm për ata që nuk kanë pasur ndonjëherë përvojë me bazat e të dhënave NoSQL - ka shumë veçori dhe nuances që duhet të njihni. Dhe nëse nuk keni parë ndonjëherë përveç Oracle apo ndonjë baze tjetër relacional, këto gjëra do t'ju shpëtojnë.
ĂfarĂ« e bĂ«n Cassandra tĂ« shkĂ«lqyer? Kjo Ă«shtĂ« njĂ« bazĂ« tĂ« dhĂ«nash NoSQL, e projektuar pa njĂ« pikĂ« tĂ« vetme dĂ«shtimi, qĂ« pĂ«rmirĂ«sohet lehtĂ«sisht. NĂ«se ju nevojitet tĂ« shtoni disa terabajt pĂ«r njĂ« bazĂ«, thjesht shtoni node nĂ« rrip. DĂ«shironi ta zgjeroni nĂ« njĂ« qendĂ«r tĂ« tĂ« dhĂ«nash tjetĂ«r? Shtoni node nĂ« kluster. Duhet tĂ« rrisni RPS? Shtoni node nĂ« kluster. Kjo funksionon edhe nga ana tjetĂ«r.
ĂfarĂ« tjetĂ«r e bĂ«n atĂ« tĂ« mirĂ«? Ajo Ă«shtĂ« nĂ« gjendje tĂ« pĂ«rballojĂ« shumĂ« kĂ«rkesa. Po shumĂ« â sa Ă«shtĂ«? 10, 20, 30, 40 mijĂ« kĂ«rkesa nĂ« sekundĂ« â kjo Ă«shtĂ« pak. 100 mijĂ« kĂ«rkesa nĂ« sekundĂ« pĂ«r shkrim â gjithashtu tĂ« pamjaftueshme. Ka kompani qĂ« thonin se mbajnĂ« 2 milion kĂ«rkesa nĂ« sekundĂ«. PĂ«r kĂ«ta, ndoshta do tĂ« duhet t'i besoni.
Dhe nĂ« thelb, Cassandra ka njĂ« ndryshim tĂ« madh nga tĂ« dhĂ«nat relacional â ajo nuk i ngjan fare atyre. Kjo Ă«shtĂ« njĂ« gjĂ« shumĂ« e rĂ«ndĂ«sishme pĂ«r t'u mbajtur mend.
E gjithë kjo tregon se nuk është gjithçka që duket njësoj, punon njësoj.
Një herë një koleg erdhi dhe më pyeti: «Epo, ka SQL Cassandra query language dhe ka një select statement, ka një where, ka një and. Unë shkruaj shkronjat dhe nuk punon. Pse?». Nëse e shihni Cassandra-n si një bazë të dhënash relacional, kjo është një mënyrë e shkëlqyer për të përfunduar me një vetëvrasje brutale. Dhe nuk po propagandoj, kjo është e ndaluar në Rusi. Thjesht do të projektoni diçka keq.
Për shembull, një klient vjen dhe thotë: «Le të ndërtojmë një bazë të dhënash për seriale, ose një bazë të dhënash për një udhëzues recetash. Ne do të kemi pjata me produkte ose një listë serialesh dhe aktorësh në të». Ne themi me gëzim: «Le të bëjmë!». Kjo është për të transferuar dy byte, disa tabela dhe gjithçka do të funksionojë shumë shpejt, me besueshmëri. Dhe gjithçka është e shkëlqyer, derisa klientët nuk vijnë dhe nuk thonë që amvisat gjithashtu duan të zgjidhin reversin: ata kanë një listë produktesh dhe duan të dinë se çfarë për Shqipërinë duan të përgatisin. Ju jeni të humbur.
E gjithë kjo ndodh sepse Cassandra është një bazë të dhënash hibride: ajo është në të njëjtën kohë një strukturë key-value dhe ruan të dhënat në kolona të gjera. Në gjuhën Java ose Kotlin, kjo mund të përshkruhet kështu:
Map<RowKey, SortedMap>
Pra, Ă«shtĂ« njĂ« mapĂ«, brenda sĂ« cilĂ«s ka edhe njĂ« mapĂ« tĂ« renditur. ĂelĂ«si i parĂ« pĂ«r kĂ«tĂ« mapĂ« Ă«shtĂ« Row key ose Partition key â çelĂ«si i parit. ĂelĂ«si i dytĂ«, i cili Ă«shtĂ« çelĂ«si pĂ«r kĂ«tĂ« mapĂ« tĂ« renditur, Ă«shtĂ« Clustering key.
PĂ«r tĂ« ilustruar shpĂ«rndarjen e bazĂ«s sĂ« tĂ« dhĂ«nave, le tĂ« vizatojmĂ« tre node. Tani duhet tĂ« kuptojmĂ« se si tĂ« shpĂ«rndajmĂ« tĂ« dhĂ«nat nĂ« node. Sepse nĂ«se do tĂ« dĂ«rgojmĂ« gjithçka nĂ« njĂ« (mund tĂ« ketĂ« njĂ« mijĂ«, dy mijĂ«, pesĂ« â sa tĂ« doni), kjo nuk tregon shpĂ«rndarje. Prandaj na nevojitet njĂ« funksion matematikor qĂ« do tĂ« kthejĂ« njĂ« numĂ«r. Thjesht njĂ« numĂ«r, njĂ« int i gjatĂ«, qĂ« do tĂ« bjerĂ« nĂ« njĂ« gamĂ« tĂ« caktuar. Dhe njĂ« node do tĂ« pĂ«rgjigjet pĂ«r njĂ« gamĂ«, tjetra pĂ«r njĂ« tjetĂ«r, n-ta pĂ«r n-tin.

Ky numër merret me anë të një funksioni hash, i cili aplikohet pikërisht në atë që ne e quajmë Partition key. Ky është kolona e parë që tregohet në direktivën Primary key dhe është kolona që do të jetë çelësi më i rëndësishëm i mapës. Ai përcakton se cilat të dhëna do të shkojnë në cilin node. Tabela krijohet në Cassandra në një sintaksë pothuajse të njëjtë si në SQL:
CREATE TABLE users (
user_id uuid,
name text,
year int,
salary float,
PRIMARY KEY(user_id)
)
ĂelĂ«si kryesor nĂ« kĂ«tĂ« rast pĂ«rbĂ«het nga njĂ« kolonĂ«, e cila Ă«shtĂ« gjithashtu çelĂ«si i ndarjes.
Si do tĂ« pĂ«rfundojnĂ« pĂ«rdoruesit? NjĂ« pjesĂ« do tĂ« shkojĂ« nĂ« njĂ« node, njĂ« pjesĂ« nĂ« tjetrĂ«n dhe njĂ« pjesĂ« nĂ« tĂ« tretĂ«n. Kemi njĂ« tabelĂ« hash tĂ« zakonshme, e cila Ă«shtĂ« njĂ« mapĂ«, e cila gjithashtu nĂ« Python Ă«shtĂ« njĂ« fjalor, e cila Ă«shtĂ« â njĂ« strukturĂ« e thjeshtĂ« Key value, nga e cila mund tĂ« lexojmĂ« tĂ« gjitha vlerat, tĂ« lexojmĂ« dhe tĂ« shkruajmĂ« sipas çelĂ«sit.

Select: kur allow filtering shndërrohet në një skanim të plotë, ose si nuk duhet ta bëni
Le tĂ« shkruajmĂ« ndonjĂ« select statement: select * from users where userid = . Duket sikur nĂ« Oracle: shkruajmĂ« select, pĂ«rcaktojmĂ« kushtet dhe gjithçka funksionon, pĂ«rdoruesit nxirren. Por nĂ«se zgjidhni, pĂ«r shembull, njĂ« pĂ«rdorues me njĂ« vit tĂ« caktuar lindjeje, Cassandra ankohet se nuk mund ta ekzekutojĂ« kĂ«rkesĂ«n. Sepse ajo nĂ« fakt nuk di asgjĂ« pĂ«r shpĂ«rndarjen e tĂ« dhĂ«nave pĂ«r vitin e lindjes â siç Ă«shtĂ« e pĂ«rcaktuar vetĂ«m njĂ« kolonĂ« si çelĂ«s. AtĂ«herĂ« ajo thotĂ«: «MirĂ«, unĂ« mund tĂ« vazhdoj ta ekzekutoj kĂ«tĂ« kĂ«rkesĂ«. Shtoni allow filtering». Ne e shtojmĂ« direktivĂ«n, gjithçka funksionon. Dhe nĂ« kĂ«tĂ« moment ndodh diçka e tmerrshme.
Kur ne testojmë me të dhëna provë, gjithçka është në rregull. Por kur bëjmë kërkesen në prodhim, ku kemi, për shembull, 4 milion regjistrime, gjërat nuk shkojnë aq mirë. Sepse allow filtering është një direktivë që lejon Cassandra-n të mbledhë të gjitha të dhënat nga kjo tabelë nga të gjitha nodet, të gjitha të qendrave të të dhënave (nëse ka shumë në këtë klaster), dhe pastaj të filtrojë. Kjo është e ngjashme me një Full Scan, dhe dyshoj se dikush është entuziast për të.
Nëse do na duheshin përdoruesit vetëm sipas identifikuesve, do na mjaftonte. Por ndonjëherë na nevojitet të shkruajmë kërkesa të tjera dhe të vendosim kufizime të tjera në përzgjedhje. Prandaj kujtojmë: kjo është e gjitha një hartë, e cila ka një çelës partioullimi, por brenda saj është një hartë e renditur.
Dhe ajo gjithashtu ka një çelës, të cilin e quajmë Clustering Key. Ky çelës, i cili përfshin kolonat që ne do të zgjedhim, lejon Cassandra-n të kuptojë si janë fizikisht të renditura dhe ku do të jenë të dhënat në çdo nod. Domethënë, për një çelës Partition, Clustering Key do të tregojë se si t'i vendosim të dhënat në këtë pemë, në cilin vend do të zënë atje.
ĂshtĂ« me tĂ« vĂ«rtetĂ« njĂ« pemĂ«, vetĂ«m se thĂ«rritet njĂ« krahasues, nĂ« tĂ« cilin ne kalojmĂ« njĂ« grup kolonash nĂ« formĂ« objekti, dhe ai pĂ«rcaktohet gjithashtu si njĂ« enumerim kolonash.
CREATE TABLE users_by_year_salary_id (
user_id uuid,
name text,
year int,
salary float,
PRIMARY KEY((year), salary, user_id)
Vini re direktivĂ«n Primary key, argumenti i saj i parĂ« (nĂ« rastin tonĂ« viti) gjithmonĂ« shkon çelĂ«si i Particionit. Ai mund tĂ« pĂ«rbĂ«het nga njĂ« ose disa kolona, nuk ka rĂ«ndĂ«si. NĂ«se ka disa kolona, duhet ta vendosim pĂ«rsĂ«ri nĂ« kllapa, nĂ« mĂ«nyrĂ« qĂ« pĂ«rpunuesi i gjuhĂ«s ta kuptojĂ« se pĂ«rfshin pikĂ«risht çelĂ«sin e parĂ«, dhe pastaj tĂ« gjitha kolonat e tjera â Clustering Key. NdĂ«rsa ato do tĂ« kalohen nĂ« krahasues nĂ« rendin nĂ« tĂ« cilin janĂ«. DomethĂ«nĂ«, kolona e parĂ« Ă«shtĂ« mĂ« e rĂ«ndĂ«sishmja, e dyta mĂ« pak e rĂ«ndĂ«sishme dhe kĂ«shtu me radhĂ«. Siç bĂ«jmĂ« pĂ«r klasat e tĂ« dhĂ«nave, p.sh., ne listojmĂ« fushat e ngjashmĂ«risĂ«: listojmĂ« fushat dhe i pĂ«rcaktojmĂ« ato qĂ« janĂ« mĂ« shumĂ« dhe ato qĂ« janĂ« mĂ« pak. NĂ« Cassandra, kjo Ă«shtĂ«, nĂ« mĂ«nyrĂ« tĂ« kushtuar, fusha e klasĂ«s sĂ« tĂ« dhĂ«nave, pĂ«r tĂ« cilĂ«n do tĂ« aplikohet ngjashmĂ«ria e shkruar.
Vendosim renditjen, vendosim kufizime
Duhet të mbani mend se rendi i renditjes (zbritës, rritës, nuk ka rëndësi) përcaktohet në momentin kur krijohet çelësi, dhe nuk do të jetë e mundur ta ndryshoni më vonë. Ai fizikisht përcakton se si do të renditen të dhënat dhe se si do të jenë ata. Nëse do të duhet të ndryshoni Clustering Key ose rendin e renditjes, do të duhet të krijoni një tabelë të re dhe të transferoni të dhënat aty. Me tabelën ekzistuese nuk do të jetë e mundur.

Ne e mbushëm tabelën tonë me përdorues dhe pamë se ata u rreshtuan fillimisht sipas vitit të lindjes, dhe pastaj brenda çdo nodi sipas pagës dhe ID-së së përdoruesit. Tani mund të selektojmë, vendosim kufizime.
Rishfaqet pĂ«rsĂ«ri ku where, and, dhe pĂ«rdoruesit na vijnĂ«, dhe gjithçka Ă«shtĂ« sĂ«rish nĂ« rregull. Por nĂ«se pĂ«rpiqemi tĂ« pĂ«rdorim vetĂ«m njĂ« pjesĂ« tĂ« Clustering Key, dhe pĂ«r mĂ« tepĂ«r, mĂ« pak tĂ« rĂ«ndĂ«sishme, atĂ«herĂ« Cassandra do tĂ« ankohet menjĂ«herĂ«, se nuk mund tĂ« gjejĂ« vend nĂ« hartĂ«n tonĂ« ku ndodhet ky objekt, ku janĂ« kĂ«to fusha pĂ«r krahasuesin null, dhe ky tjetĂ«r qĂ« sapo e pĂ«rcaktuam â ku Ă«shtĂ« ai. MĂ« duhet tĂ« ngre sĂ«rish tĂ« gjithĂ« tĂ« dhĂ«nat nga kjo nod dhe t'i filtroj ato. Dhe kjo Ă«shtĂ« e ngjashme me njĂ« Full Scan brenda nodit, e keqe.
Në çdo situatë të paqartë krijoni një tabelë të re
NĂ«se duam tĂ« kemi mundĂ«sinĂ« tĂ« marrim pĂ«rdoruesit sipas ID-sĂ«, moshĂ«s, ose pagĂ«s, çfarĂ« tĂ« bĂ«jmĂ«? AsgjĂ«. Thjesht pĂ«rdorni dy tabela. NĂ«se na duhet tĂ« marrim pĂ«rdoruesit me tre mĂ«nyra tĂ« ndryshme â do tĂ« ketĂ« tre tabela. Ka kaluar koha kur kursenim hapĂ«sirĂ« nĂ« disqet e ngurta. Kjo Ă«shtĂ« resurset mĂ« e lirĂ«. Kushton mĂ« shumĂ« se sa koha e pĂ«rgjigjes, e cila mund tĂ« jetĂ« shkatĂ«rruese pĂ«r pĂ«rdoruesin. PĂ«rdoruesit preferojnĂ« shumĂ« mĂ« tepĂ«r tĂ« marrin diçka brenda njĂ« sekunde, sesa brenda 10 minutash.
Ne këmbjmë hapësirën e tepruar të zënë, të dhënat e de-normalizuara me mundësinë për të shkallëzuar mirë, për të punuar me besueshmëri. Sepse, në të vërtetë, një klaster që përbëhet nga tre qendra të të dhënave, secila me pesë nodë, me një nivel të pranueshëm të ruajtjes së të dhënave (kur asgjë nuk humbet), është në gjendje të përballojë humbjen e një qendre të dhënash plotësisht. Dhe gjithashtu dy nodet në çdo një nga dy të mbetura. Dhe vetëm pas kësaj do të fillojnë problemet. Ky është një rezervim mjaft i mirë, që kushton disa SSD shtesë dhe procesorë. Prandaj, për të përdorur Cassandra-n, që nuk është SQL, e cila nuk ka marrëdhënie, çelësa të jashtëm, duhet të dini rregullat e thjeshta.
Ne projektojmë gjithçka nga kërkesa. Ajo që ka rëndësi nuk janë të dhënat, por mënyra se si aplikacioni do të punojë me to. Nëse aplikacioni ka nevojë të marrë të dhëna të ndryshme në mënyra të ndryshme ose të njëjtat të dhëna në mënyra të ndryshme, ne duhet t'i vendosim ato ashtu si do të ishte më e lehtë për aplikacionin. Ndryshe, do të bjerë në Full Scan dhe asnjë përfitim nga Cassandra nuk do të na ndihmojë.
Denormalizimi i të dhënave është norma. Harrojmë në lidhje me format normale, nuk kemi më baza të dhënash relacionale. Nëse vendosim diçka 100 herë, do të bëhet e pranishme 100 herë. Kjo është ende më e lirë se sa të ngadalësohemi.
Zgjidhim çelësat për ndarjen në mënyrë që ato të shpërndahen normalisht. Nuk na nevojitet që hash i çelësave të tanishëm të bjerë në një gamë të ngushtë. Pra, viti i lindjes në shembullin e mësipërm është një shembull i keq. Në të vërtetë, është i mirë nëse përdoruesit janë shpërndarë normalisht sipas vitit të lindjes, dhe i keq nëse bëhet fjalë për nxënësit e klasës së 5-të - atje nuk do të ishte shumë mirë për t'u ndarë.
Renditja zgjidhet një herë në fazën e krijimit të Clustering Key. Nëse ka nevojë të ndryshohet, do të duhet të ri-përdorim tabellën tonë me një çelës tjetër.
Dhe më e rëndësishmja: nëse kemi nevojë të marrim të njëjtat të dhëna në 100 mënyra të ndryshme, atëherë do të kemi 100 tabela të ndryshme.
Burimi: habr.com
