Sharding

All lectures for RO purposes
Nivel , Lecţie
Disponibil

1.1 Ce este sharding-ul?

Dacă căutați în mod constant pe google, se dovedește că există o graniță destul de neclară între așa-numita partiționare și așa-numita sharding. Fiecare numește cum vrea, cum vrea. Unii oameni fac distincția între partiționare orizontală și fragmentare. Alții spun că sharding-ul este un anumit tip de partiție orizontală.

Nu am găsit un singur standard terminologic care să fie aprobat de părinții fondatori și certificat de ISO. Convingerea interioară personală este cam așa: împărțirea înseamnă, în medie, „taierea bazei în bucăți” într-un mod arbitrar.

  • Compartimentare verticală - pe coloană. De exemplu, există un tabel uriaș cu câteva miliarde de înregistrări în 60 de coloane. În loc să păstrăm un astfel de tabel uriaș, păstrăm cel puțin 60 de tabele gigantice a câte 2 miliarde de înregistrări fiecare - și aceasta nu este o bază de coloană, ci o partiție verticală (ca exemplu de terminologie).
  • Partiționare orizontală - tăiem linie cu linie, poate în interiorul serverului.

Momentul ciudat aici este diferența subtilă dintre partiționarea orizontală și fragmentarea. Pot fi tăiat în bucăți, dar nu vă pot spune cu siguranță ce este. Există sentimentul că fragmentarea și partiționarea orizontală sunt aproximativ același lucru.

Sharding este, în general, atunci când un tabel mare în ceea ce privește bazele de date sau o pro-colecție de documente, obiecte, dacă nu ai o bază de date, ci un depozit de documente, este tăiat exact de obiecte. Adică, din 2 miliarde de obiecte, piesele sunt selectate indiferent de dimensiune. Obiectele în sine în interiorul fiecărui obiect nu sunt tăiate în bucăți, nu le așezăm în coloane separate, și anume, le așezăm în loturi în locuri diferite.

Există diferențe terminologice subtile. De exemplu, relativ vorbind, dezvoltatorii Postgres pot spune că partiționarea orizontală este atunci când toate tabelele în care este împărțit tabelul principal se află în aceeași schemă, iar atunci când sunt pe mașini diferite, aceasta este deja fragmentare.

Într-un sens general, fără a fi legat de terminologia unei anumite baze de date și a unui anumit sistem de gestionare a datelor, există sentimentul că sharding-ul este doar tăierea linie cu linie / document cu document și așa mai departe - asta e tot.

Subliniez tipic. În sensul că facem toate acestea nu doar pentru a tăia 2 miliarde de documente în 20 de tabele, fiecare dintre ele mai ușor de gestionat, ci pentru a le distribui pe mai multe nuclee, multe discuri sau mai multe servere fizice sau virtuale diferite.

1.2 Împărțiți indivizibilul

Se înțelege că facem acest lucru astfel încât fiecare fragment - fiecare bucată de date - să fie replicată de mai multe ori. Dar cu adevărat, nu.

INSERT INTO docs00 
SELECT * FROM documents WHERE (id%16)=0 
... 
 
INSERT INTO docs15 
SELECT * FROM documents WHERE (id%16)=15 

De fapt, dacă faci o astfel de tăiere de date și dintr-un tabel SQL gigant de pe MySQL pe laptop-ul tău curajos, vei genera 16 tabele mici, fără a depăși un singur laptop, nici o singură schemă, nici o singură bază de date etc. . și așa mai departe. - asta e, ai deja sharding.

Acest lucru are ca rezultat următoarele:

  • Lățimea de bandă crește.
  • Latența nu se schimbă, adică fiecare, ca să spunem așa, lucrător sau consumator în acest caz, își capătă a lui. Diferite solicitări sunt deservite aproximativ în același timp.
  • Sau ambele, și alta, și, de asemenea, disponibilitate ridicată (replicare).

De ce lățimea de bandă? Uneori putem avea astfel de volume de date care nu se potrivesc - nu este clar unde, dar nu se potrivesc - pe 1 {kernel | disc | server | ...}. Pur și simplu nu sunt suficiente resurse, asta-i tot. Pentru a lucra cu acest set mare de date, trebuie să-l tăiați.

