modul greșit de a scala Cassandra DB atunci când indicii secundari sunt în loc
Cassandra este baza mea de date preferată (nu este gestionată) din mai multe motive: nu are un singur punct de eșec (SPoF), acceptă mai multe regiuni, este bună pentru operațiunile de citire și scriere, flexibilă în ceea ce privește nivelurile de consistență a citirii și scrierii, scalează liniar și nu este prea complexă pentru a fi gestionată pentru operațiunile de zi cu zi.
ca orice bază de date, ar trebui să utilizați Cassandra pe baza modelelor dvs. de acces la date, deci dacă aveți nevoie de o bază de date flexibilă pentru interogări ad-hoc sau suficient de adaptabilă pentru modificări constante ale modelului bazei de date, ar trebui să luați în considerare alte opțiuni.
Cassandra este un DB orientat pe coloane și este foarte puternic atunci când aveți interogările de date deja definite. Datastax, compania care susține Cassandra, vă recomandă să începeți prin proiectarea interogărilor dvs. și apoi modelul dvs. de date în Cassandra. În ciuda faptului că structura dvs. coloană, Cassandra acceptă multe structuri de date ca tip coloană(coloane), cum ar fi Hărți.
Cassandra este o bază de date cheie primară, ceea ce înseamnă că datele dvs. sunt persistate și organizate în jurul unui cluster bazat pe valoarea hash (cheia de partiție) a cheii primare. Pentru tabelele care au mai mult de un PK, Cassandra consideră doar prima parte a PK ca fiind cheia de partiție. Vedeți mai multe despre cheile compozite aici.
pentru a fi mai clar, să revenim la una dintre cele mai importante caracteristici ale unui Cassandra DB: este arhitectura și faptul că nu are un SPoF.
un cluster Cassandra este compus din noduri (3 sau mai multe) și aceste noduri împreună compune un inel de noduri:
fiecare nod dintr-un cluster Cassandra funcționează “independent”, dar noduri diferite ar putea stoca aceleași date în consecință configurația factorului de replicare (RF) configurată pentru cluster.
pentru a ști unde (care nod) sunt persistate datele dvs., Cassandra folosește valoarea hash (token) calculată printr-o funcție hash consistentă folosind coloana PK a unui tabel dat.
când executați o interogare, nodul coordonator (în mod normal, cel mai apropiat unul dintre instanțele de aplicare) va căuta ce noduri într-un inel au datele, în acest fel, în cazul în care un nod este în jos pentru un motiv oarecare, un alt nod ar putea servi datele (RF 2). Aceasta este magia unei abordări fără stăpân, în care fiecare nod dintr-un inel este egal în ceea ce privește citirea și scrierea.
acest concept despre PK și factorul de replicare este foarte important pentru a înțelege cum să vă scalați clusterul Cassandra atunci când aplicația dvs. este în condiții de încărcare ridicată.
indici secundari
Cassandra are și conceptul de indici secundari. În bazele de date relaționale, ați putea avea mulți indici într-un tabel dat, costul unui indice secundar este asociat cu operațiile de scriere, nu pentru operațiile de citire. În Cassandra acest lucru nu este adevărat.
indexurile secundare din Cassandra ar putea fi utile și tentante atunci când modelul dvs. de date s-a schimbat și trebuie să interogați pe baza unei coloane noi.
în acest fel, cu un index secundar, ai putea rula o interogare de genul asta:
selectați * din my_table unde SECONDARY_INDEX = ‘valoare’;
problema cu privire la utilizarea unui Index secundar
Imaginați-vă scenariul: vă aflați într-un Blackfriday/CyberMonday și clusterul Cassandra suferă de evenimente de vârf și trebuie să adăugați mai multe noduri pentru a vă scala baza de date, echilibrând mai bine traficul și… supraviețuind. Bine, nu?
în mod normal, este o situație normală într-o aplicație extrem de scalabilă. Dar ce se întâmplă dacă aplicația dvs. rulează interogări folosind un index secundar?
da, ai înțeles ideea.
vă amintiți când am spus că Cassandra distribuie date într-un inel folosind cheia de partiție? Acest lucru se întâmplă deja, dar problema este atunci când introduceți un index secundar în interogarea dvs. Indicii secundari nu fac parte dintr-o cheie de partiție, iar Cassandra știe unde trăiesc datele dvs. prin cheia de partiție. Când executați o interogare care utilizează acest tip de index, ceea ce face Cassandra este să caute fiecare nod din inelul dvs. încercând să vă satisfacă interogarea.
scenariu Real
în timpul unui Blackfriday, aplicațiile noastre au fost cu sarcini mari. Mulți și mulți clienți care doresc să beneficieze de reducerile uriașe oferite de un eveniment Blackfriday.
am aruncat o privire asupra APM-ului nostru și toată analiza ne-a condus la persistența noastră, în acest caz o Cassandra DB. Avem perioade lungi de latență, dar nu pentru fiecare cerere, doar pentru unii.
încercând să menținem lucrurile înapoi la starea normală, prima noastră manevră a fost să adăugăm mai multe noduri clusterului nostru Cassandra.
am adăugat și încă suferim de probleme de latență. Întrebarea era: de ce se mai întâmplă asta?
ne-am înșelat. A fost o concluzie simplistă și nu am avut grijă de un detaliu foarte important: acest comportament se întâmpla nu în toate cererile, ci în unele dintre ele.
dacă te-ai gândit la indicele secundar, bingo! Asta a fost exact problema.
adăugarea de noduri nu ar rezolva niciodată problema, deoarece problema nu a fost legată de toate interogările care sosesc în baza de date, problema a fost în unele și acestea au fost cele reale care au degradat performanța bazei de date. A fost o chestie cu Pareto.
detaliind problema și modul în care o atenuăm
cu un moment înainte de evenimentul Blackfriday, a trebuit să ne schimbăm modelul de date. Ne-am regionalizat aplicația și regiunea clientului a început să fie un lucru important pentru noi, trebuia să interogăm date pe baza unui produs sau a unei regiuni.
Privind înapoi și conectând punctele, am putut realiza că am fost foarte prețioși în ceea ce privește implementarea, deoarece am vrut să reflectăm acest nou comportament nu numai în stratul API (new query param), ci și în modul în care am accesat datele din Cassandra.
și de ce am fost atât de prețioși? Deoarece chiar și având în vedere că timpul nostru de interogare nu a crescut atât de mult, am făcut schimbarea.
această implementare nu numai că ne-a mărit timpul de interogare folosind un index secundar, dar a generat și mai multe probleme în funcție de extinderea infrastructurii Cassandrei. Pe măsură ce am adăugat mai multe noduri în cluster-ul nostru, a însemnat mai multe noduri pentru a căuta pentru a găsi datele, astfel problema a crescut exponențial.
pentru a atenua problema, ceea ce am făcut a fost să luăm înapoi Numărul de noduri pe care le-am avut anterior și să creștem factorul de replicare pentru majoritatea nodurilor noastre din cluster.
de asemenea, ne-am schimbat nivelul de consistență a citirii pentru a fi mai puțin consecvent. Am fost folosind * cvorum și în schimb ne-am schimbat la unul. Acest lucru ne-a ajutat să scădem sarcina în noduri.
pe măsură ce ne-am înghețat aplicațiile cu câteva zile înainte de eveniment, știam că nu avem date noi (operații de scriere) și datele vor fi consecvente în starea lor actuală.
zilele următoare și soluția modelului DB
ca parte a soluției finale, a trebuit să ne (re)gândim la modelul bazei noastre de date și să revenim la schimbările pe care le-am făcut ca o cale de atenuare în timpul evenimentului.
înainte de eveniment am folosit ID-ul produsului (PID)ca cheie de partiție, ceea ce a fost o decizie bună, deoarece PID are atribute bune pentru a fi un PK datorită naturii sale de a fi un număr secvențial (cardinalitate ridicată) și, în acest fel, răspândirea uniformă a datelor în jurul clusterului.
despre noul câmp “Regiune”, folosim Tipul de date Cassandra collections și am folosit o hartă pentru fiecare regiune ca coloană în tabelul nostru de produse.
indicii secundari sunt întotdeauna o idee proastă?
răspunsul scurt este nu.
explicând un pic mai bine, există două tipuri de indici în Cassandra: indici locali și globali.
un index local, așa cum spune și numele, este un fel de index care există doar local, adică într-un nod. Când creați un index secundar, Cassandra creează un nou tabel (ascuns) în care secundarul devine o cheie primară în acest tabel. Vizibilitatea acestui nou tabel este în termeni de un nod, nu un inel (cluster). Acesta este cazul indicilor secundari.
pe de altă parte, un index Global are vizibilitatea inelului prin cheia de partiție, astfel încât Cassandra știe unde se află datele într-un inel prin acea cheie de partiție.
indicii secundari ar putea fi o alternativă, atunci când aveți într-o interogare atât: indici primari, cât și secundari. În acest caz, Cassandra știe unde se află datele dvs. (care NOD) prin cheia partiției și apoi caută tabelul local din nodul care se referă la indicii secundari (locali).
există și alte nuanțe despre indicii secundari care sunt foarte bine explicați aici, dar cea mai bună practică este să le evitați denormalizând modelul dvs. de date.