NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Viime aikoihin asti Odnoklassniki tallensi noin 50 TB reaaliajassa käsiteltyä dataa SQL Serveriin. Tällaiselle volyymille on lähes mahdotonta tarjota nopeaa ja luotettavaa ja jopa datakeskuksen vikasietoisuutta SQL DBMS:n avulla. Tyypillisesti tällaisissa tapauksissa käytetään jotakin NoSQL-tallennustilaa, mutta kaikkea ei voi siirtää NoSQL:ään: jotkut entiteetit vaativat ACID-tapahtumatakuita.

Tämä johti NewSQL-tallennustilan käyttöön, eli DBMS-järjestelmään, joka tarjoaa NoSQL-järjestelmien vikasietoisuuden, skaalautuvuuden ja suorituskyvyn, mutta samalla säilyttää klassisille järjestelmille tutut ACID-takuut. Tämän uuden luokan toimivia teollisia järjestelmiä on vähän, joten toteutimme sellaisen itse ja otimme sen kaupalliseen käyttöön.

Kuinka se toimii ja mitä tapahtui - lue alla.

Nykyään Odnoklassnikin kuukausittainen yleisö on yli 70 miljoonaa ainutlaatuista kävijää. Me Olemme viiden parhaan joukossa maailman suurimmat sosiaaliset verkostot ja niiden 8000 sivuston joukossa, joilla käyttäjät viettävät eniten aikaa. OK-infrastruktuuri käsittelee erittäin suuria kuormia: yli miljoona HTTP-pyyntöä sekunnissa edessä. Yli 1 XNUMX kappaleen palvelinkaluston osat sijaitsevat lähellä toisiaan - neljässä Moskovan palvelinkeskuksessa, mikä mahdollistaa alle XNUMX ms:n verkon latenssin niiden välillä.

Olemme käyttäneet Cassandraa vuodesta 2010 alkaen versiosta 0.6. Nykyään toiminnassa on useita kymmeniä klustereita. Nopein klusteri käsittelee yli 4 miljoonaa toimintoa sekunnissa ja suurin varastoi 260 TB.

Nämä ovat kuitenkin tavallisia NoSQL-klustereita, joita käytetään varastointiin heikosti koordinoitu tiedot. Halusimme korvata tärkeimmän yhtenäisen tallennustilan, Microsoft SQL Serverin, joka on ollut käytössä Odnoklassnikin perustamisesta lähtien. Tallennus koostui yli 300 SQL Server Standard Edition -koneesta, jotka sisälsivät 50 TB dataa - liiketoimintakokonaisuuksia. Näitä tietoja muokataan osana ACID-tapahtumia ja vaaditaan korkea konsistenssi.

Tietojen jakamiseen SQL Server -solmujen välillä käytimme sekä pysty- että vaakasuuntaisia osiointi (halkeilu). Historiallisesti käytimme yksinkertaista tietojen jakamismallia: jokaiseen entiteettiin liitettiin tunnus - entiteettitunnuksen funktio. Entiteetit, joilla oli sama merkki, sijoitettiin samalle SQL-palvelimelle. Pää-yksityiskohta -suhde toteutettiin siten, että pää- ja alatietueiden tunnukset aina vastasivat ja sijaitsivat samalla palvelimella. Sosiaalisessa verkostossa lähes kaikki tietueet luodaan käyttäjän puolesta - mikä tarkoittaa, että kaikki yhden toimivan osajärjestelmän käyttäjätiedot tallennetaan yhdelle palvelimelle. Eli liiketapahtumaan liittyi lähes aina taulukoita yhdeltä SQL-palvelimelta, mikä mahdollisti tietojen johdonmukaisuuden varmistamisen paikallisilla ACID-tapahtumilla ilman tarvetta käyttää hidas ja epäluotettava hajautetut ACID-tapahtumat.

Jakamisen ja SQL:n nopeuttamisen ansiosta:

  • Emme käytä vieraiden avainten rajoituksia, koska jakamisen yhteydessä kokonaisuuden tunnus voi sijaita toisella palvelimella.
  • Emme käytä tallennettuja proseduureja ja laukaisimia DBMS-suorittimen lisäkuormituksen vuoksi.
  • Emme käytä JOIN-koodeja kaiken edellä mainitun ja useiden satunnaisten lukujen vuoksi levyltä.
  • Tapahtuman ulkopuolella käytämme Read Uncommitted -eristystasoa vähentääksemme umpikujaa.
  • Suoritamme vain lyhyitä tapahtumia (keskimäärin alle 100 ms).
  • Emme käytä monirivisiä UPDATE- ja DELETE-toimintoja suuren lukkiutumisen vuoksi - päivitämme vain yhden tietueen kerrallaan.
  • Teemme aina kyselyitä vain indekseille - kysely, jossa on täydellinen taulukkoskannaussuunnitelma, tarkoittaa meille tietokannan ylikuormittamista ja sen epäonnistumista.

