NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Hanggang kamakailan lamang, ang Odnoklassniki ay nag-imbak ng humigit-kumulang 50 TB ng data na naproseso sa real time sa SQL Server. Para sa ganoong dami, halos imposibleng magbigay ng mabilis at maaasahan, at kahit na ang data center failure-tolerant access gamit ang isang SQL DBMS. Karaniwan, sa mga ganitong kaso, ginagamit ang isa sa mga imbakan ng NoSQL, ngunit hindi lahat ay maaaring ilipat sa NoSQL: ang ilang mga entidad ay nangangailangan ng mga garantiya sa transaksyon ng ACID.

Ito ay humantong sa amin sa paggamit ng NewSQL storage, iyon ay, isang DBMS na nagbibigay ng fault tolerance, scalability at performance ng NoSQL system, ngunit sa parehong oras ay pinapanatili ang mga garantiya ng ACID na pamilyar sa mga klasikal na system. Mayroong ilang mga gumaganang sistemang pang-industriya ng bagong uri na ito, kaya ipinatupad namin mismo ang gayong sistema at inilagay ito sa komersyal na operasyon.

Paano ito gumagana at kung ano ang nangyari - basahin sa ilalim ng hiwa.

Ngayon, ang buwanang madla ng Odnoklassniki ay higit sa 70 milyong natatanging bisita. Kami Nasa top five kami pinakamalaking mga social network sa mundo, at kabilang sa dalawampung site kung saan ang mga gumagamit ay gumugugol ng pinakamaraming oras. Ang imprastraktura ng OK ay humahawak ng napakataas na pag-load: higit sa isang milyong HTTP na kahilingan/seg bawat harap. Ang mga bahagi ng isang fleet ng server na higit sa 8000 piraso ay matatagpuan malapit sa isa't isa - sa apat na sentro ng data ng Moscow, na ginagawang posible upang matiyak ang latency ng network na mas mababa sa 1 ms sa pagitan nila.

Ginagamit namin si Cassandra mula noong 2010, simula sa bersyon 0.6. Ngayon ay may ilang dosenang kumpol na gumagana. Ang pinakamabilis na cluster ay nagpoproseso ng higit sa 4 na milyong operasyon bawat segundo, at ang pinakamalaking tindahan ay 260 TB.

Gayunpaman, ang lahat ng ito ay ordinaryong NoSQL cluster na ginagamit para sa imbakan mahina ang coordinated datos. Nais naming palitan ang pangunahing pare-parehong imbakan, ang Microsoft SQL Server, na ginamit mula nang itatag ang Odnoklassniki. Ang storage ay binubuo ng higit sa 300 SQL Server Standard Edition machine, na naglalaman ng 50 TB ng data - mga entity ng negosyo. Ang data na ito ay binago bilang bahagi ng mga transaksyon sa ACID at kinakailangan mataas na pagkakapare-pareho.

Upang ipamahagi ang data sa mga SQL Server node, ginamit namin ang parehong patayo at pahalang paghahati (sharding). Sa kasaysayan, gumamit kami ng simpleng data sharding scheme: ang bawat entity ay nauugnay sa isang token - isang function ng entity ID. Ang mga entity na may parehong token ay inilagay sa parehong SQL server. Ang master-detail na relasyon ay ipinatupad upang ang mga token ng pangunahing at bata na mga tala ay palaging magkatugma at matatagpuan sa parehong server. Sa isang social network, halos lahat ng mga tala ay nabuo sa ngalan ng user - na nangangahulugan na ang lahat ng data ng user sa loob ng isang functional subsystem ay naka-imbak sa isang server. Iyon ay, ang isang transaksyon sa negosyo ay halos palaging nagsasangkot ng mga talahanayan mula sa isang SQL server, na naging posible upang matiyak ang pagkakapare-pareho ng data gamit ang mga lokal na transaksyon sa ACID, nang hindi kinakailangang gumamit mabagal at hindi mapagkakatiwalaan ipinamahagi na mga transaksyon sa ACID.

Salamat sa sharding at para mapabilis ang SQL:

  • Hindi kami gumagamit ng Foreign key constraints, dahil kapag sharding ang entity ID ay maaaring matatagpuan sa ibang server.
  • Hindi kami gumagamit ng mga stored procedure at trigger dahil sa karagdagang load sa DBMS CPU.
  • Hindi kami gumagamit ng mga JOIN dahil sa lahat ng nasa itaas at maraming random na pagbabasa mula sa disk.
  • Sa labas ng isang transaksyon, ginagamit namin ang Read Uncommitted isolation level para mabawasan ang mga deadlock.
  • Nagsasagawa lamang kami ng mga maiikling transaksyon (sa average na mas maikli sa 100 ms).
  • Hindi kami gumagamit ng multi-row na UPDATE at DELETE dahil sa malaking bilang ng mga deadlock - isang record lang ang ina-update namin sa bawat pagkakataon.
  • Palagi kaming nagsasagawa ng mga query sa mga index lamang - ang isang query na may buong table scan plan para sa amin ay nangangahulugan ng labis na karga ng database at nagiging sanhi ng pagkabigo nito.