De ce latență? Pe un nucleu, scanarea unui tabel de 2 miliarde de rânduri este de 20 de ori mai lentă decât scanarea a 20 de tabele pe 20 de nuclee, făcând-o în paralel. Datele sunt procesate prea lent pe o singură resursă.

De ce disponibilitate ridicată? Sau tăiem datele pentru a le face pe amândouă în același timp și, în același timp, mai multe copii ale fiecărui shard - replicarea asigură o disponibilitate ridicată.

1.3 Un exemplu simplu „cum se face manual”

Fragmentarea condiționată poate fi tăiată folosind tabelul de testare test.documents pentru 32 de documente și generând 16 tabele de testare din acest tabel, aproximativ 2 documente fiecare test.docs00, 01, 02, ..., 15.

INSERT INTO docs00 
SELECT * FROM documents WHERE (id%16)=0 
... 
 
INSERT INTO docs15 
SELECT * FROM documents WHERE (id%16)=15 

De ce despre? Pentru că a priori nu știm cum sunt distribuite id-urile, dacă de la 1 la 32 inclusiv, atunci vor fi exact 2 documente fiecare, altfel nu.

O facem aici de ce. După ce am făcut 16 mese, putem „prinde” 16 din ceea ce ne trebuie. Indiferent de ceea ce lovim, putem paraleliza aceste resurse. De exemplu, dacă nu există suficient spațiu pe disc, ar fi logic să descompunem aceste tabele pe discuri separate.

Toate acestea, din păcate, nu sunt gratuite. Bănuiesc că în cazul standardului SQL canonic (nu am recitit standardul SQL de mult timp, poate că nu a fost actualizat de mult timp), nu există o sintaxă standardizată oficială pentru a spune oricărui server SQL : „Dragă server SQL, fă-mi 32 de cioburi și împarte-le în 4 discuri. Dar în implementările individuale, există adesea o sintaxă specifică pentru a face practic același lucru. PostgreSQL are mecanisme de partiționare, MySQL are MariaDB, probabil că Oracle a făcut toate acestea cu mult timp în urmă.

Cu toate acestea, dacă o facem manual, fără suport pentru baze de date și în cadrul standardului, atunci plătim condiționat cu complexitatea accesului la date . Unde era un simplu SELECT * FROM documente WHERE id=123, acum 16 x SELECT * FROM docsXX. Și e bine dacă am încercat să obținem înregistrarea cu cheie. Mult mai interesant dacă am încerca să obținem o gamă timpurie de înregistrări. Acum (dacă, subliniez, suntem, așa cum spuneam, proști și rămânem în cadrul standardului), rezultatele acestor 16 SELECT * FROM vor trebui combinate în aplicație.

La ce schimbare de performanță vă puteți aștepta?

  • Intuitiv - liniar.
  • Teoretic - subliniar, deoarece legea Amdahl.
  • Practic, poate aproape liniar, poate nu.

De fapt, răspunsul corect este necunoscut. Cu o aplicare inteligentă a tehnicii sharding, puteți obține o degradare super-liniară semnificativă a performanței aplicației dvs. și chiar și DBA-ul va rula cu un poker aprins.

Să vedem cum se poate realiza acest lucru. Este clar că doar setarea la PostgreSQL shards=16 și apoi decolează de la sine, nu este interesantă. Să ne gândim cum ne putem asigura că încetinim de la sharding de 16 ori până la 32 - acest lucru este interesant din punctul de vedere al cum să nu facem acest lucru.

Încercările noastre de a accelera sau încetini întotdeauna se vor întâlni cu clasicii - vechea lege Amdahl, care spune că nu există o paralelizare perfectă a oricărei cereri, există întotdeauna o parte consistentă.

1.4 Legea Amdahl

Există întotdeauna o parte serializată.

Există întotdeauna o parte din execuția interogării care este paralelizată și întotdeauna există o parte care nu este paralelizată. Chiar dacă vi se pare că o interogare perfect paralelă, cel puțin colecția rândului rezultat pe care urmează să o trimiteți clientului din rândurile primite de la fiecare shard este întotdeauna acolo și este întotdeauna secvențială.

