{"id":37956,"date":"2019-10-31T22:20:47","date_gmt":"2019-10-31T19:20:47","guid":{"rendered":"https:\/\/prohoster.info\/blog\/newsql-nosql-acid\/"},"modified":"2019-10-31T22:20:47","modified_gmt":"2019-10-31T19:20:47","slug":"newsql-nosql-acid","status":"publish","type":"post","link":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/newsql-nosql-acid","title":{"rendered":"NewSQL = NoSQL+ACID","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/973145efa566bca10ffe7ba95733a1d5.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nP\u00e2n\u0103 de cur\u00e2nd, \u00een Odnoklassniki se aflau aproximativ 50 TB de date, prelucrate \u00een timp real, stocate \u00een SQL Server. Asigurarea unui acces rapid, de \u00eencredere \u0219i rezistent la defecte la un astfel de volum de date, utiliz\u00e2nd o baz\u0103 de date SQL, este practic imposibil\u0103. De obicei, \u00een aceste cazuri se folose\u0219te una dintre solu\u021biile NoSQL, \u00eens\u0103 nu totul poate fi transferat \u00een NoSQL: unele entit\u0103\u021bi necesit\u0103 garan\u021bii pentru tranzac\u021bii ACID. <\/p>\n<p>Acest lucru ne-a dus la utilizarea unei solu\u021bii de tip NewSQL, adic\u0103 a unui sistem de gestionare a bazelor de date care ofer\u0103 rezisten\u021b\u0103 la defec\u021biuni, scalabilitate \u0219i rapiditate, asem\u0103n\u0103toare sistemelor NoSQL, dar p\u0103str\u00e2nd \u00een acela\u0219i timp garan\u021biile ACID cunoscute din sistemele clasice. Exist\u0103 pu\u021bine sisteme industriale func\u021bionale din aceast\u0103 nou\u0103 categorie, a\u0219a c\u0103 am implementat noi un astfel de sistem \u0219i l-am pus \u00een exploatare industrial\u0103. <\/p>\n<p>Cum func\u021bioneaz\u0103 \u0219i ce am realizat \u2014 citeste mai departe.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nAst\u0103zi, audien\u021ba lunar\u0103 a 'Odnoklassniki' dep\u0103\u0219e\u0219te 70 de milioane de vizitatori unici. Noi <noindex><a rel=\"nofollow\" href=\"https:\/\/www.similarweb.com\/top-websites\/category\/internet-and-telecom\/social-network\">ne afl\u0103m \u00een top cinci<\/a><\/noindex> cele mai mari re\u021bele sociale din lume \u0219i \u00een top dou\u0103zeci de site-uri pe care utilizatorii \u00ee\u0219i petrec cel mai mult timp. Infrastructura 'OK' gestioneaz\u0103 sarcini foarte mari: peste un milion de solicit\u0103ri HTTP pe secund\u0103 pe fronturi. P\u0103r\u021bile parcului de servere, \u00een num\u0103r de peste 8000, sunt amplasate aproape una de cealalt\u0103 - \u00een patru centre de date din Moscova, ceea ce permite s\u0103 se asigure o \u00eent\u00e2rziere de re\u021bea de mai pu\u021bin de 1 ms \u00eentre ele.<\/p>\n<p>Folosim Cassandra din 2010, \u00eencep\u00e2nd cu versiunea 0.6. Ast\u0103zi, avem \u00een exploatare c\u00e2teva zeci de clustere. Cel mai rapid cluster gestioneaz\u0103 peste 4 milioane de opera\u021bii pe secund\u0103, iar cel mai mare stocheaz\u0103 260 TB. <\/p>\n<p>Totu\u0219i, toate acestea sunt clustere NoSQL obi\u0219nuite, folosite pentru stocarea <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%BE%D0%B3%D0%BB%D0%B0%D1%81%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D1%8C_%D0%B2_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D0%BC_%D1%81%D1%87%D1%91%D1%82%D0%B5\">datelor slab consistente.<\/a><\/noindex> Noi am dorit s\u0103 \u00eenlocuim principala solu\u021bie de stocare consistent\u0103, Microsoft SQL Server, care a fost utilizat\u0103 \u00eenc\u0103 de la \u00eenfiin\u021barea 'Odnoklassniki'. Stocarea consta \u00een peste 300 de servere SQL Server Standard Edition, care con\u021bineau 50 TB de date - entit\u0103\u021bi de afaceri. Aceste date sunt modificate \u00een cadrul tranzac\u021biilor ACID \u0219i necesit\u0103 <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">\u00eenalt\u0103 consisten\u021b\u0103.<\/a><\/noindex>.<\/p>\n<p>Pentru distribuirea datelor pe noduri SQL Server, am folosit at\u00e2t partitonare vertical\u0103, c\u00e2t \u0219i orizontal\u0103. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Partition_(database)\">parti\u021bionare.<\/a><\/noindex> (sharding). Din punct de vedere istoric, am utilizat un sistem simplu de sharding al datelor: fiec\u0103rei entit\u0103\u021bi \u00eei era asociat un token \u2014 o func\u021bie pe baza ID-ului entit\u0103\u021bii. Entit\u0103\u021bile cu acela\u0219i token erau plasate pe un singur server SQL. Rela\u021bia de tip master-detail era implementat\u0103 astfel \u00eenc\u00e2t token-urile \u00eenregistr\u0103rii principale \u0219i celei derivate s\u0103 coincid\u0103 \u00eentotdeauna \u0219i s\u0103 se afle pe acela\u0219i server. \u00cen re\u021belele sociale, aproape toate \u00eenregistr\u0103rile sunt generate \u00een numele utilizatorului \u2014 ceea ce \u00eenseamn\u0103 c\u0103 toate datele utilizatorului din cadrul unei subsisteme func\u021bionale erau stocate pe un singur server. A\u0219adar, \u00een cadrul unei tranzac\u021bii de afaceri, aproape \u00eentotdeauna erau implicate tabele de pe un singur server SQL, ceea ce permitea asigurarea coeren\u021bei datelor prin tranzac\u021bii ACID locale, f\u0103r\u0103 a fi necesar\u0103 utilizarea <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">tranzac\u021biilor ACID<\/a><\/noindex> distribuite.<\/p>\n<p>Datorit\u0103 sharding-ului \u0219i pentru a accelera func\u021bionarea SQL:<\/p>\n<ul>\n<li>Nu folosim constr\u00e2ngeri Foreign key, deoarece \u00een cazul sharding-ului ID-ul entit\u0103\u021bii ar putea fi pe un alt server.<\/li>\n<li>Nu folosim proceduri stocate \u0219i trigger-e din cauza \u00eenc\u0103rc\u0103rii suplimentare pe CPU-ul SGBD.<\/li>\n<li>Nu folosim JOIN-uri din cauza tuturor celor men\u021bionate mai sus \u0219i a num\u0103rului mare de citiri aleatorii de pe disc.<\/li>\n<li>\u00cen afara tranzac\u021biei, pentru a reduce bloc\u0103rile, utiliz\u0103m nivelul de izolare Read Uncommitted.<\/li>\n<li>Execut\u0103m doar tranzac\u021bii scurte (\u00een medie sub 100 ms).<\/li>\n<li>Nu folosim UPDATE-uri \u0219i DELETE-uri multi-row din cauza num\u0103rului mare de bloc\u0103ri \u2014 actualiz\u0103m doar c\u00e2te o \u00eenregistrare.<\/li>\n<li>Execut\u0103m \u00eentotdeauna interog\u0103rile doar pe indec\u0219i \u2014 interogarea cu un plan de vizualizare complet\u0103 a tabelului pentru noi \u00eenseamn\u0103 o suprasarcin\u0103 a DB-ului \u0219i e\u0219ecul acestuia.<\/li>\n<\/ul>\n<p>\nAce\u0219ti pa\u0219i ne-au permis s\u0103 extragem aproape maximum de performan\u021b\u0103 din serverele SQL. Totu\u0219i, problemele deveneau din ce \u00een ce mai multe. S\u0103 le examin\u0103m.<\/p>\n<h2>Probleme cu SQL<\/h2>\n<p><\/p>\n<ul>\n<li>Deoarece am folosit sharding personalizat, ad\u0103ugarea de noi shardi era realizat\u0103 manual de c\u0103tre administratori. \u00cen tot acest timp, replicile scalabile ale datelor nu deservesc solicit\u0103rile. <\/li>\n<li>Pe m\u0103sur\u0103 ce num\u0103rul de \u00eenregistr\u0103ri din tabel cre\u0219te, viteza inser\u021biei \u0219i modific\u0103rii scade; ad\u0103ugarea de indec\u0219i pe un tabel existent reduce drastic viteza, iar crearea \u0219i recrearea indec\u0219ilor se face cu timp de nefunc\u021bionare.<\/li>\n<li>Prezen\u021ba unui num\u0103r mic de Windows pentru SQL Server \u00een produc\u021bie \u00eengreuneaz\u0103 gestionarea infrastructurii.<\/li>\n<\/ul>\n<p>\n\u00cens\u0103 problema principal\u0103 este \u2014 <\/p>\n<h2>Redundan\u021b\u0103<\/h2>\n<p>\nServerele SQL clasice au o disponibilitate slab\u0103. S\u0103 presupunem c\u0103 ave\u021bi un singur server de baze de date, iar acesta se defecteaz\u0103 o dat\u0103 la trei ani. \u00cen aceast\u0103 perioad\u0103, site-ul nu func\u021bioneaz\u0103 timp de 20 de minute, ceea ce poate fi acceptabil. Dac\u0103 ave\u021bi 64 de servere, site-ul nu func\u021bioneaz\u0103 deja o dat\u0103 la fiecare trei s\u0103pt\u0103m\u00e2ni. \u0218i dac\u0103 ave\u021bi 200 de servere, site-ul nu func\u021bioneaz\u0103 s\u0103pt\u0103m\u00e2nal. Aceasta este o problem\u0103. <\/p>\n<p>Ce se poate face pentru a \u00eembun\u0103t\u0103\u021bi disponibilitatea serverului SQL? Wikipedia ne sugereaz\u0103 s\u0103 construim <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/High-availability_cluster\">un cluster de \u00eenalt\u0103 disponibilitate<\/a><\/noindex>: unde, \u00een cazul defect\u0103rii oric\u0103rei componente, exist\u0103 o copie redundant\u0103.<\/p>\n<p>Aceasta necesit\u0103 un parc costisitor de echipamente: numeroase duplic\u0103ri, fibr\u0103 optic\u0103, stocare partajat\u0103, iar activarea rezervelor func\u021bioneaz\u0103 nesigur: aproximativ 10% din activ\u0103ri se termin\u0103 cu e\u0219ecul nodului de rezerv\u0103 urm\u00e2nd nodul principal. <\/p>\n<p>Dar principalul dezavantaj al unui astfel de cluster de \u00eenalt\u0103 disponibilitate este lipsa total\u0103 de accesibilitate \u00een cazul defect\u0103rii centrului de date \u00een care se afl\u0103. \u201eOdnoklassniki\u201d are patru centre de date, iar noi trebuie s\u0103 asigur\u0103m func\u021bionarea \u00een caz de avarie total\u0103 \u00een unul dintre ele.<\/p>\n<p>Pentru aceasta, am putea aplica <noindex><a rel=\"nofollow\" href=\"https:\/\/technet.microsoft.com\/en-us\/library\/ms151196.aspx\">replicarea Multi-Master<\/a><\/noindex> , integrat\u0103 \u00een SQL Server. Aceast\u0103 solu\u021bie este mult mai costisitoare din cauza pre\u021bului software-ului \u0219i sufer\u0103 de probleme bine cunoscute cu replicarea \u2014 \u00eent\u00e2rzieri imprevizibile ale tranzac\u021biilor \u00een cazul replic\u0103rii sincronice \u0219i \u00eent\u00e2rzieri \u00een aplicarea replic\u0103rilor (\u0219i, ca urmare, modific\u0103ri pierdute) \u00een cazul celei asincrone. R\u0103spunsul implicit la aceste probleme <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/replication\/transactional\/peer-to-peer-conflict-detection-in-peer-to-peer-replication\">rezolvarea manual\u0103 a conflictelor<\/a><\/noindex> face ca aceast\u0103 variant\u0103 s\u0103 fie complet neaplicabil\u0103 pentru noi.<\/p>\n<p>Toate aceste probleme necesitau o solu\u021bie radical\u0103 \u0219i am \u00eenceput analiza detaliat\u0103 a lor. Aici trebuie s\u0103 ne familiariz\u0103m cu ceea ce face, \u00een principal, SQL Server \u2014 tranzac\u021biile.<\/p>\n<h2>O tranzac\u021bie simpl\u0103<\/h2>\n<p>\nS\u0103 examin\u0103m cea mai simpl\u0103 tranzac\u021bie, din perspectiva unui programator SQL aplicativ: ad\u0103ugarea unei fotografii \u00eentr-un album. Albumele \u0219i fotografiile sunt stocate \u00een tabele diferite. Albumul are un contor al fotografiilor publice. Atunci, aceast\u0103 tranzac\u021bie se \u00eemparte \u00een urm\u0103torii pa\u0219i: <\/p>\n<ol>\n<li>Blok\u0103m albumul pe cheie.<\/li>\n<li>Cre\u0103m o \u00eenregistrare \u00een tabelul fotografiilor. <\/li>\n<li>Dac\u0103 fotografia are statut public, atunci increment\u0103m contorul fotografiilor publice din album, actualiz\u0103m \u00eenregistrarea \u0219i facem commit tranzac\u021biei.<\/li>\n<\/ol>\n<p>\nSau sub form\u0103 de pseudocod:<\/p>\n<pre><code>TX.start(\"Albums\", id);\nAlbum album = albums.lock(id);\nPhoto photo = photos.create(\u2026);\n\nif (photo.status == PUBLIC ) {\n    album.incPublicPhotosCount();\n}\nalbum.update();\n\nTX.commit();<\/code><\/pre>\n<p>\nObserv\u0103m c\u0103 cel mai frecvent scenariu pentru o tranzac\u021bie de afaceri este citirea datelor din baza de date \u00een memoria serverului de aplica\u021bii, modificarea acestora \u0219i salvarea noilor valori \u00eenapoi \u00een baza de date. De obicei, \u00een cadrul unei astfel de tranzac\u021bii, actualiz\u0103m mai multe entit\u0103\u021bi, mai multe tabele. <\/p>\n<p>La executarea unei tranzac\u021bii, poate ap\u0103rea modificarea concurent\u0103 a acelora\u0219i date dintr-un alt sistem. De exemplu, un sistem anti-spam poate decide c\u0103 un utilizator este suspect \u0219i, prin urmare, toate fotografiile acestui utilizator nu mai trebuie s\u0103 fie publice, trebuie trimise pentru moderare, ceea ce \u00eenseamn\u0103 s\u0103 schimb\u0103m photo.status \u00eentr-o alt\u0103 valoare \u0219i s\u0103 ajust\u0103m contorii corespunz\u0103tori. Este evident c\u0103, dac\u0103 aceast\u0103 opera\u021bie va fi efectuat\u0103 f\u0103r\u0103 garan\u021bii de atomicitate \u0219i de izolare a modific\u0103rilor concurente, ca \u00een <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/ACID\">ACID<\/a><\/noindex>, rezultatul nu va fi cel necesar \u2014 ori contorul fotografiilor va ar\u0103ta o valoare gre\u0219it\u0103, ori nu toate fotografiile vor fi trimise pentru moderare. <\/p>\n<p>O astfel de logic\u0103, care manipuleaz\u0103 diverse entit\u0103\u021bi de afaceri \u00eentr-o singur\u0103 tranzac\u021bie, a fost scris\u0103 de foarte multe ori pe parcursul existen\u021bei Odnoklassniki. Din experien\u021ba migra\u021biilor c\u0103tre NoSQL cu <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">Consisten\u021b\u0103 Temporar\u0103<\/a><\/noindex> \u0219tim c\u0103 cele mai mari dificult\u0103\u021bi (\u0219i pierderi de timp) sunt cauzate de necesitatea de a dezvolta cod menit s\u0103 men\u021bin\u0103 consisten\u021ba datelor. Prin urmare, cerin\u021ba principal\u0103 pentru noul magazin de date a fost asigurarea pentru logica aplica\u021biei a tranzac\u021biilor ACID reale. <\/p>\n<p>Alte cerin\u021be, la fel de importante, au fost:<\/p>\n<ul>\n<li>\u00cen cazul unei defec\u021biuni a centrului de date, at\u00e2t citirea, c\u00e2t \u0219i scrierea \u00een noul magazin trebuie s\u0103 fie disponibile.<\/li>\n<li>Men\u021binerea vitezei actuale de dezvoltare. Cu alte cuvinte, lucr\u00e2nd cu noul magazin, cantitatea de cod ar trebui s\u0103 fie aproximativ aceea\u0219i, f\u0103r\u0103 necesitatea de a ad\u0103uga ceva \u00een magazin, de a dezvolta algoritmi pentru rezolvarea conflictelor, de a men\u021bine indec\u0219i secundari etc. <\/li>\n<li>Viteza de operare a noului magazin de date trebuie s\u0103 fie suficient de mare at\u00e2t pentru citirea datelor, c\u00e2t \u0219i pentru procesarea tranzac\u021biilor, ceea ce \u00eenseamn\u0103 \u00een mod eficient inadecvarea solu\u021biilor academice riguroase, universale, dar lent eficiente, precum <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Two-phase_commit_protocol\">commit-uri \u00een dou\u0103 faze<\/a><\/noindex>.<\/li>\n<li>Scalare automatizat\u0103 \u00een timp real. <\/li>\n<li>Folosirea serverelor obi\u0219nuite \u0219i ieftine, f\u0103r\u0103 a fi necesar\u0103 achizi\u021bia de echipamente exotice. <\/li>\n<li>Posibilitatea de a dezvolta stocarea cu ajutorul echipei de dezvoltare a companiei. Cu alte cuvinte, prioritatea era dat\u0103 solu\u021biilor interne sau bazate pe cod deschis, de preferat pe Java.<\/li>\n<\/ul>\n<p><\/p>\n<h2>Solu\u021bii, solu\u021bii<\/h2>\n<p>\nAnaliz\u00e2nd posibilit\u0103\u021bile, am ajuns la dou\u0103 op\u021biuni de arhitectur\u0103:<\/p>\n<p>Prima op\u021biune este s\u0103 lu\u0103m orice server SQL \u0219i s\u0103 implement\u0103m fiabilitatea necesar\u0103, mecanismul de scalare, un cluster rezistent la erori, rezolvarea conflictelor \u0219i tranzac\u021bii ACID distribuite, fiabile \u0219i rapide. Am evaluat aceast\u0103 op\u021biune ca fiind destul de dificil\u0103 \u0219i consumatoare de timp.<\/p>\n<p>A doua op\u021biune este s\u0103 folosim un stocaj NoSQL gata preg\u0103tit, cu scalare implementat\u0103, un cluster rezistent la erori, rezolvarea conflictelor \u0219i s\u0103 implement\u0103m tranzac\u021bii \u0219i SQL noi. La prima vedere, chiar \u0219i sarcina de a implementa SQL, s\u0103 nu mai vorbim de tranzac\u021biile ACID, pare un proiect de lung\u0103 durat\u0103. Dar apoi am realizat c\u0103 setul de func\u021bionalit\u0103\u021bi SQL pe care \u00eel utiliz\u0103m \u00een practic\u0103 este departe de ANSI SQL la fel de departe precum <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Apache_Cassandra#Cassandra_Query_Language\">Cassandra CQL<\/a><\/noindex> departe de ANSI SQL. Privind mai atent CQL, am realizat c\u0103 este destul de apropiat de ceea ce avem nevoie.<\/p>\n<h2>Cassandra \u0219i CQL<\/h2>\n<p>\nA\u0219adar, ce g\u0103sim interesant la Cassandra, ce caracteristici are?<\/p>\n<p>\u00cen primul r\u00e2nd, este posibil s\u0103 cre\u0103m tabele care suport\u0103 diverse tipuri de date, iar SELECT sau UPDATE pot fi realizate pe baza cheii primare.<\/p>\n<pre><code>CREATE TABLE photos (id bigint KEY, owner bigint,\u2026);\nSELECT * FROM photos WHERE id=?;\nUPDATE photos SET \u2026 WHERE id=?;<\/code><\/pre>\n<p>\nPentru a asigura consisten\u021ba datelor replicilor, Cassandra folose\u0219te <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Quorum_(distributed_computing)\">metodologia de quorum<\/a><\/noindex>. \u00cen cel mai simplu caz, aceasta \u00eenseamn\u0103 c\u0103, atunci c\u00e2nd se plaseaz\u0103 trei replici ale acelea\u0219i \u00eenregistr\u0103ri pe noduri diferite ale unui cluster, \u00eenregistrarea este considerat\u0103 reu\u0219it\u0103 dac\u0103 majoritatea nodurilor (adic\u0103 dou\u0103 din trei) confirm\u0103 succesul acestei opera\u021biuni de scriere. Datele \u00eenregistr\u0103rii sunt considerate consistente dac\u0103, la citire, majoritatea nodurilor au fost interogate \u0219i au confirmat acestea. Astfel, cu trei replici disponibile, se garanteaz\u0103 consisten\u021ba complet\u0103 \u0219i instantanee a datelor \u00een cazul defect\u0103rii unei noduri. Aceast\u0103 abordare ne-a permis s\u0103 implement\u0103m un schema \u0219i mai fiabil\u0103: s\u0103 trimitem \u00eentotdeauna cereri c\u0103tre toate cele trei replici, a\u0219tept\u00e2nd r\u0103spuns de la cele dou\u0103 cele mai rapide. R\u0103spunsul \u00eent\u00e2rziat al celei de-a treia replici este astfel ignorat. Nodul care \u00eent\u00e2rz\u00eeie la r\u0103spuns poate avea probleme grave \u2014 bloc\u0103ri, colectare de gunoi \u00een JVM, recuperare de memorie direct\u0103 \u00een nucleul Linux, defec\u021biuni hardware, deconectare de la re\u021bea. Cu toate acestea, acest lucru nu afecteaz\u0103 opera\u021bia clientului \u0219i datele.<\/p>\n<p>Abordarea \u00een care ne adres\u0103m la trei noduri, dar primim r\u0103spuns de la dou\u0103, se nume\u0219te <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Speculative_execution\">specula\u021bie<\/a><\/noindex>: cererea pentru replicile \u00een plus este trimis\u0103 \u00eenainte de a 'c\u0103dea'. <\/p>\n<p>Un alt avantaj al Cassandra este Batchlog \u2014 un mecanism care garanteaz\u0103 fie aplicarea complet\u0103, fie ner\u0103spunderea complet\u0103 a setului de modific\u0103ri aduse de dvs. Aceasta ne permite s\u0103 solu\u021bion\u0103m A \u00een ACID \u2014 atomicitatea din cutie.<\/p>\n<p>Cel mai apropiat de tranzac\u021biile \u00een Cassandra este a\u0219a-numita &#171;<noindex><a rel=\"nofollow\" href=\"https:\/\/docs.datastax.com\/en\/cql\/3.3\/cql\/cql_using\/useInsertLWT.html\">tranzac\u021bii u\u0219oare<\/a><\/noindex>&#171;. Dar de tranzac\u021biile ACID \u201eadev\u0103rate\u201d sunt departe: de fapt, aceasta este o posibilitate de a face <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Compare-and-swap\">CAS<\/a><\/noindex> pe datele unei singure \u00eenregistr\u0103ri, folosind consensul printr-un protocol greu de Paxos. Prin urmare, viteza acestor tranzac\u021bii nu este mare. <\/p>\n<h2>Ce ne-a lipsit \u00een Cassandra<\/h2>\n<p>\nDeci, ne-a fost necesar s\u0103 implement\u0103m tranzac\u021bii reale ACID \u00een Cassandra. Cu ajutorul c\u0103rora am putea implementa cu u\u0219urin\u021b\u0103 dou\u0103 alte func\u021bionalit\u0103\u021bi utile ale DBMS tradi\u021bionale: indici consisten\u021bi rapizi, ceea ce ne-ar permite s\u0103 efectu\u0103m interog\u0103ri de date nu doar dup\u0103 cheia primar\u0103 \u0219i un generator obi\u0219nuit de ID-uri monotone autonumerotate.<\/p>\n<h4>C*One<\/h4>\n<p>\nAstfel s-a n\u0103scut noua baz\u0103 de date <b>C*One<\/b>, format\u0103 din trei tipuri de noduri server:<\/p>\n<ul>\n<li>Stocarea \u2014 servere Cassandra (aproape) standard, responsabile pentru stocarea datelor pe discurile locale. Pe m\u0103sur\u0103 ce sarcina \u0219i volumul de date cresc, num\u0103rul lor poate fi u\u0219or scalat p\u00e2n\u0103 la zeci \u0219i sute.<\/li>\n<li>Coordonatorii de tranzac\u021bii \u2014 asigur\u0103 execu\u021bia tranzac\u021biilor. <\/li>\n<li>Clien\u021bii \u2014 serverele de aplica\u021bii care implementeaz\u0103 opera\u021biuni de afaceri \u0219i ini\u021biaz\u0103 tranzac\u021bii. Ace\u0219ti clien\u021bi pot fi mii.<\/li>\n<\/ul>\n<p>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/f3809d89404ae596c62b642dbdb9e431.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nServerele de toate tipurile fac parte dintr-un cluster comun, folosesc un protocol intern de mesagerie Cassandra pentru a comunica \u00eentre ele \u0219i <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Gossip_protocol\">protocolul gossip.<\/a><\/noindex> pentru a schimba informa\u021bii despre cluster. Prin intermediul Heartbeat, serverele \u00ee\u0219i notific\u0103 reciproc despre defec\u021biuni, men\u021bin o schem\u0103 unitar\u0103 de date \u2014 tabele, structura lor \u0219i replicarea; schema de parti\u021bionare, topologia clusterului, etc.<\/p>\n<h4>Clien\u021bi<\/h4>\n<p>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/80f83358f215cf59db3b657ba1083420.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\n\u00cen locul driverelor standard se folose\u0219te modul Fat Client. Aceast\u0103 nod\u0103 nu stocheaz\u0103 date, dar poate ac\u021biona ca un coordonator al execu\u021biei cererilor, adic\u0103 Clientul \u00ee\u0219i \u00eendepline\u0219te singur func\u021bia de coordonator pentru cererile sale: interogheaz\u0103 replicile stoc\u0103rii \u0219i rezolv\u0103 conflictele. Acest lucru este nu doar mai fiabil \u0219i mai rapid dec\u00e2t un driver standard, care necesit\u0103 comunicarea cu un coordonator extern, ci permite \u0219i gestionarea transmiterii cererilor. \u00cen afara unei tranzac\u021bii deschise pe client, cererile sunt direc\u021bionate c\u0103tre stoc\u0103ri. Dac\u0103 clientul a deschis o tranzac\u021bie, atunci toate cererile din cadrul tranzac\u021biei sunt direc\u021bionate c\u0103tre coordonatorii de tranzac\u021bii.<br \/>\n<img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/709bf227eeb8f71abeff703a72780efd.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<\/p>\n<h2>Coordonatorul tranzac\u021biilor C*One<\/h2>\n<p>\nCoordonatorul \u2014 ceea ce am implementat pentru C*One de la zero. El se ocup\u0103 cu gestionarea tranzac\u021biilor, blocajelor \u0219i ordinii de aplicare a tranzac\u021biilor.<\/p>\n<p>Pentru fiecare tranzac\u021bie deservit\u0103, coordonatorul genereaz\u0103 un timestamp: fiecare urm\u0103toare este mai mare dec\u00e2t cea a tranzac\u021biei anterioare. Deoarece \u00een Cassandra sistemul de rezolvare a conflictelor se bazeaz\u0103 pe timestamp-uri (dintre dou\u0103 \u00eenregistr\u0103ri conflictuale, cea cu timestamp-ul mai recent este considerat\u0103 actual\u0103), conflictul va fi \u00eentotdeauna rezolvat \u00een favoarea tranzac\u021biei urm\u0103toare. Astfel am implementat <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A7%D0%B0%D1%81%D1%8B_%D0%9B%D1%8D%D0%BC%D0%BF%D0%BE%D1%80%D1%82%D0%B0\">ceasurile Lampa<\/a><\/noindex> \u2014 o metod\u0103 ieftin\u0103 de rezolvare a conflictelor \u00eentr-un sistem distribuit.<\/p>\n<h2>Blocajele<\/h2>\n<p>\nPentru a asigura izola\u021bia am ales s\u0103 folosim cea mai simpl\u0103 metod\u0103 \u2014 blocaje pesimiste pe cheia primar\u0103 a \u00eenregistr\u0103rii. Cu alte cuvinte, \u00een cadrul tranzac\u021biei, \u00eenregistrarea trebuie mai \u00eent\u00e2i blocat\u0103, apoi citit\u0103, modificat\u0103 \u0219i salvat\u0103. Numai dup\u0103 un commit de succes, \u00eenregistrarea poate fi deblocat\u0103, pentru ca tranzac\u021biile concurente s\u0103 o poat\u0103 utiliza.<\/p>\n<p>Implementarea unei astfel de bloc\u0103ri este simpl\u0103 \u00eentr-un mediu nedistribuit. \u00centr-un sistem distribuit, exist\u0103 dou\u0103 abord\u0103ri principale: fie se implementeaz\u0103 o blocare distribuit\u0103 \u00een cluster, fie se distribuie tranzac\u021biile astfel \u00eenc\u00e2t tranzac\u021biile care implic\u0103 o \u00eenregistrare s\u0103 fie \u00eentotdeauna gestionate de acela\u0219i coordonator.<\/p>\n<p>\u00cen cazul nostru, datele sunt deja distribuite \u00een grupuri de tranzac\u021bii locale \u00een SQL, a\u0219a c\u0103 s-a decis atribuirea coordonatorilor grupurilor de tranzac\u021bii locale: un coordonator efectueaz\u0103 toate tranzac\u021biile cu tokenul de la 0 la 9, al doilea - cu tokenul de la 10 la 19, \u0219i a\u0219a mai departe. Drept urmare, fiecare dintre instan\u021bele coordonatorului devine liderul grupului de tranzac\u021bii. <\/p>\n<p>Atunci, bloc\u0103rile pot fi implementate sub forma unui simplu HashMap \u00een memoria coordonatorului.<\/p>\n<h2>Defec\u021biuni ale coordonatorilor<\/h2>\n<p>\nDeoarece un singur coordonator gestioneaz\u0103 exclusiv un grup de tranzac\u021bii, este foarte important s\u0103 se determine rapid dac\u0103 acesta a picat, pentru a asigura reluarea \u00eencerc\u0103rii de execu\u021bie a tranzac\u021biei \u00een termenul limit\u0103. Pentru a realiza acest lucru rapid \u0219i fiabil, am aplicat un protocol de tip heartbeat cu quorum legat pe toate nodurile:<\/p>\n<p>\u00cen fiecare centru de date sunt amplasate minimum dou\u0103 noduri de coordonator. Perioada de timp, fiecare coordonator trimite un mesaj heartbeat celorlal\u021bi coordonatori, inform\u00e2ndu-i despre func\u021bionarea sa \u0219i despre mesajele heartbeat pe care le-a primit ultima dat\u0103 de la ceilal\u021bi coordonatori din cluster. <\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/5c337e53815cad0a2071b160d98d42fa.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPrimind informa\u021bii similare de la ceilal\u021bi \u00een mesajele lor heartbeat, fiecare coordonator decide pentru sine care noduri din cluster func\u021bioneaz\u0103 \u0219i care nu, baz\u00e2ndu-se pe principiul quorum-ului: dac\u0103 nodul X a primit de la majoritatea nodurilor din cluster informa\u021bia c\u0103 a primit mesajele de la nodul Y, \u00eenseamn\u0103 c\u0103 Y func\u021bioneaz\u0103. \u0218i invers, imediat ce majoritatea raporteaz\u0103 c\u0103 nu mai primesc mesaje de la nodul Y, \u00eenseamn\u0103 c\u0103 Y a e\u0219uat. Este interesant c\u0103, dac\u0103 quorum-ul informeaz\u0103 nodul X c\u0103 nu mai prime\u0219te mesaje de la el, \u00eenseamn\u0103 c\u0103 \u00eensu\u0219i nodul X se va considera c\u0103 a picat.<\/p>\n<p>Mesajele heartbeat sunt trimise cu o frecven\u021b\u0103 mare, aproximativ de 20 de ori pe secund\u0103, cu un interval de 50 ms. \u00cen Java este greu s\u0103 garant\u0103m un timp de r\u0103spuns al aplica\u021biei de 50 ms din cauza pauzelor comparabile cauzate de colectorul de gunoi. Am reu\u0219it s\u0103 ob\u021binem un timp de r\u0103spuns astfel utiliz\u00e2nd colectorul de gunoi G1, care permite specificarea unei \u021binte pentru durata pauzelor GC. Totu\u0219i, uneori, destul de rar, pauzele colectorului dep\u0103\u0219esc 50 ms, ceea ce poate duce la detectarea fals\u0103 a unei defec\u021biuni. Pentru a evita acest lucru, coordonatorul nu semnaleaz\u0103 defec\u021biunea nodului \u00eendep\u0103rtat la pierderea primului mesaj heartbeat de la acesta, ci doar dac\u0103 mai multe mesaje consecutive sunt pierdute. Astfel, am reu\u0219it s\u0103 realiz\u0103m detectarea defec\u021biunii nodului coordonator \u00een 200 ms. <\/p>\n<p>\u00cens\u0103, este insuficient s\u0103 \u00een\u021belegem rapid care nod a \u00eencetat s\u0103 func\u021bioneze. Trebuie s\u0103 facem ceva \u00een leg\u0103tur\u0103 cu asta. <\/p>\n<h2>Rezervare<\/h2>\n<p>\nSchema clasic\u0103 presupune, \u00een cazul unei defec\u021biuni a master-ului, ini\u021bierea alegerii unui nou master folosind unul dintre<noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_Raft\"> algoritmii<\/a><\/noindex> <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9F%D0%B0%D0%BA%D1%81%D0%BE%D1%81\">universal<\/a><\/noindex> faimo\u0219i. Totu\u0219i, aceste algoritmi au probleme bine cunoscute legate de convergen\u021ba \u00een timp \u0219i de durata procesului de alegere. Am reu\u0219it s\u0103 evit\u0103m astfel de \u00eent\u00e2rzieri suplimentare printr-o schem\u0103 de substitu\u021bie a coordonatorilor \u00eentr-o re\u021bea complet interconectat\u0103:<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/5e2db258fa2200026390a73a414dfe81.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nS\u0103 presupunem c\u0103 dorim s\u0103 efectu\u0103m o tranzac\u021bie \u00een grupul 50. S\u0103 stabilim din timp schema de substitu\u021bie, adic\u0103 ce noduri vor executa tranzac\u021biile grupului 50 \u00een cazul defec\u021biunii coordonatorului principal. Scopul nostru este de a men\u021bine func\u021bionalitatea sistemului \u00een cazul unei defec\u021biuni a unui centru de date. S\u0103 stabilim c\u0103 primul rezerv este un nod dintr-un alt centru de date, iar al doilea rezerv este un nod dintr-un al treilea. Aceast\u0103 schem\u0103 este aleas\u0103 o singur\u0103 dat\u0103 \u0219i nu se schimb\u0103 p\u00e2n\u0103 c\u00e2nd nu se schimb\u0103 topologia cluster-ului, adic\u0103 p\u00e2n\u0103 nu intr\u0103 noduri noi (ceea ce se \u00eent\u00e2mpl\u0103 foarte rar). Ordinea alegerii unui nou master activ \u00een cazul defec\u021biunii celui vechi va fi \u00eentotdeauna urm\u0103toarea: primul rezerv va deveni master activ, iar dac\u0103 \u0219i acesta \u00eenceteaz\u0103 s\u0103 func\u021bioneze, al doilea rezerv. <\/p>\n<p>Aceast\u0103 schem\u0103 este mai fiabil\u0103 dec\u00e2t algoritmul universal, deoarece pentru activarea unui nou master este suficient s\u0103 determin\u0103m faptul c\u0103 vechiul a e\u0219uat.<\/p>\n<p>Dar cum \u00ee\u0219i vor da seama clien\u021bii care dintre mae\u0219tri lucreaz\u0103 \u00een prezent? \u00cen 50 ms nu este posibil s\u0103 trimitem informa\u021bii la mii de clien\u021bi. Exist\u0103 posibilitatea ca un client s\u0103 trimit\u0103 o cerere de deschidere a unei tranzac\u021bii, f\u0103r\u0103 s\u0103 \u0219tie c\u0103 acel maestru nu mai func\u021bioneaz\u0103, iar cererea s\u0103 r\u0103m\u00e2n\u0103 blocat\u0103 pe timeout. Pentru a evita acest lucru, clien\u021bii trimit speculativ cereri de deschidere a unei tranzac\u021bii at\u00e2t c\u0103tre maestrul grupului, c\u00e2t \u0219i c\u0103tre to\u021bi rezervi. Dar doar acel maestru care este activ \u00een acel moment va r\u0103spunde la cerere. Toat\u0103 comunicarea ulterioar\u0103 \u00een cadrul tranzac\u021biei va avea loc doar cu maestrul activ.<\/p>\n<p>Mae\u0219trii de rezerv\u0103 care primesc cereri pentru tranzac\u021bii care nu le apar\u021bin le plaseaz\u0103 \u00een coada tranzac\u021biilor nen\u0103scute, unde r\u0103m\u00e2n pentru un timp. Dac\u0103 maestrul activ moare, noul maestru preia cererile de deschidere a tranzac\u021biilor din coada sa \u0219i r\u0103spunde clientului. Dac\u0103 clientul a reu\u0219it deja s\u0103 deschid\u0103 o tranzac\u021bie cu vechiul maestru, atunci al doilea r\u0103spuns este ignorat (\u0219i, evident, acea tranzac\u021bie nu se va finaliza \u0219i va fi repetat\u0103 de client).<\/p>\n<h2>Cum func\u021bioneaz\u0103 tranzac\u021bia<\/h2>\n<p>\nS\u0103 presupunem c\u0103 un client a trimis coordonatorului o cerere de deschidere a unei tranzac\u021bii pentru o anumit\u0103 entitate cu o anumit\u0103 cheie primar\u0103. Coordonatorul blocheaz\u0103 aceast\u0103 entitate \u0219i o plaseaz\u0103 \u00een tabelul de blocare \u00een memorie. Dac\u0103 este necesar, coordonatorul cite\u0219te aceast\u0103 entitate din stocare \u0219i salveaz\u0103 datele ob\u021binute \u00een starea tranzac\u021biei \u00een memoria coordonatorului.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/246b4a5c68a4cc886b87af784c16b9dc.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nC\u00e2nd clientul dore\u0219te s\u0103 modifice datele \u00een tranzac\u021bie, trimite coordonatorului o cerere de modificare a entit\u0103\u021bii, iar acesta plaseaz\u0103 noile date \u00een tabelul st\u0103rii tranzac\u021biilor din memorie. La acest punct, \u00eenregistrarea a fost finalizat\u0103 \u2014 nu se efectueaz\u0103 o \u00eenregistrare \u00een stocare.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/4400a91af30eb71134978ff27474f745.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nC\u00e2nd clientul solicit\u0103 datele sale modificate \u00een cadrul unei tranzac\u021bii active, coordonatorul ac\u021bioneaz\u0103 astfel: <\/p>\n<ul>\n<li>dac\u0103 ID-ul este deja prezent \u00een tranzac\u021bie, datele sunt preluate din memorie; <\/li>\n<li>dac\u0103 ID-ul nu este \u00een memorie, datele lips\u0103 sunt citite din nodurile de stocare, combinate cu cele deja existente \u00een memorie, iar rezultatul este returnat clientului. <\/li>\n<\/ul>\n<p>\nAstfel, clientul poate citi propriile modific\u0103ri, iar ceilal\u021bi clien\u021bi nu v\u0103d aceste modific\u0103ri, deoarece sunt stocate doar \u00een memoria coordonatorului, \u00een nodurile Cassandra \u00eenc\u0103 nu exist\u0103.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/c93fd7e8393f431a976f1f56b95e227c.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nC\u00e2nd un client trimite un commit, starea care era \u00een memorie la serviciu este salvat\u0103 de coordonator \u00een batch-ul \u00eenregistrat, iar sub form\u0103 de batch \u00eenregistrat este trimis\u0103 c\u0103tre depozitele Cassandra. Depozitele fac tot ce este necesar pentru ca acest pachet s\u0103 fie aplicat atomic (\u00eentreaga) \u0219i returneaz\u0103 un r\u0103spuns coordonatorului, care apoi deblocheaz\u0103 \u0219i confirm\u0103 succesul tranzac\u021biei clientului.<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/ab20487ba53ed88e2f560a2249b5d6f9.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nIar pentru a anula, coordonatorului \u00eei este suficient doar s\u0103 elibereze memoria ocupat\u0103 de starea tranzac\u021biei.<\/p>\n<p>Ca rezultat al \u00eembun\u0103t\u0103\u021birilor descrise mai sus, am realizat principiile ACID:<\/p>\n<ul>\n<li><b>Atomicitate<\/b>. Aceasta este o garan\u021bie c\u0103 nicio tranzac\u021bie nu va fi \u00eenregistrat\u0103 par\u021bial \u00een sistem, vor fi fie executate toate subopera\u021biile sale, fie nu va fi executat\u0103 niciuna. La noi, acest principiu este respectat datorit\u0103 batch-ului \u00eenregistrat \u00een Cassandra.<\/li>\n<li><b>Consisten\u021b\u0103<\/b>. Fiecare tranzac\u021bie reu\u0219it\u0103, prin defini\u021bie, \u00eenregistreaz\u0103 doar rezultate valide. Dac\u0103 dup\u0103 deschiderea tranzac\u021biei \u0219i executarea unei p\u0103r\u021bi din opera\u021bii se constat\u0103 c\u0103 rezultatul este invalid, se efectueaz\u0103 o anulare.<\/li>\n<li><b>Izolarea<\/b>. \u00cen timpul execut\u0103rii unei tranzac\u021bii, tranzac\u021biile paralele nu ar trebui s\u0103 influen\u021beze rezultatul acesteia. Tranzac\u021biile concurente sunt izolate prin bloc\u0103ri pesimiste pe coordonator. Pentru citiri \u00een afara tranzac\u021biei, se respect\u0103 principiul izol\u0103rii la nivelul Read Committed.<\/li>\n<li><b>Durabilitate<\/b>. Indiferent de problemele de la nivelurile inferioare - deconectarea sistemului, e\u0219ecul echipamentului - modific\u0103rile efectuate de o tranzac\u021bie finalizat\u0103 cu succes trebuie s\u0103 r\u0103m\u00e2n\u0103 salvate dup\u0103 reluarea func\u021bion\u0103rii. <\/li>\n<\/ul>\n<p><\/p>\n<h2>Cite\u0219te dup\u0103 indec\u0219i<\/h2>\n<p>\nS\u0103 lu\u0103m un tabel simplu: <\/p>\n<pre><code>CREATE TABLE photos (\nid bigint primary key,\nowner bigint,\nmodified timestamp,\n\u2026)<\/code><\/pre>\n<p>\nAcesta are un ID (cheie primar\u0103), un proprietar \u0219i o dat\u0103 de modificare. Trebuie s\u0103 facem o cerere foarte simpl\u0103 - s\u0103 select\u0103m datele \u00een func\u021bie de proprietar cu data de modificare \u201e\u00een ultimele 24 de ore\u201d. <\/p>\n<pre><code>SELECT *\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nPentru ca o astfel de cerere s\u0103 fie procesat\u0103 rapid, \u00eentr-o baz\u0103 de date SQL clasic\u0103, trebuie construit un index pe coloanele (owner, modified). Acest lucru \u00eel putem face destul de u\u0219or, deoarece acum avem garan\u021biile ACID!<\/p>\n<h2>Indec\u0219i \u00een C*One<\/h2>\n<p>\nExist\u0103 un tabel de baz\u0103 cu fotografii, \u00een care ID-ul \u00eenregistr\u0103rii este cheia primar\u0103. <\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/9f5d7c5f66882666a7ae8219f710beeb.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPentru index, C*One creeaz\u0103 un nou tabel care este o copie a tabelului original. Cheia se potrive\u0219te cu expresia index, incluz\u00e2nd, de asemenea, cheia primar\u0103 a \u00eenregistr\u0103rii din tabelul original:<\/p>\n<p><img decoding=\"async\" alt=\"NewSQL = NoSQL+ACID\" src=\"\/wp-content\/uploads\/2019\/09\/ce6df1c8407bca7ddc61fd88141455e6.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nAcum, interogarea pentru \u201eproprietar \u00een ultimele 24 de ore\u201d poate fi rescris\u0103 ca un select dintr-un alt tabel:<\/p>\n<pre><code>SELECT * FROM i1_test\nWHERE owner=?\nAND modified&gt;?<\/code><\/pre>\n<p>\nCoeren\u021ba datelor din tabelul original photos \u0219i din indexul i1 este men\u021binut\u0103 automat de coordonator. Pe baza doar a schemei de date, atunci c\u00e2nd se prime\u0219te o modificare, coordonatorul genereaz\u0103 \u0219i re\u021bine modificarea nu doar a tabelului principal, ci \u0219i a modific\u0103rilor copiilor. Nu se efectueaz\u0103 nicio ac\u021biune suplimentar\u0103 cu tabelul indexului, jurnalele nu sunt citite, bloc\u0103rile nu sunt utilizate. Adic\u0103, ad\u0103ugarea indec\u0219ilor consum\u0103 aproape resurse minime \u0219i nu influen\u021beaz\u0103 practic viteza de aplicare a modific\u0103rilor.<\/p>\n<p>Cu ajutorul ACID, am reu\u0219it s\u0103 implement\u0103m indec\u0219i \u201eca \u00een SQL\u201d. Ace\u0219tia au coeren\u021b\u0103, pot scala, func\u021bioneaz\u0103 rapid, pot fi compu\u0219i \u0219i integra\u021bi \u00een limbajul de interog\u0103ri CQL. Pentru a sus\u021bine indec\u0219ii, nu este necesar s\u0103 se fac\u0103 modific\u0103ri \u00een codul aplica\u021biei. Totul este simplu, ca \u00een SQL. \u0218i ceea ce este cel mai important, indec\u0219ii nu afecteaz\u0103 viteza de execu\u021bie a modific\u0103rilor din tabelul original al tranzac\u021biilor.<\/p>\n<h2>Ce am ob\u021binut<\/h2>\n<p>\nAm dezvoltat C*One acum trei ani \u0219i l-am lansat \u00een exploatare industrial\u0103. <\/p>\n<p>Ce am ob\u021binut \u00een final? S\u0103 evalu\u0103m acest lucru prin exemplul subsistemului de procesare \u0219i stocare a fotografiilor, unul dintre cele mai importante tipuri de date din re\u021beaua social\u0103. Nu este vorba despre corpurile fotografiilor, ci despre tot felul de metainforma\u021bii. \u00cen prezent, \u00een \u201eOdnoklassniki\u201d sunt aproximativ 20 miliarde astfel de \u00eenregistr\u0103ri, sistemul proceseaz\u0103 80 de mii de interog\u0103ri de citire pe secund\u0103, p\u00e2n\u0103 la 8 mii de tranzac\u021bii ACID pe secund\u0103, legate de modificarea datelor. <\/p>\n<p>C\u00e2nd am folosit SQL cu replication factor = 1 (dar \u00een RAID 10), metainforma\u021bia fotografiilor a fost stocat\u0103 pe un cluster de \u00eenalt\u0103 disponibilitate format din 32 de ma\u0219ini cu Microsoft SQL Server (plus 11 de rezerv\u0103). De asemenea, au fost alocate 10 servere pentru stocarea backup-urilor. \u00cen total, 50 de ma\u0219ini costisitoare. \u00cen aceast\u0103 situa\u021bie, sistemul a func\u021bionat la o sarcin\u0103 nominal\u0103, f\u0103r\u0103 rezerve.<\/p>\n<p>Dup\u0103 migrarea la noul sistem, am ob\u021binut un factor de replicare = 3 \u2014 c\u00e2te o copie \u00een fiecare centru de date. Sistemul este format din 63 de noduri de stocare Cassandra \u0219i 6 ma\u0219ini coordonatoare, totaliz\u00e2nd 69 de servere. Aceste ma\u0219ini sunt \u00eens\u0103 semnificativ mai ieftine, costul total fiind de aproximativ 30% din costul sistemului SQL. \u00cen acela\u0219i timp, \u00eenc\u0103rc\u0103tura se men\u021bine la un nivel de 30%.<\/p>\n<p>Odat\u0103 cu implementarea C*One, s-au redus \u0219i \u00eent\u00e2rzierile: \u00een SQL, opera\u021bia de scriere dura aproximativ 4,5 ms. \u00cen C*One \u2014 aproximativ 1,6 ms. Durata tranzac\u021biei, \u00een medie, este mai mic\u0103 de 40 ms, commit-ul se realizeaz\u0103 \u00een 2 ms, durata citirii \u0219i scrierii \u2014 \u00een medie 2 ms. Percentilul 99 este de doar 3-3,1 ms, iar num\u0103rul de timeout-uri s-a redus de 100 de ori \u2014 totul datorit\u0103 aplic\u0103rii pe scar\u0103 larg\u0103 a specula\u021biilor. <\/p>\n<p>P\u00e2n\u0103 \u00een prezent, cea mai mare parte a nodurilor SQL Server a fost scoas\u0103 din folosin\u021b\u0103, noile produse fiind dezvoltate doar cu utilizarea C*One. Am adaptat C*One pentru a func\u021biona \u00een norul nostru. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/company\/odnoklassniki\/blog\/346868\/\">one-cloud<\/a><\/noindex>, ceea ce a permis accelerarea desf\u0103\u0219ur\u0103rii de noi clustere, simplific\u00e2nd configurarea \u0219i automatiz\u00e2nd exploatarea. F\u0103r\u0103 codul surs\u0103, ar fi fost semnificativ mai complicat \u0219i mai dificultos. <\/p>\n<p>\u00cen prezent, lucr\u0103m la migrarea altor stoc\u0103ri \u00een nor \u2014 dar aceasta este deja o alt\u0103 poveste.<br \/>\n<br \/>Sursa: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/odnoklassniki\/blog\/417593\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0445\u0440\u0430\u043d\u0438\u043b\u043e\u0441\u044c \u0432 SQL Server. \u0414\u043b\u044f \u0442\u0430\u043a\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043c\u0430 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0442\u044c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0438 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439, \u0434\u0430 \u0435\u0449\u0435 \u0438 \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u044b\u0439 \u043a \u043e\u0442\u043a\u0430\u0437\u0443 \u0426\u041e\u0414 \u0434\u043e\u0441\u0442\u0443\u043f, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f SQL \u0421\u0423\u0411\u0414, \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e. \u041e\u0431\u044b\u0447\u043d\u043e \u0432 \u0442\u0430\u043a\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u043e\u0434\u043d\u043e \u0438\u0437 NoSQL-\u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449, \u043d\u043e \u043d\u0435 \u0432\u0441\u0451 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0432 NoSQL: \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":28484,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-37956","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"ro_RO\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/newsql-nosql-acid\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:20:47+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:20:47+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster","description":"P\u00e2n\u0103 de cur\u00e2nd, \u00een Odnoklassniki erau aproximativ 50 TB de date procesate.","canonical_url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/newsql-nosql-acid","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"ro_RO","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47NewSQL = NoSQL+ACID | ProHoster","og:description":"\u0414\u043e \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u041e\u0434\u043d\u043e\u043a\u043b\u0430\u0441\u0441\u043d\u0438\u043a\u0430\u0445 \u043e\u043a\u043e\u043b\u043e 50 \u0422\u0411 \u0434\u0430\u043d\u043d\u044b\u0445, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445.","og:url":"https:\/\/prohoster.info\/ro\/blog\/administrirovanie\/newsql-nosql-acid","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:20:47+00:00","article:modified_time":"2019-10-31T19:20:47+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"37956","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-23 19:55:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:17:23","updated":"2026-01-23 19:55:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/37956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/comments?post=37956"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/posts\/37956\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media\/28484"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/media?parent=37956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/categories?post=37956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/ro\/wp-json\/wp\/v2\/tags?post=37956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}