Ang mga hakbang na ito ay nagbigay-daan sa amin na i-squeeze ang halos maximum na performance sa mga SQL server. Gayunpaman, ang mga problema ay naging mas at mas marami. Tingnan natin sila.

Mga problema sa SQL

  • Dahil ginamit namin ang self-written sharding, ang pagdaragdag ng mga bagong shard ay ginawa nang manu-mano ng mga administrator. Sa lahat ng oras na ito, ang mga nasusukat na replika ng data ay hindi nagseserbisyo ng mga kahilingan.
  • Habang lumalaki ang bilang ng mga tala sa talahanayan, bumababa ang bilis ng pagpasok at pagbabago; kapag nagdaragdag ng mga index sa isang umiiral na talahanayan, bumababa ang bilis ng isang kadahilanan; ang paglikha at muling paglikha ng mga index ay nangyayari sa downtime.
  • Ang pagkakaroon ng kaunting Windows para sa SQL Server sa produksyon ay nagpapahirap sa pamamahala ng imprastraktura

Ngunit ang pangunahing problema ay

pagpaparaya sa kasalanan

Ang klasikong SQL server ay may mahinang fault tolerance. Sabihin nating mayroon ka lamang isang database server, at nabigo ito isang beses bawat tatlong taon. Sa panahong ito ang site ay naka-down sa loob ng 20 minuto, na katanggap-tanggap. Kung mayroon kang 64 na server, ang site ay bumaba nang isang beses bawat tatlong linggo. At kung mayroon kang 200 server, kung gayon ang site ay hindi gumagana bawat linggo. Problema ito.

Ano ang maaaring gawin upang mapabuti ang fault tolerance ng isang SQL server? Inaanyayahan tayo ng Wikipedia na bumuo mataas na magagamit na kumpol: kung saan sa kaso ng pagkabigo ng alinman sa mga bahagi ay mayroong isang backup.

Nangangailangan ito ng isang fleet ng mamahaling kagamitan: maraming duplication, optical fiber, shared storage, at ang pagsasama ng isang reserba ay hindi gumagana nang maaasahan: humigit-kumulang 10% ng mga switching ay nagtatapos sa pagkabigo ng backup node tulad ng isang tren sa likod ng pangunahing node.

Ngunit ang pangunahing kawalan ng naturang mataas na magagamit na kumpol ay zero availability kung nabigo ang data center kung saan ito matatagpuan. Ang Odnoklassniki ay may apat na data center, at kailangan naming tiyakin ang operasyon kung sakaling magkaroon ng kumpletong pagkabigo sa isa sa mga ito.

Para dito maaari naming gamitin Multi-Master pagtitiklop na binuo sa SQL Server. Ang solusyon na ito ay mas mahal dahil sa gastos ng software at naghihirap mula sa mga kilalang problema sa pagtitiklop - hindi nahuhulaang pagkaantala ng transaksyon na may kasabay na pagtitiklop at pagkaantala sa paglalapat ng mga replikasyon (at, bilang resulta, nawalang mga pagbabago) na may asynchronous na pagtitiklop. Ang ipinahiwatig manu-manong paglutas ng salungatan ginagawang ganap na hindi naaangkop sa amin ang opsyong ito.

Ang lahat ng mga problemang ito ay nangangailangan ng isang radikal na solusyon, at sinimulan naming pag-aralan ang mga ito nang detalyado. Dito kailangan nating kilalanin kung ano ang pangunahing ginagawa ng SQL Server - mga transaksyon.

Simpleng transaksyon

Isaalang-alang natin ang pinakasimpleng transaksyon, mula sa punto ng view ng isang inilapat na SQL programmer: pagdaragdag ng isang larawan sa isang album. Ang mga album at litrato ay nakaimbak sa iba't ibang mga plato. Ang album ay may pampublikong photo counter. Pagkatapos ang naturang transaksyon ay nahahati sa mga sumusunod na hakbang:

  1. Ni-lock namin ang album sa pamamagitan ng susi.
  2. Lumikha ng isang entry sa talahanayan ng larawan.
  3. Kung ang larawan ay may pampublikong katayuan, pagkatapos ay magdagdag ng pampublikong counter ng larawan sa album, i-update ang talaan at gawin ang transaksyon.

O sa pseudocode:

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

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

TX.commit();

Nakikita namin na ang pinakakaraniwang senaryo para sa isang transaksyon sa negosyo ay ang pagbabasa ng data mula sa database sa memorya ng application server, baguhin ang isang bagay at i-save ang mga bagong halaga pabalik sa database. Kadalasan sa ganoong transaksyon ay nag-a-update kami ng ilang entity, ilang table.

Kapag nagsasagawa ng isang transaksyon, maaaring mangyari ang kasabay na pagbabago ng parehong data mula sa ibang system. Halimbawa, maaaring magpasya ang Antispam na kahit papaano ay kahina-hinala ang user at samakatuwid ang lahat ng mga larawan ng user ay hindi na dapat na pampubliko, kailangan nilang ipadala para sa pagmo-moderate, na nangangahulugang pagpapalit ng photo.status sa ibang value at i-off ang mga kaukulang counter. Malinaw, kung ang operasyon na ito ay nangyayari nang walang mga garantiya ng atomicity ng aplikasyon at paghihiwalay ng mga nakikipagkumpitensyang pagbabago, tulad ng sa ACID, kung gayon ang resulta ay hindi ang kailangan - alinman sa photo counter ay magpapakita ng maling halaga, o hindi lahat ng mga larawan ay ipapadala para sa pag-moderate.

Maraming katulad na code, na nagmamanipula ng iba't ibang entidad ng negosyo sa loob ng isang transaksyon, ay naisulat sa buong pagkakaroon ng Odnoklassniki. Batay sa karanasan ng paglilipat sa NoSQL mula sa Pangwakas na pagkakapare-pareho Alam namin na ang pinakamalaking hamon (at pamumuhunan sa oras) ay nagmumula sa pagbuo ng code upang mapanatili ang pagkakapare-pareho ng data. Samakatuwid, isinasaalang-alang namin ang pangunahing kinakailangan para sa bagong imbakan upang maging probisyon para sa mga tunay na transaksyon ng ACID para sa lohika ng aplikasyon.

Ang iba, hindi gaanong mahalaga, ang mga kinakailangan ay:

  • Kung nabigo ang data center, dapat na available ang parehong pagbabasa at pagsulat sa bagong storage.
  • Pagpapanatili ng kasalukuyang bilis ng pag-unlad. Iyon ay, kapag nagtatrabaho sa isang bagong imbakan, ang halaga ng code ay dapat na humigit-kumulang pareho; hindi na kailangang magdagdag ng anuman sa imbakan, bumuo ng mga algorithm para sa paglutas ng mga salungatan, pagpapanatili ng mga pangalawang index, atbp.
  • Ang bilis ng bagong storage ay kailangang masyadong mataas, kapwa kapag nagbabasa ng data at kapag nagpoproseso ng mga transaksyon, na epektibong nangangahulugan na ang mahigpit na akademiko, pangkalahatan, ngunit mabagal na mga solusyon, gaya ng, halimbawa, ay hindi naaangkop. two-phase commit.
  • Awtomatikong on-the-fly scaling.
  • Gamit ang mga regular na murang server, nang hindi na kailangang bumili ng kakaibang hardware.
  • Posibilidad ng pag-unlad ng imbakan ng mga developer ng kumpanya. Sa madaling salita, ang priyoridad ay ibinigay sa pagmamay-ari o open source na mga solusyon, mas mabuti sa Java.

Mga desisyon

Sa pagsusuri ng mga posibleng solusyon, nakarating kami sa dalawang posibleng pagpipilian sa arkitektura:

Ang una ay kumuha ng anumang SQL server at ipatupad ang kinakailangang fault tolerance, scaling mechanism, failover cluster, conflict resolution at distributed, maaasahan at mabilis na mga transaksyon sa ACID. Ni-rate namin ang opsyong ito bilang napaka-walang halaga at labor-intensive.

Ang pangalawang opsyon ay ang kumuha ng handa na NoSQL na imbakan na may ipinatupad na scaling, isang failover cluster, paglutas ng salungatan, at ikaw mismo ang magpatupad ng mga transaksyon at SQL. Sa unang tingin, kahit na ang gawain ng pagpapatupad ng SQL, hindi banggitin ang mga transaksyon sa ACID, ay mukhang isang gawain na aabutin ng maraming taon. Ngunit pagkatapos ay napagtanto namin na ang hanay ng tampok na SQL na ginagamit namin sa pagsasanay ay kasing layo ng ANSI SQL Cassandra CQL malayo sa ANSI SQL. Sa mas malapit na pagtingin sa CQL, napagtanto namin na ito ay medyo malapit sa kung ano ang kailangan namin.

Cassandra at CQL

Kaya, ano ang kawili-wili tungkol kay Cassandra, anong mga kakayahan mayroon ito?

Una, dito maaari kang lumikha ng mga talahanayan na sumusuporta sa iba't ibang uri ng data; maaari mong gawin ang SELECT o UPDATE sa pangunahing key.

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