Există întotdeauna o parte consistentă. Poate fi mic, complet invizibil pe fondul general, poate fi gigantic și, în consecință, afectează puternic paralelizarea, dar există întotdeauna.

În plus, influența sa se schimbă și poate crește semnificativ, de exemplu, dacă ne tăiem masa - să creștem miza - de la 64 de înregistrări în 16 tabele de 4 înregistrări, această parte se va schimba. Desigur, judecând după cantități atât de gigantice de date, lucrăm la un telefon mobil și un procesor de 2 MHz 86 și nu avem suficiente fișiere care să poată fi păstrate deschise în același timp. Aparent, cu astfel de intrări, deschidem câte un fișier pe rând.

  • A fost Total = Serial + Paralel . Unde, de exemplu, paralel este toată munca din interiorul DB, iar serialul trimite rezultatul către client.
  • A devenit Total2 = Serial + Paralel/N + Xserial . De exemplu, când totalul ORDER BY, Xserial>0.

Cu acest exemplu simplu, încerc să arăt că apar unele Xserial. Pe lângă faptul că există întotdeauna o parte serializată și faptul că încercăm să lucrăm cu date în paralel, există o parte suplimentară pentru a oferi această tăiere a datelor. În linii mari, este posibil să avem nevoie de:

  • găsiți aceste 16 tabele în dicționarul intern al bazei de date;
  • deschide fișiere;
  • aloca memorie;
  • nealocarea memoriei;
  • îmbinarea rezultatelor;
  • sincroniza între nuclee.

Unele efecte nesincronizate apar în continuare. Ele pot fi nesemnificative și ocupă o miliardime din timpul total, dar sunt întotdeauna diferite de zero și mereu acolo. Cu ajutorul lor, putem pierde dramatic performanța după sharding.

Aceasta este o imagine standard despre legea lui Amdahl. Lucrul important aici este că liniile, care în mod ideal ar trebui să fie drepte și să crească liniar, se întâlnesc într-o asimptotă. Dar din moment ce graficul de pe internet este ilizibil, am făcut, după părerea mea, tabele mai vizuale cu cifre.

Să presupunem că avem o parte serializată a procesării cererii care durează doar 5%: serial = 0,05 = 1 / 20 .

Intuitiv, s-ar părea că cu o parte serializată care ia doar 1/20 din procesarea cererii, dacă paralelizăm procesarea cererii pentru 20 de nuclee, aceasta va deveni de aproximativ 20, în cel mai rău caz de 18 ori mai rapid.

De fapt, matematica este un lucru fără inimă :

perete = 0,05 + 0,95/num_core, accelerare = 1 / (0,05 + 0,95/num_core)

Se dovedește că dacă calculezi cu atenție, cu o parte serializată de 5%, accelerarea va fi de 10 ori (10,3), adică 51% față de idealul teoretic.

8 miezuri = 5,9 = 74%
10 nuclee = 6,9 = 69%
20 de nuclee = 10,3 = 51%
40 de nuclee = 13,6 = 34%
128 de nuclee = 17,4 = 14%

După ce am folosit 20 de nuclee (20 de discuri, dacă doriți) pentru sarcina la care obișnuia să lucreze, nu vom obține niciodată o accelerație teoretică de mai mult de 20 de ori, dar în practică - mult mai puțin. Mai mult, odată cu creșterea numărului de paralele, ineficiența crește foarte mult.

Când rămâne doar 1% din munca serializată și 99% este paralelizată, valorile de accelerare se îmbunătățesc oarecum:

8 miezuri = 7,5 = 93%
16 nuclee = 13,9 = 87%
32 de nuclee = 24,4 = 76%
64 de nuclee = 39,3 = 61%

Pentru o interogare perfect termonucleară, care în mod natural durează ore pentru a se finaliza, iar munca pregătitoare și asamblarea rezultatului durează foarte puțin timp (serial = 0,001), vom vedea deja o eficiență bună:

8 miezuri = 7,94 = 99%
16 nuclee = 15,76 = 99%
32 de nuclee = 31,04 = 97%
64 de nuclee = 60,20 = 94%

Vă rugăm să rețineți că nu vom vedea niciodată 100% . În cazuri deosebit de bune, puteți vedea, de exemplu, 99,999%, dar nu tocmai 100%.

Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION