NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Deri në kohët e fundit, në Odnoklassniki u ruajtën rreth 50 TB të dhënash që përpunoheshin në kohë reale në SQL Server. Për një volum të tillë, sigurimi i një qendra të të dhënave të shpejtë dhe të besueshme, për më tepër të qëndrueshme ndaj dështimeve, duke përdorur një DBMS SQL, është thuajse e pamundur. Në raste të tilla zakonisht përdoren një nga ruajtjet NoSQL, por nuk gjithçka mund të jetë e transferueshme në NoSQL: disa entitete kërkojnë garanci ACID për transaksionet.

Kjo na çoi në përdorimin e një ruajtje NewSQL, dmth një DBMS që ofron qëndrueshmëri, shkallëzueshmëri dhe shpejtësi të sistemeve NoSQL, por që ruan gjithashtu garancitë ACID që jemi mësuar të përdorim në sistemet tradicionale. Sistemet industriale që funksionojnë nga kjo klasë të re janë pak, prandaj ne realizuam një sistem të tillë vetë dhe e lançuam atë në prodhim.

Si funksionon dhe çfarë është arritur - lexoni më poshtë.

Sot, audienca mujore e "Odnoklassniki" kap më shumë se 70 milion vizitorë unikë. Ne jemi ndër pesë rrjetet sociale më të mëdha në botë, dhe në dvigjithja më e madhe e faqeve në të cilat përdoruesit kalojnë më shumë kohë. Infrastrukturën e "OK" e përpunon ngarkesa shumë të larta: më shumë se një milion kërkesa HTTP/s në frontet. Pjesët e parkut të serverëve që përbëhen nga më shumë se 8000 njësi janë të vendosura afër njëri-tjetrit - në katër qendra të të dhënave në Moskë, çka lejon sigurimin e një vonese rrjeti më pak se 1 ms mes tyre.

Ne kemi përdorur Cassandra që nga viti 2010, duke filluar me versionin 0.6. Sot, në përdorim janë disa dhjetëra grupe. Grupi më i shpejtë përpunon më shumë se 4 milion operacione në sekondë, ndërsa grupi më i madh ruan 260 TB.

Megjithatë, të gjitha këto janë grupe të zakonshme NoSQL, që përdoren për ruajtjen e të dhënave me konsistencë të ulët. Ne dëshironim të zëvendësonim ruajtjen kryesore të konsistencës, Microsoft SQL Server, e cila është përdorur që nga themelimi i "Odnoklassniki". Ruajtja përbëhej nga më shumë se 300 makina SQL Server Standard Edition, të cilat mbajtën 50 TB të dhënash - entitete biznesi. Këto të dhëna modifikohen në kuadër të transaksioneve ACID dhe kërkojnë konsistencë të lartë..

PĂ«r shpĂ«rndarjen e tĂ« dhĂ«nave mbi nodet SQL Server ne pĂ«rdorĂ«m si vertikalen ashtu edhe particionimin horizontal. (shardimi). Historically, we used a simple data sharding scheme: each entity was assigned a token — a function of the entity's ID. Entities with the same token were placed on the same SQL server. The master-detail relationship was implemented so that the tokens of the primary and derived records always matched and were located on the same server. In a social network, almost all records are generated on behalf of the user — meaning all user data within one functional subsystem is stored on one server. Therefore, in business transactions, tables from the same SQL server were almost always involved, which allowed us to ensure data consistency using local ACID transactions without the need for slow and unreliable distributed ACID transactions.

Thanks to sharding and to speed up SQL performance:

  • We do not use Foreign key constraints, as with sharding the entity ID may reside on another server.
  • We do not use stored procedures and triggers due to the additional load on the DBMS CPU.
  • We do not use JOINs because of all the above and the numerous random disk reads.
  • Outside of transactions, to reduce deadlocks, we use the Read Uncommitted isolation level.
  • We only execute short transactions (on average shorter than 100 ms).
  • We do not use multi-row UPDATE and DELETE due to the high number of deadlocks — we update only one record at a time.
  • Queries are always executed only using indexes — a query with a full table scan plan means an overload of the DB and its failure.

These steps allowed us to extract almost maximum performance from SQL servers. However, the problems kept increasing. Let's take a look at them.

Problems with SQL

  • Since we used custom sharding, adding new shards was performed manually by administrators. During this time, scalable data replicas did not handle queries.
  • As the number of records in the table increases, the speed of inserts and modifications decreases; adding indexes to an existing table drastically reduces speed, and creating and recreating indexes comes with downtime.
  • Having a small number of Windows for SQL Server in production complicates infrastructure management.

But the main problem is —

Qëndrueshmëri

Klasik SQL server ka një qëndrim të dobët ndaj dështimeve. Le të themi se keni vetëm një server databaze, dhe ai dështon një herë çdo tre vjet. Gjatë këtij kohe, faqja e internetit nuk funksionon për 20 minuta, kjo është e pranueshme. Nëse keni 64 serverë, faqja e internetit nuk funksionon një herë çdo tri javë. Dhe nëse keni 200 serverë, faqja e internetit nuk funksionon çdo javë. Kjo është një problem.

ÇfarĂ« mund tĂ« bĂ«het pĂ«r tĂ« rritur qĂ«ndrueshmĂ«rinĂ« e SQL serverit? Wikipedia na sugjeron tĂ« ndĂ«rtojmĂ« njĂ« klaster me disponueshmĂ«ri tĂ« lartĂ«: ku nĂ« rastin e dĂ«shtimit tĂ« ndonjĂ« komponenti, ka njĂ« kopje rezervĂ«.

Kjo kërkon një park të pajisjeve të shtrenjta: shumë kopje, fibra optike, depozita të përbashkëta, dhe madje edhe aktivizimi i rezervës funksionon në mënyrë të pasigurt: rreth 10% e aktivizimeve përfundojnë me dështimin e nodës rezervë duke e ndjekur nodën kryesore.

Por disavantazhi kryesor i njĂ« klasteri me disponueshmĂ«ri tĂ« lartĂ« Ă«shtĂ« se nuk ka disponueshmĂ«ri nĂ« rastin e dĂ«shtimit tĂ« qendrĂ«s sĂ« tĂ« dhĂ«nave ku ndodhet. ‘Odnoklassniki’ ka katĂ«r qendra tĂ« tĂ« dhĂ«nave, dhe Ă«shtĂ« e nevojshme tĂ« sigurojmĂ« funksionimin nĂ« njĂ« dĂ«shtim tĂ« plotĂ« nĂ« njĂ« prej tyre.

Për këtë, mund të aplikojmë Multi-Master replikimin e integruar në SQL Server. Ky zgjidhje është shumë më e shtrenjtë për shkak të kostos së softuerit dhe vuan nga problemet e njohura të replikimit - vonesa të paparashikueshme të transaksioneve gjatë replikimit sinkron dhe vonesa në aplikimin e replikimeve (dhe, si rrjedhojë, modifikime të humbura) gjatë replikimit asinkron. Nënkuptimi i zgjidhjes manuale të konflikteve e bën këtë variant plotësisht të papranueshëm për ne.

Të gjitha këto probleme kërkonin një zgjidhje radikale dhe ne filluam analizën e tyre të detajuar. Këtu, duhet të njohim atë që kryesisht bën SQL Server - transaksionet.

Transaksioni i thjeshtë

Le të shqyrtojmë një transaksion të thjeshtë, në mënyrë të thjeshtë për programuesin SQL: shtimi i një fotografie në album. Albumet dhe fotografitë ruajten në tabela të ndryshme. Albumi ka një numërator për fotografitë publike. Atëherë ky transaksion ndahet në hapat e mëposhtëm:

  1. Bllokojmë albumin sipas çelësit.
  2. Krijojmë një rekord në tabelën e fotografive.
  3. Nëse fotografia ka status publik, rrisim numëratorin për fotografitë publike në album, përditësojmë regjistrin dhe konfirmojmë transaksionin.

Ose në formën e pseudokodit:

TX.start("Albums", id);
Album album = albums.lock(id);
Photo photo = photos.create(
);

if (photo.status == PUBLIC ) {
    album.incPublicPhotosCount();
}
album.update();

TX.commit();

Ne e çuditshme qĂ« skenari mĂ« i zakonshĂ«m i transaksioneve tĂ« biznesit — tĂ« lexosh tĂ« dhĂ«nat nga DB nĂ« memorien e serverit tĂ« aplikatave, tĂ« bĂ«sh disa ndryshime dhe tĂ« ruash vlerat e reja pĂ«rsĂ«ri nĂ« DB. Zakonisht, nĂ« njĂ« transaksion tĂ« tillĂ« ne pĂ«rditĂ«sojmĂ« disa entitete, disa tabela.

GjatĂ« kryerjes sĂ« transaksionit mund tĂ« ndodhĂ« modifikimi kompetitiv i tĂ« njĂ«jtave tĂ« dhĂ«na nga njĂ« sistem tjetĂ«r. PĂ«r shembull, Antispam mund tĂ« vendosĂ« se pĂ«rdoruesi Ă«shtĂ« i dyshimtĂ« dhe prandaj tĂ« gjitha fotografitĂ« e pĂ«rdoruesit nuk duhet tĂ« jenĂ« mĂ« publike, ato duhet tĂ« dĂ«rgohen pĂ«r moderim, qĂ« do tĂ« thotĂ« se statusi i fotos duhet tĂ« ndryshohet nĂ« njĂ« vlerĂ« tjetĂ«r dhe tĂ« kthjellohen numĂ«ruesit pĂ«rkatĂ«s. ËshtĂ« e qartĂ« se nĂ«se kjo operacion ndodh pa garanci pĂ«r atomizmin e aplikimit dhe izolimin e modifikimeve konkuruese, si nĂ« ACID, atĂ«herĂ« rezultati do tĂ« jetĂ« ai qĂ« nuk Ă«shtĂ« i nevojshĂ«m — ose numĂ«ruesi i fotove do tĂ« tregojĂ« njĂ« vlerĂ« tĂ« gabuar, ose jo tĂ« gjitha fotografitĂ« do tĂ« dĂ«rgohen pĂ«r moderim.