Näiden vaiheiden ansiosta pystyimme puristamaan lähes maksimaalisen suorituskyvyn SQL-palvelimista. Ongelmat kuitenkin lisääntyivät ja lisääntyivät. Katsotaanpa niitä.

Ongelmia SQL:n kanssa

  • Koska käytimme itse kirjoitettua sirpalointia, järjestelmänvalvojat tekivät uusien sirpaleiden lisäämisen manuaalisesti. Koko tämän ajan skaalautuvat datakopiot eivät olleet palvelupyyntöjä.
  • Taulukon tietueiden määrän kasvaessa lisäys- ja muokkausnopeus laskee; kun indeksejä lisätään olemassa olevaan taulukkoon, nopeus laskee kertoimella; indeksien luominen ja uudelleenluominen tapahtuu seisokkien yhteydessä.
  • Pieni määrä Windows for SQL Serveriä tuotannossa tekee infrastruktuurin hallinnasta vaikeaa

Mutta pääongelma on

vikasietoisuus

Klassisella SQL-palvelimella on huono vikasieto. Oletetaan, että sinulla on vain yksi tietokantapalvelin, ja se epäonnistuu kolmen vuoden välein. Tänä aikana sivusto on poissa käytöstä 20 minuuttia, mikä on hyväksyttävää. Jos sinulla on 64 palvelinta, sivusto on poissa käytöstä kolmen viikon välein. Ja jos sinulla on 200 palvelinta, sivusto ei toimi joka viikko. Tämä on ongelma.

Mitä voidaan tehdä SQL-palvelimen vikasietoisuuden parantamiseksi? Wikipedia kutsuu meitä rakentamaan erittäin saatavilla oleva klusteri: jossa jonkin komponentin vian sattuessa on varmuuskopio.

Tämä vaatii kalliita laitteita: lukuisia kopioita, valokuitua, jaettua tallennustilaa, eikä reservin sisällyttäminen toimi luotettavasti: noin 10 % kytkennöistä päättyy varasolmun epäonnistumiseen kuin juna pääsolmun takana.

Mutta tällaisen erittäin saatavilla olevan klusterin suurin haittapuoli on nolla saatavuus, jos datakeskus, jossa se sijaitsee, epäonnistuu. Odnoklassnikissa on neljä datakeskusta, ja meidän on varmistettava toiminta, jos yhdessä niistä tulee täydellinen vika.

Tätä voisimme käyttää Multi-Master SQL Serveriin sisäänrakennettu replikointi. Tämä ratkaisu on paljon kalliimpi ohjelmistokustannusten vuoksi ja kärsii hyvin tunnetuista replikointiongelmista - ennakoimattomista tapahtumien viiveistä synkronisessa replikaatiossa ja replikaatioiden käyttöönotossa (ja sen seurauksena kadonneissa muokkauksissa) asynkronisen replikoinnin yhteydessä. implisiittinen manuaalinen konfliktien ratkaisu tekee tästä vaihtoehdosta meille täysin sopimattoman.

Kaikki nämä ongelmat vaativat radikaalia ratkaisua, ja aloimme analysoida niitä yksityiskohtaisesti. Täällä meidän on tutustuttava siihen, mitä SQL Server pääasiassa tekee - transaktiot.

Yksinkertainen kauppa

Tarkastellaan yksinkertaisinta tapahtumaa sovelletun SQL-ohjelmoijan näkökulmasta: valokuvan lisäämistä albumiin. Albumit ja valokuvat on tallennettu eri levyille. Albumissa on julkinen valokuvalaskuri. Sitten tällainen tapahtuma jaetaan seuraaviin vaiheisiin:

  1. Lukitsemme albumin avaimella.
  2. Luo merkintä valokuvataulukkoon.
  3. Jos valokuvalla on julkinen tila, lisää julkinen valokuvalaskuri albumiin, päivitä tietue ja sitouta tapahtuma.

Tai pseudokoodissa:

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

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

TX.commit();

Näemme, että liiketapahtuman yleisin skenaario on lukea tiedot tietokannasta sovelluspalvelimen muistiin, muuttaa jotain ja tallentaa uudet arvot takaisin tietokantaan. Yleensä tällaisessa tapahtumassa päivitämme useita kokonaisuuksia, useita taulukoita.

Tapahtumaa suoritettaessa voi tapahtua samanaikaisia ​​muutoksia toisesta järjestelmästä. Esimerkiksi Roskapostin esto saattaa päätellä, että käyttäjä on jotenkin epäilyttävä, ja siksi kaikkien käyttäjän kuvien ei pitäisi olla enää julkisia, vaan ne on lähetettävä moderointiin, mikä tarkoittaa photo.status -tilan muuttamista johonkin muuhun arvoon ja vastaavien laskurien sammuttamista. Ilmeisesti, jos tämä toimenpide tapahtuu ilman takeita sovelluksen atomiteetista ja kilpailevien muutosten eristämisestä, kuten ACID, niin tulos ei ole sitä, mitä tarvitaan - joko valokuvalaskuri näyttää väärän arvon tai kaikkia valokuvia ei lähetetä moderointiin.

Odnoklassnikin koko olemassaolon ajan on kirjoitettu paljon samanlaista koodia, joka manipuloi useita liiketoimintakokonaisuuksia yhdessä tapahtumassa. Perustuu kokemukseen siirtymisestä NoSQL:ään Lopullinen johdonmukaisuus Tiedämme, että suurin haaste (ja aikainvestointi) on koodin kehittäminen tietojen johdonmukaisuuden ylläpitämiseksi. Siksi pidimme uuden tallennustilan päävaatimuksena todellisten ACID-tapahtumien tarjoamista sovelluslogiikassa.

Muita, yhtä tärkeitä vaatimuksia olivat:

  • Jos datakeskus epäonnistuu, sekä lukemisen että kirjoittamisen on oltava käytettävissä uuteen tallennustilaan.
  • Nykyisen kehitysnopeuden säilyttäminen. Toisin sanoen uuden arkiston kanssa työskennellessä koodin määrän tulee olla suunnilleen sama; arkistoon ei pitäisi lisätä mitään, kehittää algoritmeja ristiriitojen ratkaisemiseksi, toissijaisten indeksien ylläpitoon jne.
  • Uuden tallennustilan nopeuden piti olla melko korkea sekä dataa luettaessa että tapahtumia käsiteltäessä, mikä käytännössä tarkoitti sitä, että akateemisesti tiukat, yleismaailmalliset, mutta hitaat ratkaisut, kuten esim. kaksivaiheisia sitoumuksia.
  • Automaattinen skaalaus lennossa.
  • Käytä tavallisia halpoja palvelimia ilman, että sinun tarvitsee ostaa eksoottisia laitteita.
  • Yrityskehittäjien mahdollisuus varastonkehitykseen. Toisin sanoen etusijalle annettiin patentoidut tai avoimen lähdekoodin ratkaisut, mieluiten Javassa.

Päätöksiä, päätöksiä

Analysoimme mahdollisia ratkaisuja, päädyimme kahteen mahdolliseen arkkitehtuurivaihtoehtoon:

Ensimmäinen on ottaa mikä tahansa SQL-palvelin ja toteuttaa vaadittu vikasietoisuus, skaalausmekanismi, vikasietoklusteri, konfliktien ratkaisu ja hajautetut, luotettavat ja nopeat ACID-tapahtumat. Arvioimme tämän vaihtoehdon erittäin ei-triviaaliksi ja työvoimavaltaiseksi.

Toinen vaihtoehto on ottaa valmis NoSQL-tallennustila, jossa on toteutettu skaalaus, vikasietoklusteri, konfliktien ratkaisu ja toteuttaa tapahtumat ja SQL itse. Ensi silmäyksellä jopa SQL:n toteuttamistehtävä, puhumattakaan ACID-tapahtumista, näyttää tehtävältä, joka kestää vuosia. Mutta sitten tajusimme, että käytännössä käyttämämme SQL-ominaisuusjoukko on yhtä kaukana ANSI SQL:stä kuin Cassandra CQL kaukana ANSI SQL:stä. Kun tarkastelimme CQL:ää vielä tarkemmin, huomasimme, että se oli melko lähellä sitä, mitä tarvitsimme.

Cassandra ja CQL

Joten mikä Cassandrassa on mielenkiintoista, mitä ominaisuuksia sillä on?

Ensinnäkin täällä voit luoda taulukoita, jotka tukevat eri tietotyyppejä; voit tehdä SELECT tai UPDATE ensisijaisella avaimella.

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

Cassandra käyttää replikatietojen johdonmukaisuuden varmistamiseksi päätösvaltainen lähestymistapa. Yksinkertaisimmassa tapauksessa tämä tarkoittaa, että kun saman rivin kolme kopiota sijoitetaan klusterin eri solmuihin, kirjoitus katsotaan onnistuneeksi, jos suurin osa solmuista (eli kaksi kolmesta) vahvisti tämän kirjoitustoiminnon onnistumisen. . Rivitietojen katsotaan olevan johdonmukaisia, jos lukemisen aikana suurin osa solmuista on kyselyssä ja varmistettu. Siten kolmen replikan avulla taataan täydellinen ja välitön tietojen johdonmukaisuus, jos yksi solmu epäonnistuu. Tämän lähestymistavan ansiosta pystyimme toteuttamaan entistä luotettavamman järjestelmän: lähetä aina pyynnöt kaikille kolmelle replikalle odottaen vastausta kahdelta nopeimmalta. Tässä tapauksessa kolmannen replikan myöhäinen vastaus hylätään. Myöhässä reagoivassa solmussa voi olla vakavia ongelmia - jarrut, roskien kerääminen JVM:ssä, suora muistin palautus Linux-ytimessä, laitteistovika, yhteyden katkeaminen verkosta. Tämä ei kuitenkaan vaikuta asiakkaan toimintaan tai tietoihin millään tavalla.

Kutsutaan lähestymistapaa, jossa otamme yhteyttä kolmeen solmuun ja saamme vastauksen kahdelta spekulaatiota: ylimääräisiä kopioita koskeva pyyntö lähetetään jo ennen kuin se "putoaa".

Toinen Cassandran etu on Batchlog, mekanismi, joka varmistaa, että tekemäsi muutokset otetaan joko kokonaan käyttöön tai niitä ei sovelleta ollenkaan. Tämä antaa meille mahdollisuuden ratkaista A ACID -atomisuuden heti.

Lähimpänä Cassandra-tapahtumia ovat ns.kevyet kaupat". Mutta ne ovat kaukana "oikeista" ACID-tapahtumista: itse asiassa tämä on mahdollisuus tehdä CAS vain yhden tietueen tiedoilla käyttäen konsensusta raskaansarjan Paxos-protokollaa käyttäen. Siksi tällaisten tapahtumien nopeus on alhainen.

Mitä meiltä puuttui Cassandrassa

Joten meidän piti toteuttaa todellisia ACID-transaktioita Cassandrassa. Näitä käyttämällä voisimme helposti toteuttaa kaksi muuta kätevää klassisen DBMS:n ominaisuutta: johdonmukaiset nopeat indeksit, joiden avulla voimme suorittaa datan valinnat paitsi ensisijaisella avaimella, ja tavallinen monotonisten automaattisesti kasvavien ID-tunnusten generaattori.

Kartio

Näin syntyi uusi DBMS Kartio, joka koostuu kolmen tyyppisistä palvelinsolmuista:

  • Tallennus – (melkein) tavalliset Cassandra-palvelimet, jotka vastaavat tietojen tallentamisesta paikallisille levyille. Datan kuormituksen ja määrän kasvaessa niiden määrä voidaan helposti skaalata kymmeniin ja satoihin.
  • Tapahtumakoordinaattorit - varmistavat tapahtumien suorittamisen.
  • Asiakkaat ovat sovelluspalvelimia, jotka toteuttavat liiketoimintaa ja käynnistävät tapahtumia. Tällaisia ​​asiakkaita voi olla tuhansia.

NewSQL = NoSQL+ACID

Kaikentyyppiset palvelimet ovat osa yhteistä klusteria, käyttävät sisäistä Cassandra-viestiprotokollaa viestiäkseen keskenään ja juoru klusteritietojen vaihtamiseen. Heartbeatin avulla palvelimet oppivat keskinäisistä vioista, ylläpitävät yhtä tietokaaviota - taulukoita, niiden rakennetta ja replikaatiota; osiointikaavio, klusteritopologia jne.

Asiakkaat

NewSQL = NoSQL+ACID

Vakioohjaimien sijaan käytetään Fat Client -tilaa. Tällainen solmu ei tallenna tietoja, vaan voi toimia pyynnön suorittamisen koordinaattorina, eli Asiakas toimii itse pyyntöjensä koordinaattorina: se kyselee tallennuskopioita ja ratkaisee ristiriitoja. Tämä ei ole vain luotettavampi ja nopeampi kuin tavallinen ajuri, joka vaatii yhteydenpitoa etäkoordinaattorin kanssa, vaan antaa myös mahdollisuuden hallita pyyntöjen lähetystä. Asiakkaalla avoimen tapahtuman ulkopuolella pyynnöt lähetetään arkistoihin. Jos asiakas on avannut tapahtuman, kaikki tapahtumaan liittyvät pyynnöt lähetetään tapahtumakoordinaattorille.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Koordinaattori on jotain, jonka otimme käyttöön C*Onelle tyhjästä. Se vastaa tapahtumien, lukkojen ja tapahtumien soveltamisjärjestyksen hallinnasta.

Kullekin hoidetulle tapahtumalle koordinaattori luo aikaleiman: jokainen myöhempi tapahtuma on suurempi kuin edellinen tapahtuma. Koska Cassandran ristiriitojen ratkaisujärjestelmä perustuu aikaleimoihin (kahdesta ristiriitaisesta tietueesta viimeisimmän aikaleiman sisältävää pidetään ajantasaisena), ristiriita ratkaistaan ​​aina seuraavan tapahtuman hyväksi. Näin toteutimme Lamport kello - halpa tapa ratkaista ristiriidat hajautetussa järjestelmässä.

Lukot

Eristyksen varmistamiseksi päätimme käyttää yksinkertaisinta menetelmää - pessimistisiä lukkoja, jotka perustuvat tietueen ensisijaiseen avaimeen. Toisin sanoen tapahtumassa tietue täytyy ensin lukita, vasta sitten lukea, muokata ja tallentaa. Tietueen lukitus voidaan avata vasta onnistuneen toimituksen jälkeen, jotta kilpailevat tapahtumat voivat käyttää sitä.

Tällaisen lukituksen toteuttaminen on yksinkertaista hajautetussa ympäristössä. Hajautetussa järjestelmässä on kaksi päävaihtoehtoa: joko toteuttaa hajautettu lukitus klusterissa tai jakaa tapahtumat niin, että sama koordinaattori palvelee aina samaan tietueeseen liittyviä tapahtumia.

Koska meidän tapauksessamme tiedot on jo jaettu paikallisten tapahtumien ryhmien kesken SQL:ssä, päätettiin osoittaa paikalliset tapahtumaryhmät koordinaattoreille: yksi koordinaattori suorittaa kaikki tapahtumat tunnuksilla 0-9, toinen - tokeneilla 10-19, ja niin edelleen. Tämän seurauksena jokaisesta koordinaattoriinstanssista tulee tapahtumaryhmän isäntä.

Sitten lukot voidaan toteuttaa banaalin HashMapin muodossa koordinaattorin muistissa.

Koordinaattorin vikoja

Koska yksi koordinaattori palvelee yksinomaan tapahtumaryhmää, on erittäin tärkeää määrittää nopeasti sen epäonnistuminen, jotta tapahtuman toinen suoritusyritys aikakatkaistaan. Tehdäksemme tästä nopeaa ja luotettavaa käytimme täysin yhdistettyä quorum hearbeat -protokollaa:

Jokaisessa datakeskuksessa on vähintään kaksi koordinaattorisolmua. Kukin koordinaattori lähettää ajoittain sykeviestin muille koordinaattoreille ja kertoo heille sen toiminnasta sekä siitä, mitä sykeviestejä se on saanut miltä klusterin koordinaattorilta viimeksi.

NewSQL = NoSQL+ACID

Vastaanotettuaan samankaltaista tietoa muilta osana sykeviestiään, jokainen koordinaattori päättää itse mitkä klusterin solmut toimivat ja mitkä eivät, ohjaten päätösvaltaisuuden periaatetta: jos solmu X on saanut tiedon suurimmalta osalta klusterin solmuista normaalista viestien vastaanottaminen solmusta Y, sitten , Y toimii. Ja päinvastoin, heti kun enemmistö ilmoittaa puuttuvista viesteistä solmusta Y, Y on kieltäytynyt. On outoa, että jos koorumi ilmoittaa solmulle X, ettei se enää vastaanota viestejä siltä, ​​solmu X itse pitää itsensä epäonnistuneena.

Sykeviestit lähetetään korkealla taajuudella, noin 20 kertaa sekunnissa, 50 ms:n jaksolla. Javassa on vaikea taata sovelluksen vastausta 50 ms:n sisällä roskankeräimen aiheuttamien taukojen pituuden vuoksi. Pystyimme saavuttamaan tämän vasteajan G1-roskankerääjällä, jonka avulla voimme määrittää tavoitteen GC-taukojen kestolle. Kuitenkin joskus, melko harvoin, keräimen tauot ylittävät 50 ms, mikä voi johtaa virheelliseen vian havaitsemiseen. Tämän estämiseksi koordinaattori ei ilmoita etäsolmun viasta, kun ensimmäinen sykeviesti siitä katoaa, vain jos useita on kadonnut peräkkäin. Näin onnistuimme havaitsemaan koordinaattorisolmun vika vuonna 200 neiti.

Mutta ei riitä, että ymmärrät nopeasti, mikä solmu on lakannut toimimasta. Meidän on tehtävä tälle jotain.

Varaus

Klassisessa järjestelmässä päällikön epäonnistuessa käynnistetään uudet vaalit jollakin seuraavista muodikas universaali algoritmeja. Tällaisilla algoritmeilla on kuitenkin tunnettuja ongelmia ajan konvergenssin ja itse vaaliprosessin pituuden kanssa. Pystyimme välttämään tällaiset ylimääräiset viiveet käyttämällä koordinaattorin korvausjärjestelmää täysin yhdistetyssä verkossa:

NewSQL = NoSQL+ACID

Oletetaan, että haluamme suorittaa tapahtuman ryhmässä 50. Määritetään etukäteen korvausjärjestelmä, eli mitkä solmut suorittavat tapahtumat ryhmässä 50, jos pääkoordinaattori epäonnistuu. Tavoitteemme on ylläpitää järjestelmän toimivuutta datakeskuksen vian sattuessa. Määritetään, että ensimmäinen reservi on solmu toisesta datakeskuksesta ja toinen reservi on solmu kolmannesta. Tämä malli valitaan kerran, eikä se muutu ennen kuin klusterin topologia muuttuu, eli kunnes siihen tulee uusia solmuja (mitä tapahtuu hyvin harvoin). Menettely uuden aktiivisen isäntälaitteen valitsemiseksi, jos vanha epäonnistuu, on aina seuraava: ensimmäisestä reservistä tulee aktiivinen isäntä, ja jos se on lakannut toimimasta, toisesta reservistä tulee aktiivinen isäntä.

Tämä järjestelmä on luotettavampi kuin yleinen algoritmi, koska uuden masterin aktivoimiseksi riittää, että määritetään vanhan vika.

Mutta miten asiakkaat ymmärtävät, mikä mestari työskentelee nyt? Tietoa on mahdotonta lähettää tuhansille asiakkaille 50 ms:ssa. Tilanne on mahdollinen, kun asiakas lähettää tapahtuman avaamispyynnön tietämättä vielä, että tämä isäntä ei enää toimi, ja pyyntö aikakatkaistaan. Tämän estämiseksi asiakkaat lähettävät spekulatiivisesti tapahtuman avaamispyynnön ryhmäisännälle ja hänen molemmille reserveilleen kerralla, mutta tähän pyyntöön vastaa vain se, joka on tällä hetkellä aktiivinen isäntä. Asiakas välittää kaiken myöhemmän tapahtuman sisällä tapahtuvan viestinnän vain aktiivisen isäntälaitteen kanssa.

Varapääkäyttäjät sijoittavat vastaanotetut tapahtumapyynnöt, jotka eivät ole heidän omiaan, syntymättömän tapahtuman jonoon, jossa niitä säilytetään jonkin aikaa. Jos aktiivinen isäntä kuolee, uusi isäntä käsittelee pyynnöt avata tapahtumia jonostaan ​​ja vastaa asiakkaalle. Jos asiakas on jo avannut tapahtuman vanhan pääkäyttäjän kanssa, toinen vastaus jätetään huomioimatta (ja ilmeisesti tällaista tapahtumaa ei suoriteta loppuun ja asiakas toistaa sen).

Kuinka kauppa toimii