Para matiyak ang pagkakapare-pareho ng replica data, ginagamit ni Cassandra diskarte sa korum. Sa pinakasimpleng kaso, nangangahulugan ito na kapag ang tatlong replika ng parehong hilera ay inilagay sa iba't ibang mga node ng cluster, ang pagsulat ay itinuturing na matagumpay kung ang karamihan ng mga node (iyon ay, dalawa sa tatlo) ay nakumpirma ang tagumpay ng write operation na ito. . Ang data ng row ay itinuturing na pare-pareho kung, kapag nagbabasa, ang karamihan ng mga node ay nasuri at nakumpirma ang mga ito. Kaya, sa tatlong replika, ang kumpleto at instant na pagkakapare-pareho ng data ay ginagarantiyahan kung nabigo ang isang node. Ang diskarte na ito ay nagbigay-daan sa amin na magpatupad ng mas maaasahang pamamaraan: palaging magpadala ng mga kahilingan sa lahat ng tatlong replika, naghihintay ng tugon mula sa dalawang pinakamabilis. Ang huling tugon ng ikatlong replika ay itinapon sa kasong ito. Ang isang node na huli sa pagtugon ay maaaring magkaroon ng malubhang problema - mga preno, pagkolekta ng basura sa JVM, direktang pag-reclaim ng memorya sa kernel ng Linux, pagkabigo ng hardware, pagdiskonekta mula sa network. Gayunpaman, hindi ito nakakaapekto sa mga operasyon o data ng kliyente sa anumang paraan.

Ang diskarte kapag nakipag-ugnayan kami sa tatlong node at nakatanggap ng tugon mula sa dalawa ay tinatawag haka-haka: isang kahilingan para sa mga karagdagang replika ay ipinapadala kahit na bago ito "mahulog".

Ang isa pang benepisyo ng Cassandra ay ang Batchlog, isang mekanismo na nagsisiguro na ang isang batch ng mga pagbabagong gagawin mo ay ganap na nailapat o hindi nalalapat. Ito ay nagpapahintulot sa amin na malutas ang A sa ACID - atomicity sa labas ng kahon.

Ang pinakamalapit na bagay sa mga transaksyon sa Cassandra ay ang tinatawag na β€œmagaan na mga transaksyon". Ngunit malayo sila sa "tunay" na mga transaksyon sa ACID: sa katunayan, ito ay isang pagkakataon na gawin Cas sa data mula sa isang tala lamang, gamit ang consensus gamit ang heavyweight na Paxos protocol. Samakatuwid, ang bilis ng naturang mga transaksyon ay mababa.

Ang kulang namin kay Cassandra

Kaya, kinailangan naming ipatupad ang mga tunay na transaksyon ng ACID sa Cassandra. Gamit ang kung saan madali naming maipapatupad ang dalawa pang maginhawang feature ng classic na DBMS: pare-parehong mabilis na mga index, na magbibigay-daan sa aming magsagawa ng mga seleksyon ng data hindi lamang sa pamamagitan ng pangunahing key, at isang regular na generator ng mga monotonic auto-incrementing ID.

C*One

Kaya isang bagong DBMS ay ipinanganak C*One, na binubuo ng tatlong uri ng mga server node:

  • Imbakan – (halos) karaniwang mga server ng Cassandra na responsable sa pag-iimbak ng data sa mga lokal na disk. Habang lumalaki ang pag-load at dami ng data, ang kanilang dami ay madaling mai-scale sa sampu at daan-daan.
  • Transaction coordinators - tiyakin ang pagpapatupad ng mga transaksyon.
  • Ang mga kliyente ay mga server ng application na nagpapatupad ng mga pagpapatakbo ng negosyo at nagpapasimula ng mga transaksyon. Maaaring mayroong libu-libo ng gayong mga kliyente.

NewSQL = NoSQL+ACID

Ang mga server ng lahat ng uri ay bahagi ng isang karaniwang kumpol, gamitin ang panloob na protocol ng mensahe ng Cassandra upang makipag-usap sa isa't isa at Pagtsismisan para sa pagpapalitan ng impormasyon ng cluster. Gamit ang Heartbeat, natututo ang mga server tungkol sa kapwa pagkabigo, nagpapanatili ng isang solong schema ng data - mga talahanayan, ang kanilang istraktura at pagtitiklop; partitioning scheme, cluster topology, atbp.

Mga Kliyente

NewSQL = NoSQL+ACID