Një kod i tillë, që manipulat me entitete të ndryshme biznesi brenda një transaksioni, është shkruar shumë gjatë ekzistencës së Odnoklassnikov. Nga përvoja e migrimeve në NoSQL me Konsistenca e Vonshme ne e dimë se problemet më të mëdha (dhe kostot e kohës) shkaktohen nga nevoja për të zhvilluar kod që mban konsistencën e të dhënave. Prandaj, kërkesa kryesore për magazinën e re ishte të ofronte për logjikën aplikative transaksione të vërteta ACID.

Kërkesat e tjera, po aq të rëndësishme, ishin:

  • NĂ« rast tĂ« dĂ«shtimit tĂ« qendrĂ«s sĂ« tĂ« dhĂ«nave, duhet tĂ« jenĂ« tĂ« disponueshme si leximi ashtu edhe shkrimi nĂ« magazinĂ«n e re.
  • Ruajtja e shpejtĂ«sisĂ« aktuale tĂ« zhvillimit. Kjo do tĂ« thotĂ« se punĂ«n me magazinĂ«n e re duhet tĂ« ketĂ« njĂ« sasi tĂ« ngjashme kodi, nuk duhet tĂ« shfaqet nevoja pĂ«r tĂ« shkruar diçka tĂ« re nĂ« magazinĂ«, tĂ« zhvillosh algoritme pĂ«r zgjidhjen e konflikteve, ruajtjen e indekseve sekondare, etj.
  • ShpejtĂ«sia e funksionimit tĂ« magazinĂ«s sĂ« re duhet tĂ« jetĂ« mjaft e lartĂ«, si pĂ«r leximin e tĂ« dhĂ«nave ashtu edhe pĂ«r pĂ«rpunimin e transaksioneve, çka do tĂ« thotĂ« se zgjidhjet akademike tĂ« sakta, universale, por tĂ« ngadalshme, si pĂ«r shembull, komiteti me dy faza.
  • Skalimi automatikisht nĂ« kohĂ« reale.
  • PĂ«rdorimi i serverĂ«ve tĂ« zakonshĂ«m tĂ« lirĂ«, pa pasur nevojĂ« tĂ« blihen pajisje ekzotike.
  • MundĂ«sia pĂ«r tĂ« zhvilluar ruajtjen nga zhvilluesit e kompanisĂ«. NĂ« fjalĂ« tĂ« tjera, prioriteti i Ă«shtĂ« dhĂ«nĂ« zgjidhjeve tĂ« brendshme ose atyre me kod tĂ« hapur, preferohet nĂ« Java.

Zgjidhjet, zgjidhjet

Duke analizuar mundësitë e zgjidhjeve, ne arritëm në dy mundësi për arkitekturën:

E para — tĂ« marrim çdo server SQL dhe tĂ« realizojmĂ« qĂ«ndrueshmĂ«rinĂ« e nevojshme, mekanizmin e ĐŒĐ°ŃŃˆŃ‚Đ°Đ±ĐžŃ€ĐŸĐČĐ°ĐœĐžĂ«, klasterin e qĂ«ndrueshĂ«m, zgjidhjen e konfliktĂ«ve dhe transaksionet ACID tĂ« shpĂ«rndara, tĂ« besueshme dhe tĂ« shpejta. Ne e vlerĂ«suam kĂ«tĂ« variant si mjaft tĂ« ndĂ«rlikuar dhe tĂ« punĂ«s intensive.

Varianti i dytĂ« — tĂ« marrim njĂ« ruajtje NoSQL tĂ« gatshme me skalim tĂ« implementuar, njĂ« klaster tĂ« qĂ«ndrueshĂ«m, zgjidhje konfliktesh dhe tĂ« realizojmĂ« transaksionet dhe SQL vetĂ«. Me sa duket, edhe detyra e implementimit tĂ« SQL, pĂ«r tĂ« mos pĂ«rmendur transaksionet ACID, duket si njĂ« sfidĂ« pĂ«r disa vite. Por mĂ« pas kuptuam se grupi i mundĂ«sive tĂ« SQL qĂ« ne pĂ«rdorim nĂ« praktikĂ« Ă«shtĂ« shumĂ« larg nga ANSI SQL ashtu si Cassandra CQL Ă«shtĂ« e largĂ«t nga ANSI SQL. Duke e shqyrtuar mĂ« nga afĂ«r CQL, kuptuam se Ă«shtĂ« mjaft i afĂ«rt me atĂ« qĂ« na nevojitet.

Cassandra dhe CQL

Pra, çfarë e bën Cassandra interesante, cilat janë mundësitë e saj?

Së pari, këtu mund të krijoni tabela me mbështetje për lloje të ndryshme të të dhënave, mund të bëni SELECT ose UPDATE sipas çelësit primar.

CREATE TABLE photos (id bigint KEY, owner bigint,
);
SELECT * FROM photos WHERE id=?;
UPDATE photos SET 
 WHERE id=?;

PĂ«r tĂ« siguruar qĂ«ndrueshmĂ«rinĂ« e tĂ« dhĂ«nave tĂ« replikave, Cassandra pĂ«rdor pranimin me shumicĂ«NĂ« rastin mĂ« tĂ« thjeshtĂ«, kjo do tĂ« thotĂ« se kur vendosim tre replika tĂ« tĂ« njĂ«jtit rresht nĂ« nodet e ndryshme tĂ« klasterit, regjistrimi konsiderohet i suksesshĂ«m nĂ«se shumica e nodave (dmth dy nga tre) konfirmojnĂ« suksesin e kĂ«tij operacioni tĂ« regjistrimit. TĂ« dhĂ«nat e rreshtit konsiderohen tĂ« koordinuara nĂ«se gjatĂ« leximit shumica e nodave janĂ« pyetur dhe kanĂ« konfirmuar ato. KĂ«shtu, me tre replika, garanton njĂ« konsistencĂ« tĂ« plotĂ« dhe tĂ« menjĂ«hershme tĂ« tĂ« dhĂ«nave nĂ« rast se njĂ« nod dĂ«shtonte. Ky qasje na lejon tĂ« realizojmĂ« njĂ« skemĂ« edhe mĂ« tĂ« besueshme: gjithmonĂ« dĂ«rgojmĂ« kĂ«rkesa nĂ« tĂ« gjitha tre replikat, duke pritur pĂ«rgjigjen nga dy mĂ« tĂ« shpejtat. PĂ«rgjigja e ngadaltĂ« e tretĂ« tĂ« replikes nĂ« kĂ«tĂ« rast pĂ«rjashtohet. Noda me pĂ«rgjigjen e ngadaltĂ« mund tĂ« ketĂ« probleme serioze — ngadalĂ«sime, grumbullim mbetjeje nĂ« JVM, rikuperim tĂ« memories direkte nĂ« kernelin linux, dĂ«shtim tĂ« harduerit, çëputje nga rrjeti. MegjithatĂ«, kjo nuk ndikon aspak nĂ« operacionin e klientit dhe nĂ« tĂ« dhĂ«na.

Qasja kur ne i drejtohemi tre nodave, dhe marrim përgjigje nga dy, quhet speculacion: kërkesa për replika të panevojshme dërgohet edhe para se të "shkëputet".

NjĂ« tjetĂ«r pĂ«rparĂ«si e Cassandra Ă«shtĂ« Batchlog — mekanizmi qĂ« garanton ose aplikimin e plotĂ«, ose mosaplikimin e plotĂ« tĂ« paketĂ«s sĂ« ndryshimeve qĂ« ju bĂ«ni. Kjo na lejon tĂ« zgjidhim A nĂ« ACID — atomiku nga kutia.

Ajo që i ngjan transaksioneve në Cassandra është ajo që quhet "transaksione të lehta". Por ato janë shumë të largëta nga transaksionet "të vërteta" ACID: në të vërtetë, kjo është mundësia për të bërë CAS në të dhënat e vetëm një regjistrimi, duke përdorur konsensusin nga protokolli i rëndë Paxos. Prandaj, shpejtësia e këtyre transaksioneve është e vogël.

ÇfarĂ« na mungoi nĂ« Cassandra

Pra, ne duhet të realizojmë në Cassandra transaksione të vërteta ACID. Me përdorim të cilave mund të realizonim lehtësisht dy mundësi të tjera të favorshme të DBMS klasik: indekse konsistente të shpejta, që do të na lejonin të bënim kërkesa të dhënash jo vetëm sipas çelësit primar dhe një gjenerator të zakonshëm të ID-ve monotone auto-increment.

C*One

Kështu lindi një DBMS e re C*One, e bërë nga tre lloje nodash serverësh:

  • MarrĂ«sit — serverĂ« (gati) standardĂ« Cassandra, tĂ« cilĂ«t janĂ« pĂ«rgjegjĂ«s pĂ«r ruajtjen e tĂ« dhĂ«nave nĂ« disqet lokale. NdĂ«rsa rritet ngarkesa dhe sasia e tĂ« dhĂ«nave, numri i tyre mund tĂ« shkallĂ«zohet lehtĂ«sisht nĂ« dhjetĂ«ra e qindra.
  • KoordinatorĂ«t e transaksioneve - sigurojnĂ« realizimin e transaksioneve.
  • KlientĂ«t - serverĂ«t e aplikacioneve qĂ« realizojnĂ« operacione biznesi dhe iniciatativat e transaksioneve. Mund tĂ« ketĂ« mijĂ«ra klientĂ« tĂ« tillĂ«.

NewSQL = NoSQL+ACID

Serverët e të gjitha llojeve janë të lidhur në një klasër të përbashkët, përdorin protokollin e brendshëm të mesazheve Cassandra për komunikim mes tyre dhe protokolli për shkëmbimin e informacionit të klasit. Me ndihmën e Heartbeat, serverët mësojnë për dështimet e ndërsjella, mbajnë një skemë të përbashkët të të dhënave - tabelat, strukturën dhe replikimin e tyre; skemën e ndarje, topologjinë e klasit, etj.

Klientët

NewSQL = NoSQL+ACID

Në vend të drejtuesve standardë, përdoret modaliteti Fat Client. Ky nod nuk ruan të dhëna, por mund të funksionojë si koordinator i realizimit të kërkesave, pra Klienti vetë kryen funksionin e koordinatës së kërkesave të tij: shqyrton replikat e magazinës dhe zgjidh konfliktet. Ky modalitet jo vetëm që është më i besueshëm dhe më i shpejtë se drejtuesi standard, që kërkon komunikim me një koordinator të largët, por gjithashtu lejon menaxhimin e përcjelljes së kërkesave. Përveçse për transaksionin e hapur në klient, kërkesat drejtohen në magazina. Nëse klienti ka hapur një transaksion, të gjitha kërkesat brenda transaksionit drejtohen në koordinatorin e transaksioneve.
NewSQL = NoSQL+ACID

Koordinatori i transaksioneve C*One

Koordinator - kjo është ajo që e kemi realizuar për C*One nga zero. Ai është përgjegjës për menaxhimin e transaksioneve, bllokimeve dhe rendit të aplikimit të transaksioneve.

PĂ«r çdo transaksion qĂ« shĂ«rbehet, koordinator gjeneron njĂ« vulĂ« tĂ« pĂ«rkohshme: çdo e ardhshme Ă«shtĂ« mĂ« e madhe se e mĂ«parshmja. Duke qenĂ« se nĂ« sistemin Cassandra, sistemi i zgjidhjes sĂ« konflikteve bazohen nĂ« vulat e kohĂ«s (nga dy regjistrime konfliktuale, e sakta konsiderohet ajo me vulĂ« mĂ« tĂ« vonshme), konflikti gjithmonĂ« do tĂ« zgjidhet nĂ« favor tĂ« transaksionit tĂ« ardhshĂ«m. KĂ«shtu e realizuam orĂ«n e Lamportit – njĂ« mĂ«nyrĂ« e lirĂ« pĂ«r zgjidhjen e konflikteve nĂ« njĂ« sistem tĂ« shpĂ«rndarĂ«.

Bllokimet

Për të siguruar izolimin, ne vendosëm të përdorim mënyrën më të thjeshtë - bllokimet pesimiste sipas çelësit primar të regjistrimit. Në fjalë të tjera, brenda transaksionit, regjistrimi duhet fillimisht të bllokohet, pastaj të lexohet, modifikohet dhe të ruhet. Vetëm pas një komitimi të suksesshëm, regjistrimi mund të përshtatet, në mënyrë që transaksionet kompetuese të mund ta përdorin atë.

Zbatimi i një bllokimi të tillë është i thjeshtë në një ambient jo të shpërndarë. Në një sistem të shpërndarë ka dy rrugë kryesore: ose të realizohet një bllokim i shpërndarë në klaster, ose të shpërndahen transaksionet në mënyrë që transaksionet që përfshijnë një rekord të caktuar gjithmonë të shërbehen nga i njëjti koordinatori.

Pasi që në rastin tonë të dhënat janë tashmë të shpërndara në grupe lokalësh transaksionesh në SQL, u vendos që t'u caktohet koordinatorëve grupet e transaksioneve lokale: një koordinator kryen të gjitha transaksionet me numër token nga 0 në 9, tjetri nga 10 në 19, dhe kështu me radhë. Si rezultat, çdo njësi e koordinatorit bëhet master e grupit të transaksioneve.

Atëherë bllokimet mund të zbatohen si një HashMap e thjeshtë në memorie të koordinatorit.

Dështimet e koordinatorëve

Pasi që një koordinator shërben ekskluzivisht një grup transaksionesh, është shumë e rëndësishme të përcaktohet shpejt fakti i dështimit të tij, në mënyrë që përpjekja për të rinisur transaksionin të përfshihet në afatin e përcaktuar. Qëllimi është që kjo të jetë e shpejtë dhe e besueshme, prandaj ne përdorëm një protokoll qorrheti të lëvizshëm me kvorum:

Në çdo qendër të të dhënave vendosen të paktën dy nyje koordinatorësh. Periodikisht, çdo koordinator dërgon një mesazh heartbeat te koordinatorët e tjerë dhe i informon ata për funksionimin e tij, si dhe për cilat mesazhe heartbeat nga koordinatorët e tjerë në klaster ka marrë për herë të fundit.

NewSQL = NoSQL+ACID

Duke marrĂ« informacion tĂ« ngjashĂ«m nga tĂ« tjerĂ«t nĂ« mesazhet e tyre heartbeat, çdo koordinator vendos vetĂ« se cilat nyje tĂ« klasterit janĂ« funksionale dhe cilat jo, duke u drejtuar nga parimi i kvorumit: nĂ«se nyja X ka marrĂ« nga shumica e nyjeve nĂ« klaster informacionin pĂ«r marrjen normale tĂ« mesazheve nga nyja Y, atĂ«herĂ« Y Ă«shtĂ« funksionale. Dhe anasjelltas, sapo shumica tĂ« njoftojĂ« pĂ«r humbjen e mesazheve nga nyja Y, atĂ«herĂ« Y ka dĂ«shtuar. ËshtĂ« interesante se nĂ«se kvorumi i thotĂ« nyjĂ«s X se nuk po merr mĂ« mesazhe prej saj, atĂ«herĂ« vetĂ« nyja X do ta konsiderojĂ« veten si tĂ« dĂ«shtuar.