Oletetaan, että asiakas lähetti koordinaattorille pyynnön avata tapahtuma sellaiselle ja sellaiselle entiteetille sellaisella ja sellaisella ensisijaisella avaimella. Koordinaattori lukitsee tämän entiteetin ja sijoittaa sen muistiin lukitustaulukkoon. Tarvittaessa koordinaattori lukee tämän entiteetin muistista ja tallentaa tuloksena saadut tiedot tapahtumatilassa koordinaattorin muistiin.

NewSQL = NoSQL+ACID

Kun asiakas haluaa muuttaa tapahtuman tietoja, se lähettää koordinaattorille pyynnön kokonaisuuden muokkaamisesta, ja koordinaattori sijoittaa uudet tiedot tapahtuman tilataulukkoon muistiin. Tämä lopettaa tallennuksen - tallennusta ei tehdä.

NewSQL = NoSQL+ACID

Kun asiakas pyytää omia muuttuneita tietojaan osana aktiivista tapahtumaa, koordinaattori toimii seuraavasti:

  • jos tunnus on jo tapahtumassa, tiedot otetaan muistista;
  • jos muistissa ei ole ID:tä, niin puuttuvat tiedot luetaan tallennussolmuista, yhdistetään muistissa oleviin ja tulos annetaan asiakkaalle.

Asiakas voi siis lukea omat muutokset, mutta muut asiakkaat eivät näe näitä muutoksia, koska ne on tallennettu vain koordinaattorin muistiin, ne eivät ole vielä Cassandra-solmuissa.

NewSQL = NoSQL+ACID

Kun asiakas lähettää vahvistuksen, koordinaattori tallentaa palvelun muistissa olleen tilan lokiin kirjautuneena eränä, ja se lähetetään kirjattuna eränä Cassandra-tallennustilaan. Kaupat tekevät kaikkensa varmistaakseen, että tämä paketti on atomisesti (täysin) käytössä, ja palauttavat vastauksen koordinaattorille, joka vapauttaa lukot ja vahvistaa asiakkaalle kaupan onnistumisen.

NewSQL = NoSQL+ACID

Ja palauttaakseen koordinaattorin tarvitsee vain vapauttaa tapahtumatilan varaama muisti.

Yllä olevien parannusten seurauksena otimme käyttöön ACID-periaatteet:

  • Atomuus. Tämä on tae siitä, että tapahtumaa ei kirjata osittain järjestelmään, joko kaikki sen osatoiminnot on suoritettu tai ei suoriteta yhtään mitään. Noudatamme tätä periaatetta kirjatun erän kautta Cassandrassa.
  • Johdonmukaisuus. Jokainen onnistunut tapahtuma tallentaa määritelmän mukaan vain kelvollisia tuloksia. Jos tapahtuman avaamisen ja osan operaatioiden suorittamisen jälkeen havaitaan, että tulos on virheellinen, suoritetaan palautus.
  • Eristäytyminen. Kun tapahtuma toteutetaan, samanaikaiset tapahtumat eivät saisi vaikuttaa sen lopputulokseen. Kilpailevat tapahtumat eristetään käyttämällä koordinaattorin pessimistisiä lukkoja. Tapahtuman ulkopuolisissa lukemissa eristysperiaatetta noudatetaan Read Committed -tasolla.
  • pysyvyys. Huolimatta alempien tasojen ongelmista – järjestelmän sähkökatkos, laitteistovika – onnistuneesti suoritetun tapahtuman tekemien muutosten tulee säilyä, kun toimintaa jatketaan.

Lukeminen hakemistojen mukaan

Otetaan yksinkertainen taulukko:

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

Siinä on tunnus (ensisijainen avain), omistaja ja muokkauspäivä. Sinun on tehtävä hyvin yksinkertainen pyyntö - valitse tiedot omistajasta muutospäivämäärällä "viime päivä".

SELECT *
WHERE owner=?
AND modified>?

Jotta tällainen kysely voidaan käsitellä nopeasti, klassisessa SQL DBMS:ssä sinun on rakennettava indeksi sarakkeiden mukaan (omistaja, muokattu). Voimme tehdä tämän melko helposti, koska meillä on nyt ACID-takuu!

Indeksit C*Onessa

Valokuvilla on lähdetaulukko, jossa tietueen tunnus on ensisijainen avain.

NewSQL = NoSQL+ACID

Indeksille C*One luo uuden taulukon, joka on kopio alkuperäisestä. Avain on sama kuin indeksilauseke, ja se sisältää myös tietueen ensisijaisen avaimen lähdetaulukosta:

NewSQL = NoSQL+ACID

Nyt kysely "viimeisen päivän omistaja" voidaan kirjoittaa uudelleen valinnaksi toisesta taulukosta:

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

Koordinaattori ylläpitää automaattisesti lähdetaulukkokuvien ja indeksitaulukon i1 tietojen johdonmukaisuutta. Pelkästään tietoskeeman perusteella koordinaattori generoi ja tallentaa muutoksen saatuaan muutoksen paitsi päätaulukkoon myös kopioina. Hakemistotaulukossa ei suoriteta lisätoimintoja, lokeja ei lueta eikä lukituksia käytetä. Toisin sanoen indeksien lisääminen ei kuluta juuri lainkaan resursseja eikä sillä ole käytännössä mitään vaikutusta muutosten käyttöönottonopeuteen.

ACID:n avulla pystyimme toteuttamaan SQL:n kaltaisia ​​indeksejä. Ne ovat johdonmukaisia, skaalautuvia, nopeita, koostettavia ja sisäänrakennettuja CQL-kyselykieleen. Sovelluskoodiin ei vaadita muutoksia indeksien tukemiseksi. Kaikki on yhtä yksinkertaista kuin SQL:ssä. Ja mikä tärkeintä, indeksit eivät vaikuta alkuperäisen tapahtumataulukon muutosten suoritusnopeuteen.

Mitä tapahtui

Kehitimme C*Onen kolme vuotta sitten ja käynnistimme sen kaupalliseen käyttöön.

Mitä saimme lopulta? Arvioidaan tätä käyttämällä esimerkkiä valokuvien käsittely- ja tallennusalijärjestelmästä, joka on yksi sosiaalisen verkoston tärkeimmistä tietotyypeistä. Emme puhu itse valokuvien rungosta, vaan kaikenlaisesta metainformaatiosta. Nyt Odnoklassnikilla on noin 20 miljardia tällaista tietuetta, järjestelmä käsittelee 80 tuhatta lukupyyntöä sekunnissa, jopa 8 tuhatta ACID-tapahtumaa sekunnissa, jotka liittyvät tietojen muokkaamiseen.

Kun käytimme SQL:ää replikointikertoimella = 1 (mutta RAID 10:ssä), valokuvien metatiedot tallennettiin erittäin saatavilla olevaan 32 Microsoft SQL Serveriä käyttävän koneen klusteriin (plus 11 varmuuskopiota). Varmuuskopioiden tallentamiseen varattiin myös 10 palvelinta. Yhteensä 50 kallista autoa. Samaan aikaan järjestelmä toimi nimelliskuormalla, ilman varausta.

Uuteen järjestelmään siirtymisen jälkeen saimme replikointikertoimen = 3 - kopion jokaisessa palvelinkeskuksessa. Järjestelmässä on 63 Cassandra-tallennussolmua ja 6 koordinaattorikonetta, yhteensä 69 palvelinta. Mutta nämä koneet ovat paljon halvempia, niiden kokonaiskustannukset ovat noin 30% SQL-järjestelmän hinnasta. Samalla kuormitus pidetään 30 prosentissa.

C*Onen käyttöönoton myötä myös latenssi väheni: SQL:ssä kirjoitusoperaatio kesti noin 4,5 ms. C*Onessa - noin 1,6 ms. Tapahtuman kesto on keskimäärin alle 40 ms, sitoumus valmistuu 2 ms:ssa, luku- ja kirjoitusaika on keskimäärin 2 ms. 99. prosenttipiste - vain 3-3,1 ms, aikakatkaisujen määrä on vähentynyt 100-kertaiseksi - kaikki johtuu spekuloinnin laajasta käytöstä.

Tähän mennessä suurin osa SQL Server -solmuista on poistettu käytöstä, uusia tuotteita kehitetään vain C*Onella. Mukautimme C*Onen toimimaan pilvessämme yksi pilvi, joka mahdollisti uusien klustereiden käyttöönoton nopeuttamisen, konfiguroinnin yksinkertaistamisen ja toiminnan automatisoinnin. Ilman lähdekoodia tämän tekeminen olisi paljon vaikeampaa ja hankalampaa.

Nyt työskentelemme muiden varastotilojemme siirtämisessä pilveen - mutta se on täysin eri tarina.

Lähde: will.com

Lisää kommentti