Sa halip na mga karaniwang driver, Fat Client mode ang ginagamit. Ang nasabing node ay hindi nag-iimbak ng data, ngunit maaaring kumilos bilang isang coordinator para sa pagpapatupad ng kahilingan, iyon ay, ang Kliyente mismo ay gumaganap bilang isang coordinator ng mga kahilingan nito: nagtatanong ito ng mga replika ng imbakan at niresolba ang mga salungatan. Ito ay hindi lamang mas maaasahan at mas mabilis kaysa sa karaniwang driver, na nangangailangan ng komunikasyon sa isang remote coordinator, ngunit pinapayagan ka rin na kontrolin ang paghahatid ng mga kahilingan. Sa labas ng isang transaksyon na bukas sa kliyente, ang mga kahilingan ay ipinapadala sa mga repositoryo. Kung ang kliyente ay nagbukas ng isang transaksyon, ang lahat ng mga kahilingan sa loob ng transaksyon ay ipapadala sa coordinator ng transaksyon.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Ang coordinator ay isang bagay na ipinatupad namin para sa C*One mula sa simula. Responsable ito sa pamamahala ng mga transaksyon, mga kandado, at ang pagkakasunud-sunod kung saan inilalapat ang mga transaksyon.

Para sa bawat serbisyong transaksyon, bumubuo ang coordinator ng timestamp: ang bawat kasunod na transaksyon ay mas malaki kaysa sa nakaraang transaksyon. Dahil ang sistema ng paglutas ng salungatan ni Cassandra ay nakabatay sa mga timestamp (ng dalawang magkasalungat na tala, ang may pinakabagong timestamp ay itinuturing na kasalukuyan), ang salungatan ay palaging malulutas pabor sa kasunod na transaksyon. Kaya ipinatupad namin Lamport na relo - isang murang paraan upang malutas ang mga salungatan sa isang distributed system.

Mga kandado

Upang matiyak ang paghihiwalay, napagpasyahan naming gamitin ang pinakasimpleng paraan - mga pessimistic na lock batay sa pangunahing key ng record. Sa madaling salita, sa isang transaksyon, dapat munang i-lock ang isang tala, pagkatapos lamang basahin, baguhin, at i-save. Pagkatapos lamang ng matagumpay na pag-commit, maaaring ma-unlock ang isang talaan upang magamit ito ng mga nakikipagkumpitensyang transaksyon.

Ang pagpapatupad ng naturang pagla-lock ay simple sa isang hindi naipamahagi na kapaligiran. Sa isang distributed system, mayroong dalawang pangunahing opsyon: alinman sa ipatupad ang distributed locking sa cluster, o ipamahagi ang mga transaksyon upang ang mga transaksyong kinasasangkutan ng parehong record ay palaging naseserbisyuhan ng parehong coordinator.

Dahil sa aming kaso ang data ay naipamahagi na sa mga grupo ng mga lokal na transaksyon sa SQL, napagpasyahan na magtalaga ng mga lokal na grupo ng transaksyon sa mga coordinator: ang isang coordinator ay nagsasagawa ng lahat ng mga transaksyon na may mga token mula 0 hanggang 9, ang pangalawa - na may mga token mula 10 hanggang 19, at iba pa. Bilang resulta, ang bawat isa sa mga instance ng coordinator ay nagiging master ng pangkat ng transaksyon.

Pagkatapos ay maaaring ipatupad ang mga kandado sa anyo ng isang banal na HashMap sa memorya ng coordinator.

Mga pagkabigo ng coordinator

Dahil ang isang coordinator ay eksklusibong nagsisilbi sa isang pangkat ng mga transaksyon, napakahalaga na mabilis na matukoy ang katotohanan ng pagkabigo nito upang ang pangalawang pagtatangka na isagawa ang transaksyon ay mag-time out. Upang gawin itong mabilis at maaasahan, gumamit kami ng isang ganap na konektadong quorum hearbeat protocol:

Ang bawat data center ay nagho-host ng hindi bababa sa dalawang coordinator node. Paminsan-minsan, ang bawat coordinator ay nagpapadala ng isang heartbeat message sa iba pang mga coordinator at ipinapaalam sa kanila ang tungkol sa paggana nito, pati na rin kung aling mga heartbeat na mensahe ang natanggap nito mula sa kung aling mga coordinator sa cluster huling beses.

NewSQL = NoSQL+ACID

Ang pagtanggap ng katulad na impormasyon mula sa iba bilang bahagi ng kanilang mga mensahe sa tibok ng puso, ang bawat coordinator ay magpapasya para sa kanyang sarili kung aling mga cluster node ang gumagana at kung alin ang hindi, ginagabayan ng prinsipyo ng korum: kung ang node X ay nakatanggap ng impormasyon mula sa karamihan ng mga node sa cluster tungkol sa normal pagtanggap ng mga mensahe mula sa node Y, pagkatapos , gumagana ang Y. At vice versa, sa sandaling ang karamihan ay nag-ulat ng mga nawawalang mensahe mula sa node Y, pagkatapos ay tumanggi si Y. Nakakapagtataka na kung ipaalam ng korum sa node X na hindi na ito nakakatanggap ng mga mensahe mula rito, ituturing mismo ng node X na nabigo ang sarili.

Ang mga mensahe ng heartbeat ay ipinapadala nang may mataas na dalas, mga 20 beses bawat segundo, na may tagal na 50 ms. Sa Java, mahirap igarantiya ang pagtugon sa application sa loob ng 50 ms dahil sa maihahambing na haba ng mga pag-pause na dulot ng tagakolekta ng basura. Nagawa naming makamit ang oras ng pagtugon na ito gamit ang G1 garbage collector, na nagbibigay-daan sa aming tumukoy ng target para sa tagal ng mga pag-pause ng GC. Gayunpaman, kung minsan, medyo bihira, ang kolektor ay nag-pause ng higit sa 50 ms, na maaaring humantong sa isang maling pagtuklas ng pagkakamali. Upang maiwasang mangyari ito, hindi nag-uulat ang coordinator ng pagkabigo ng isang malayong node kapag nawala ang unang mensahe ng tibok ng puso mula rito, kung ilang sunod-sunod na nawala. Ito ay kung paano namin natukoy ang pagkabigo ng coordinator node noong 200 MS.

Ngunit hindi sapat upang mabilis na maunawaan kung aling node ang tumigil sa paggana. May kailangan tayong gawin tungkol dito.

Pagpapareserba

Ang klasikong pamamaraan ay nagsasangkot, sa kaganapan ng isang master failure, pagsisimula ng isang bagong halalan gamit ang isa sa uso unibersal mga algorithm. Gayunpaman, ang mga naturang algorithm ay may mga kilalang problema sa time convergence at ang haba ng mismong proseso ng halalan. Naiwasan namin ang mga ganoong karagdagang pagkaantala gamit ang scheme ng pagpapalit ng coordinator sa isang ganap na konektadong network:

NewSQL = NoSQL+ACID

Sabihin nating gusto nating magsagawa ng transaksyon sa pangkat 50. Tukuyin natin nang maaga ang scheme ng kapalit, iyon ay, kung aling mga node ang magsasagawa ng mga transaksyon sa pangkat 50 kung sakaling mabigo ang pangunahing coordinator. Ang aming layunin ay mapanatili ang functionality ng system kung sakaling magkaroon ng pagkabigo sa data center. Tukuyin natin na ang unang reserba ay isang node mula sa isa pang data center, at ang pangalawang reserba ay isang node mula sa isang ikatlo. Pinipili ang scheme na ito nang isang beses at hindi nagbabago hanggang sa magbago ang topology ng cluster, iyon ay, hanggang sa mapasok ito ng mga bagong node (na napakabihirang mangyari). Ang pamamaraan para sa pagpili ng isang bagong aktibong master kung ang luma ay nabigo ay palaging ang mga sumusunod: ang unang reserba ay magiging aktibong master, at kung ito ay tumigil sa paggana, ang pangalawang reserba ay magiging aktibong master.

Ang pamamaraan na ito ay mas maaasahan kaysa sa unibersal na algorithm, dahil upang maisaaktibo ang isang bagong master sapat na upang matukoy ang kabiguan ng luma.

Ngunit paano mauunawaan ng mga kliyente kung aling master ang nagtatrabaho ngayon? Imposibleng magpadala ng impormasyon sa libu-libong kliyente sa loob ng 50 ms. Ang isang sitwasyon ay posible kapag ang isang kliyente ay nagpadala ng isang kahilingan upang magbukas ng isang transaksyon, hindi pa alam na ang master na ito ay hindi na gumagana, at ang kahilingan ay magtatapos. Upang maiwasang mangyari ito, ang mga kliyente ay nagpapadala ng isang kahilingan na magbukas ng isang transaksyon sa master ng grupo at pareho sa kanyang mga reserba nang sabay-sabay, ngunit ang isa lamang na aktibong master sa ngayon ang tutugon sa kahilingang ito. Gagawin ng kliyente ang lahat ng kasunod na komunikasyon sa loob ng transaksyon lamang sa aktibong master.

Ang mga backup masters ay nakatanggap ng mga kahilingan para sa mga transaksyon na hindi sa kanila sa pila ng mga hindi pa isinisilang na transaksyon, kung saan sila ay naka-imbak nang ilang oras. Kung ang aktibong master ay namatay, ang bagong master ay nagpoproseso ng mga kahilingan upang buksan ang mga transaksyon mula sa pila nito at tumugon sa kliyente. Kung ang kliyente ay nagbukas na ng isang transaksyon sa lumang master, kung gayon ang pangalawang tugon ay hindi pinansin (at, malinaw naman, ang naturang transaksyon ay hindi makukumpleto at uulitin ng kliyente).

Paano gumagana ang transaksyon

Sabihin nating nagpadala ang isang kliyente ng kahilingan sa coordinator na magbukas ng transaksyon para sa ganito at ganoong entity na may ganito at ganoong pangunahing key. Ila-lock ng coordinator ang entity na ito at inilalagay ito sa lock table sa memorya. Kung kinakailangan, binabasa ng coordinator ang entity na ito mula sa storage at iniimbak ang nagresultang data sa isang estado ng transaksyon sa memorya ng coordinator.

NewSQL = NoSQL+ACID

Kapag gustong baguhin ng kliyente ang data sa isang transaksyon, nagpapadala ito ng kahilingan sa coordinator na baguhin ang entity, at inilalagay ng coordinator ang bagong data sa talahanayan ng katayuan ng transaksyon sa memorya. Kinukumpleto nito ang pag-record - walang ginawang pag-record sa storage.

NewSQL = NoSQL+ACID

Kapag ang isang kliyente ay humiling ng sarili nitong binagong data bilang bahagi ng isang aktibong transaksyon, ang coordinator ay kumikilos bilang sumusunod:

  • kung ang ID ay nasa transaksyon na, ang data ay kinuha mula sa memorya;
  • kung walang ID sa memorya, pagkatapos ay ang nawawalang data ay binabasa mula sa mga storage node, na sinamahan ng mga nasa memorya na, at ang resulta ay ibibigay sa kliyente.

Kaya, mababasa ng kliyente ang sarili nitong mga pagbabago, ngunit hindi nakikita ng ibang mga kliyente ang mga pagbabagong ito, dahil naka-imbak lamang sila sa memorya ng coordinator; wala pa sila sa mga Cassandra node.

NewSQL = NoSQL+ACID

Kapag nagpadala ang kliyente ng commit, ang estado na nasa memorya ng serbisyo ay ise-save ng coordinator sa isang naka-log na batch, at ipapadala bilang isang naka-log na batch sa Cassandra storage. Ginagawa ng mga tindahan ang lahat ng kailangan upang matiyak na ang paketeng ito ay atomically (ganap) na inilapat, at nagbabalik ng tugon sa coordinator, na naglalabas ng mga kandado at nagkukumpirma ng tagumpay ng transaksyon sa kliyente.

NewSQL = NoSQL+ACID

At para mag-rollback, kailangan lang palayain ng coordinator ang memory na inookupahan ng estado ng transaksyon.

Bilang resulta ng mga pagpapabuti sa itaas, ipinatupad namin ang mga prinsipyo ng ACID:

  • Atomicity. Ito ay isang garantiya na walang transaksyon na bahagyang itatala sa system; alinman sa lahat ng mga suboperasyon nito ay makukumpleto, o walang makukumpleto. Sumusunod kami sa prinsipyong ito sa pamamagitan ng naka-log na batch sa Cassandra.
  • Hindi pagbabago. Ang bawat matagumpay na transaksyon, ayon sa kahulugan, ay nagtatala lamang ng mga wastong resulta. Kung, pagkatapos magbukas ng isang transaksyon at magsagawa ng bahagi ng mga operasyon, natuklasan na ang resulta ay hindi wasto, ang isang rollback ay isinasagawa.
  • Isolation. Kapag ang isang transaksyon ay naisakatuparan, ang mga kasabay na transaksyon ay hindi dapat makaapekto sa kinalabasan nito. Ang mga nakikipagkumpitensyang transaksyon ay nakahiwalay gamit ang mga pessimistic na lock sa coordinator. Para sa mga pagbabasa sa labas ng isang transaksyon, ang prinsipyo ng paghihiwalay ay sinusunod sa antas ng Read Committed.
  • Tibay. Anuman ang mga problema sa mas mababang antasβ€”system blackout, hardware failureβ€”ang mga pagbabagong ginawa ng matagumpay na nakumpletong transaksyon ay dapat manatiling mapangalagaan kapag nagpapatuloy ang mga operasyon.

Pagbasa sa pamamagitan ng mga index

Kumuha tayo ng isang simpleng talahanayan:

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

Mayroon itong ID (primary key), may-ari at petsa ng pagbabago. Kailangan mong gumawa ng napakasimpleng kahilingan - pumili ng data sa may-ari na may petsa ng pagbabago "para sa huling araw".

SELECT *
WHERE owner=?
AND modified>?

Upang mabilis na maproseso ang naturang query, sa isang klasikong SQL DBMS kailangan mong bumuo ng index ayon sa mga column (may-ari, binago). Madali namin itong magagawa, dahil mayroon na kaming mga garantiya ng ACID!

Mga index sa C*One