Mesazhet e heartbeat dërgohet me një frekuencë të lartë, rreth 20 herë në sekondë, me një periudhë prej 50 ms. Në Java është e vështirë të garantosh reagimin e aplikacionit brenda 50 ms për shkak të gjatëzisë krahasuese të ndalimeve që shkaktohen nga mbledhësi i plehrave. Kemi arritur të kemi këtë kohë reagimi duke përdorur mbledhësin e plehrave G1, i cili lejon përcaktimin e një objekti për gjatësi ndalimi të GC. Megjithatë, ndonjëherë, mjaft rrallë, ndalimet e mbledhësit kalojnë 50 ms, gjë që mund të çojë në zbulim të rremë të dështimit. Për të shmangur këtë, koordinatori nuk njofton për dështimin e nodit të largët me humbjen e mesazhit të parë heartbeat prej tij, vetëm nëse humbasin disa radhazi. Kështu, kemi arritur të zbulojmë dështimin e nodit koordinues brenda 200 ms.

Por është e pamjaftueshme të kuptosh shpejt se cili nod ka pushuar së funksionuari. Duhet të bëjmë diçka për këtë.

Rezervimi

Skema klasike parashikon që në rast dështimi të masterit të fillohen zgjedhjet e reja me një nga algoritmet e njohura unike Megjithatë, këtyre algoritmeve u njihen mirë problemet e konvergjencës në kohë dhe gjatësi të procesit të zgjedhjeve. Këto vonesa shtesë ia kemi arritur të shmangim duke përdorur një skemë zëvendësimi për koordinatoret në një rrjet të plotë:

NewSQL = NoSQL+ACID

Le të supozojmë se duam të kryejmë një transaksion në grupin 50. Në mënyrë paraprake përcaktojmë skemën e zëvendësimit, duke përcaktuar se cilat nod do të kryejnë transaksionet e grupit 50 në rast dështimi të koordinatoret kryesor. Qëllimi ynë është të ruajmë funksionalitetin e sistemit në rastin e dështimit të datacenter-it. Le të përcaktojmë se rezevarti i parë do të jetë një nod nga një datacenter tjetër, dhe rezevarti i dytë do të jetë një nod nga një datacenter të tretë. Kjo skemë zgjidhet një herë dhe nuk ndryshon derisa të ndryshohet topologjia e klasterit, që do të thotë derisa të hyjnë nodet e reja (gjë që ndodh shumë rrallë). Rendi i zgjedhjes së një masteri aktiv të ri në rast dështimi të një të vjetri do të jetë gjithmonë ky: masteri aktiv do të bëhet rezevarti i parë dhe nëse ai ka pushuar gjithashtu së funksionuari - rezevarti i dytë.

Kjo skemë është më e besueshme se algoritmi universal, pasi për aktivizimin e një masteri të ri mjafton të përcaktohet fakti i dështimit të atij të vjetër.

Por si si e kuptojne klientet, cili nga mjeshtrit po punon tani? Në 50 ms është e pamundur të dërgosh informacion në mijëra klientë. Mund të ndodhi që një klient dërgon një kërkesë për hapjen e një transaksioni, pa e ditur ende se ky mjeshtër nuk po funksionon më, dhe kërkesa do të ngecë në një skadim kohor. Për të parandaluar këtë, klientët dërgojnë spekulativisht kërkesën për hapjen e transaksionit përkatës në grupin e mjeshtrit dhe në dy rezervat e tij, por do të përgjigjet vetëm ai që është mjeshtër aktiv në atë moment. Të gjithë komunikimet e mëtejshme brenda transaksionit do t'i kryejë klienti vetëm me mjeshtrin aktiv.

Mjeshtrit rezervë që marrin kërkesat për transaksione që nuk janë të tyre i vendosin ato në një radhë të transaksioneve të paralindura, ku ato ruhen për një kohë të caktuar. Nëse mjeshtri aktiv vdes, atëherë mjeshtri i ri merr kërkesat për hapjen e transaksioneve nga radhë e tij dhe i përgjigjet klientit. Nëse klienti tashmë ka hapur një transaksion me mjeshtrin e vjetër, atëherë përgjigja e dytë injorohet (dhe, natyrisht, një transaksion i tillë nuk do të përfundojë dhe do të përsëritet nga klienti).

Si funksionon transaksioni

Supozoni se klienti i ka dërguar koordinatorit një kërkesë për hapjen e një transaksioni për një entitet të caktuar me një çelës primar të caktuar. Koordinatori e bllokon këtë entitet dhe e vendos në tabelën e bllokimeve në memorjen e tij. Nëse është e nevojshme, koordinatori e lexon këtë entitet nga depoja dhe ruan të dhënat e marra në gjendjen e transaksionit në memorjen e koordinatorit.

NewSQL = NoSQL+ACID