May source table na may mga litrato kung saan ang record ID ang pangunahing key.

NewSQL = NoSQL+ACID

Para sa isang index, ang C*One ay gumagawa ng bagong talahanayan na isang kopya ng orihinal. Ang key ay kapareho ng index expression, at kasama rin dito ang pangunahing key ng record mula sa source table:

NewSQL = NoSQL+ACID

Ngayon ang query para sa "may-ari para sa huling araw" ay maaaring muling isulat bilang isang pili mula sa isa pang talahanayan:

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

Ang pagkakapare-pareho ng data sa mga larawan ng source table at ang index table i1 ay awtomatikong pinapanatili ng coordinator. Batay sa schema ng data lamang, kapag ang isang pagbabago ay natanggap, ang coordinator ay bumubuo at nag-iimbak ng pagbabago hindi lamang sa pangunahing talahanayan, kundi pati na rin sa mga kopya. Walang mga karagdagang aksyon na ginagawa sa index table, hindi binabasa ang mga log, at walang ginagamit na lock. Iyon ay, ang pagdaragdag ng mga index ay gumagamit ng halos walang mapagkukunan at halos walang epekto sa bilis ng paglalapat ng mga pagbabago.

Gamit ang ACID, nagawa naming ipatupad ang mga index na tulad ng SQL. Ang mga ito ay pare-pareho, scalable, mabilis, composable, at nakapaloob sa CQL query language. Walang mga pagbabago sa application code ang kinakailangan upang suportahan ang mga index. Ang lahat ay kasing simple ng sa SQL. At ang pinakamahalaga, ang mga index ay hindi nakakaapekto sa bilis ng pagpapatupad ng mga pagbabago sa orihinal na talahanayan ng transaksyon.

Anong nangyari

Binuo namin ang C*One tatlong taon na ang nakakaraan at inilunsad ito sa komersyal na operasyon.

Ano ang nakuha natin sa huli? Suriin natin ito gamit ang halimbawa ng photo processing at storage subsystem, isa sa pinakamahalagang uri ng data sa isang social network. Hindi namin pinag-uusapan ang mga katawan ng mga larawan mismo, ngunit tungkol sa lahat ng uri ng meta-impormasyon. Ngayon ang Odnoklassniki ay may humigit-kumulang 20 bilyong tulad ng mga rekord, ang sistema ay nagpoproseso ng 80 libong mga kahilingan sa pagbasa bawat segundo, hanggang sa 8 libong mga transaksyon sa ACID bawat segundo na nauugnay sa pagbabago ng data.

Noong ginamit namin ang SQL na may replication factor = 1 (ngunit sa RAID 10), ang metainformation ng larawan ay nakaimbak sa isang mataas na magagamit na kumpol ng 32 machine na tumatakbo sa Microsoft SQL Server (kasama ang 11 backup). 10 server din ang inilaan para sa pag-iimbak ng mga backup. Isang kabuuang 50 mamahaling sasakyan. Kasabay nito, ang sistema ay nagpapatakbo sa na-rate na pagkarga, nang walang reserba.

Pagkatapos lumipat sa bagong system, nakatanggap kami ng replication factor = 3 - isang kopya sa bawat data center. Ang system ay binubuo ng 63 Cassandra storage node at 6 na coordinator machine, para sa kabuuang 69 na server. Ngunit ang mga makinang ito ay mas mura, ang kanilang kabuuang gastos ay halos 30% ng halaga ng isang SQL system. Kasabay nito, ang pagkarga ay pinananatiling 30%.

Sa pagpapakilala ng C*One, nabawasan din ang latency: sa SQL, tumagal ng humigit-kumulang 4,5 ms ang isang write operation. Sa C*One - mga 1,6 ms. Ang tagal ng transaksyon ay nasa average na mas mababa sa 40 ms, ang commit ay nakumpleto sa 2 ms, ang read at write na tagal ay nasa average na 2 ms. 99th percentile - 3-3,1 ms lang, bumaba ng 100 beses ang bilang ng mga timeout - lahat dahil sa malawakang paggamit ng haka-haka.

Sa ngayon, karamihan sa mga SQL Server node ay na-decommission na; ang mga bagong produkto ay ginagawa lamang gamit ang C*One. Inangkop namin ang C*One para gumana sa aming cloud isang ulap, na naging posible upang mapabilis ang pag-deploy ng mga bagong cluster, pasimplehin ang configuration at i-automate ang operasyon. Kung wala ang source code, ang paggawa nito ay magiging mas mahirap at masalimuot.

Ngayon ay nagsusumikap kami sa paglilipat ng aming iba pang mga pasilidad ng imbakan sa cloud - ngunit iyon ay isang ganap na naiibang kuwento.

Pinagmulan: www.habr.com

Magdagdag ng komento