Kur klienti dĂ«shiron tĂ« ndryshojĂ« tĂ« dhĂ«nat brenda transaksionit, ai dĂ«rgon njĂ« kĂ«rkesĂ« pĂ«r modifikimin e entitetit te koordinatori, dhe ai vendos tĂ« dhĂ«nat e reja nĂ« tabelĂ«n e gjendjes sĂ« transaksioneve nĂ« memorje. NĂ« kĂ«tĂ« pikĂ«, regjistrimi pĂ«rfundon — regjistrimi nĂ« depo nuk bĂ«het.

NewSQL = NoSQL+ACID

Kur klienti kërkon të dhënat e tij të modifikuara brenda një transaksioni aktiv, koordinatori vepron si më poshtë:

  • nĂ«se ID tashmĂ« ekziston nĂ« transaksion, tĂ« dhĂ«nat merren nga memoria;
  • nĂ«se ID nuk Ă«shtĂ« nĂ« memorie, tĂ« dhĂ«nat e mungojnĂ« lexohen nga nodet e depozitave, bashkohen me ato qĂ« tashmĂ« ekzistojnĂ« nĂ« memorje, dhe rezultati i jepet klientit.

Kështu, klienti mund të lexojë ndryshimet e tij, ndërsa klientët e tjerë nuk i shohin këto ndryshime, sepse ato ruhen vetëm në memorjen e koordinatorit, në nodet e Cassandra ato ende nuk kanë mbërritur.

NewSQL = NoSQL+ACID

Kur klienti dërgon një commit, gjendja që kishte në kujtesë shërbimi, ruhet nga koordinatori në logged batch, dhe në formën e logged batch dërgohet në magazinat Cassandra. Magazinat bëjnë gjithçka të nevojshme që ky paketë të aplikohet në mënyrë atomike (plotsisht), dhe kthejnë një përgjigje koordinatori, i cili çliron bllokimet dhe konfirmon suksesin e transaksionit për klientin.

NewSQL = NoSQL+ACID

Dhe për të anuluar, koordinatori ka nevojë të çlirojë vetëm kujtesën e zënë nga gjendja e transaksionit.

Si rezultat i përmirësimeve të përmendura më lart, ne realizuam parimet ACID:

  • Atomizmi. Kjo Ă«shtĂ« njĂ« garanci qĂ« asnjĂ« transaksion nuk do tĂ« regjistrohet pjesĂ«risht nĂ« sistem, do tĂ« executohen tĂ« gjitha nĂ«n-operacionet e tij, ose nuk do tĂ« ekzekutohet asnjĂ«ra. Ky princip respektohet nga logged batch nĂ« Cassandra.
  • Konsistenca. Çdo transaksion i suksesshĂ«m pĂ«r definicion regjistron vetĂ«m rezultate tĂ« lejuara. NĂ«se pas hapjes sĂ« transaksionit dhe kryerjes sĂ« disa operacioneve, zbulohet se rezultati Ă«shtĂ« i papranueshĂ«m, kryhet njĂ« rikthim.
  • Izolimi. GjatĂ« ekzekutimit tĂ« njĂ« transaksioni, transaksionet paralele nuk duhet tĂ« ndikojnĂ« nĂ« rezultatin e tij. Transaksionet konkurruese janĂ« tĂ« izoluara pĂ«rmes bllokimeve pesimiste nĂ« koordinatore. PĂ«r leximet jashtĂ« transaksionit respektohet parimi i izolimit nĂ« nivelin Read Committed.
  • QĂ«ndrueshmĂ«ria. PavarĂ«sisht nga problemet nĂ« nivelet e poshtme — ndĂ«rprerja e energjisĂ«, defekti nĂ« pajisje — ndryshimet e bĂ«ra nga njĂ« transaksion i pĂ«rfunduar me sukses duhet tĂ« mbahen tĂ« ruajtura pas rimĂ«kĂ«mbjes sĂ« funksionimit.

Leximi përmes indekseve

Le të marrim një tabelë të thjeshtë:

CREATE TABLE photos (
id bigint primary key,
owner bigint,
modified timestamp,

)

Ajo ka njĂ« ID (çelĂ«si primar), pronari dhe data e ndryshimit. Duhet tĂ« bĂ«jmĂ« njĂ« kĂ«rkesĂ« shumĂ« tĂ« thjeshtĂ« — tĂ« selektojmĂ« tĂ« dhĂ«nat sipas pronarit me datĂ«n e ndryshimit "nĂ« 24 orĂ«t e fundit".

SELECT *
WHERE owner=?
AND modified>?

Për ta bërë një kërkesë të tillë të operojë shpejt, në një DBMS klasik SQL ne duhet të ndërtojmë një indeks mbi kolonat (owner, modified). Këtë do ta bëjmë mjaft lehtësisht, tani që kemi garantuar ACID!

Indekset në C*One

Ekziston tabela origjinale me fotografi, ku ID e regjistrimit është çelësi primar.

NewSQL = NoSQL+ACID

PĂ«r indeksin, C*One krijon njĂ« tabelĂ« tĂ« re, e cila Ă«shtĂ« njĂ« kopje e origjinales. ÇelĂ«si pĂ«rputhet me shprehjen indeksuese, pasi pĂ«rfshin gjithashtu çelĂ«sin primar tĂ« regjistrimit nga tabela origjinale:

NewSQL = NoSQL+ACID

Tani tash në «pronarin e 24 orëve të fundit» mund të riktheni si një select nga një tabelë tjetër:

SELECT * FROM i1_test
WHERE owner=?
AND modified>?

Konsistenca e të dhënave të tabelës fillestare photos dhe indeksit i1 mbahet automatikisht nga koordinatori. Bazuar vetëm në skemën e të dhënave, kur merrni ndryshime, koordinatori gjeneron dhe ruan ndryshimin jo vetëm të tabelës kryesore, por edhe ndryshimet e kopjeve. Asnjë veprim shtesë me tabelën e indeksit nuk kryhet, logjet nuk lexohen, bllokimet nuk përdoren. Kjo do të thotë që shtimi i indekseve konsumon pothuajse asnjë burim dhe ka pak ndikim në shpejtësinë e aplikimit të modifikimeve.

Me ACID arritëm të implementojmë indekse «si në SQL». Ato kanë konsistencë, mund të shkallëzohen, punojnë shpejt, mund të jenë të përbëra dhe të integruara në gjuhën e pyetjeve CQL. Për mbështetje të indekseve nuk është e nevojshme të bëni ndryshime në kodin aplikativ. Gjithçka është e thjeshtë, si në SQL. Dhe, ajo që është më e rëndësishmja, indekset nuk ndikojnë në shpejtësinë e ekzekutimit të modifikimeve të tabelës fillestare të transaksioneve.

ÇfarĂ« rezultati kemi arritur

Ne zhvilluam C*One tre vjet më parë dhe e vendosëm në përdorim industrial.

ÇfarĂ« e kemi arritur nĂ« fund? Le tĂ« e vlerĂ«sojmĂ« kĂ«tĂ« pĂ«rmes sistemit tĂ« pĂ«rpunimit dhe ruajtjes sĂ« fotografive, njĂ« nga llojet mĂ« tĂ« rĂ«ndĂ«sishme tĂ« tĂ« dhĂ«nave nĂ« njĂ« rrjet social. BĂ«het fjalĂ« jo pĂ«r trupat e fotografive, por pĂ«r tĂ« gjitha metainformatat. Tani nĂ« «Odnoklassniki» ka rreth 20 miliardĂ« tĂ« tilla regjistrimesh, sistemi pĂ«rpunon 80,000 pyetje leximi nĂ« sekondĂ«, deri nĂ« 8,000 ACID-transaksione nĂ« sekondĂ«, tĂ« lidhura me modifikimin e tĂ« dhĂ«nave.

Kur përdorëm SQL me replication factor = 1 (por në RAID 10), metainformatat e fotografive ruheshin në një klaster me 32 makina me Microsoft SQL Server (plus 11 rezervë). Gjithashtu, ishin caktuar 10 servera për ruajtjen e backup-eve. Në total 50 makina të kushtueshme. Sidoqoftë, sistemi punonte me ngarkesë nominale, pa rezervë.

Pas migrimit nĂ« sistemin e ri, ne morĂ«m replication factor = 3 — njĂ« kopje nĂ« çdo data-center. Sistemi pĂ«rbĂ«het nga 63 nodet e ruajtjes Cassandra dhe 6 makina koordinatores, gjithsej 69 servera. Por kĂ«to makina janĂ« ndjeshĂ«m mĂ« tĂ« lira, shuma totale e kostos Ă«shtĂ« rreth 30% e kostos sĂ« sistemit nĂ« SQL. NdĂ«rkohĂ«, ngarkesa qĂ«ndron nĂ« 30%.

Me implementimin e C*One, vonesat janĂ« zvogĂ«luar: operacioni i shkruajtes nĂ« SQL zĂ« rreth 4.5 ms. NĂ« C*One — rreth 1.6 ms. Koha mesatare e transaksionit Ă«shtĂ« mĂ« pak se 40 ms, komiti ekzekutohet pĂ«r 2 ms, koha e leximit dhe shkruajtes Ă«shtĂ« mesatarisht 2 ms. Percentili i 99-tĂ« Ă«shtĂ« vetĂ«m 3-3.1 ms, numri i kohĂ«-mandeve Ă«shtĂ« zvogĂ«luar 100 herĂ« — falĂ« pĂ«rdorimit tĂ« gjerĂ« tĂ« spekulimeve.

Derisa, tani, pjesa më e madhe e nyjeve të SQL Server është hequr nga përdorimi, produktet e reja zhvillohen vetëm me përdorimin e C*One. Ne e kemi adaptuar C*One për të punuar në cloud-in tonë. one-cloud, e cila ka lejuar që të shpejtojë ndërtimin e klasterëve të rinj, të thjeshtësojë konfigurimin dhe të automatizojë operimin. Pa kodin burimor, kjo do të kishte qenë ndjeshëm më e ndërlikuar dhe më e çuditshme.

Tani jemi duke punuar pĂ«r tĂ« transferuar magazinat e tjera nĂ« cloud — por kjo Ă«shtĂ« njĂ« histori krejt ndryshe.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster