NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Až donedávna v Odnoklassniki bylo na SQL Server uloženo asi 50 TB dat zpracovaných v reálném čase. Pro takový objem je téměř nemožné poskytnout rychlý a spolehlivý a dokonce odolný přístup k datovému centru pomocí SQL DBMS. Obvykle se v takových případech používá některý z NoSQL úložišť, ale ne vše lze přenést do NoSQL: některé entity vyžadují záruky transakcí ACID.

To nás vedlo k použití úložiště NewSQL, tedy DBMS, které poskytuje odolnost proti chybám, škálovatelnost a výkon systémů NoSQL, ale zároveň si zachovává záruky ACID známé klasickým systémům. Funkčních průmyslových systémů této nové třídy je málo, proto jsme takový systém sami implementovali a uvedli do komerčního provozu.

Jak to funguje a co se stalo - přečtěte si pod řezem.

Dnes je měsíční publikum Odnoklassniki více než 70 milionů unikátních návštěvníků. My zadejte prvních pět největší sociální sítě na světě a v první dvacítce webů, na kterých uživatelé tráví nejvíce času. Infrastruktura „OK“ zvládá velmi vysoké zatížení: přes milion požadavků HTTP/s na frontu. Části serverového parku v počtu více než 8000 1 kusů jsou umístěny blízko sebe - ve čtyřech moskevských datových centrech, což umožňuje zajistit mezi nimi zpoždění sítě menší než XNUMX ms.

Cassandru používáme od roku 2010, počínaje verzí 0.6. Dnes je v provozu několik desítek klastrů. Nejrychlejší cluster zpracuje přes 4 miliony operací za sekundu, zatímco největší uchovává 260 TB.

To vše jsou však běžné clustery NoSQL používané k ukládání slabě koordinovaná data. Chtěli jsme také nahradit hlavní konzistentní úložiště, Microsoft SQL Server, který se používá od založení Odnoklassniki. Úložiště sestávalo z více než 300 strojů SQL Server Standard Edition, které obsahovaly 50 TB dat – podnikatelských subjektů. Tato data se upravují jako součást transakcí ACID a vyžadují vysoká konzistence.

K distribuci dat mezi uzly SQL Server jsme použili vertikální i horizontální rozdělení (sharding). Historicky jsme používali jednoduché schéma sdílení dat: každá entita byla spojena s tokenem – funkcí ID entity. Entity se stejným tokenem byly umístěny na stejný SQL server. Vztah master-detail byl implementován takovým způsobem, že tokeny hlavních a podřízených záznamů se vždy shodují a jsou umístěny na stejném serveru. V sociální síti jsou téměř všechny záznamy generovány jménem uživatele, což znamená, že všechna uživatelská data v rámci jednoho funkčního subsystému jsou uložena na jednom serveru. To znamená, že obchodní transakce téměř vždy zahrnovala tabulky jednoho SQL serveru, což umožnilo zajistit konzistenci dat pomocí lokálních ACID transakcí, bez nutnosti použití pomalé a nespolehlivé distribuované transakce ACID.

Díky shardingu a zrychlení SQL:

  • Nepoužíváme omezení cizího klíče, protože při shardování může být ID entity umístěno na jiném serveru.
  • Nepoužíváme uložené procedury a triggery kvůli dodatečné zátěži CPU DBMS.
  • Nepoužíváme JOINy ​​kvůli všem výše uvedeným a mnoha náhodným čtením z disku.
  • Mimo transakci používáme úroveň izolace Read Uncommitted, abychom snížili zablokování.
  • Provádíme pouze krátké transakce (v průměru méně než 100 ms).
  • Víceřádkové UPDATE a DELETE nepoužíváme z důvodu velkého počtu uváznutí – aktualizujeme vždy pouze jeden záznam.
  • Dotazy jsou vždy prováděny pouze podle indexů - dotaz s plánem full table scan pro nás znamená přetížení databáze a její výpadek.

Tyto kroky nám umožnily vymáčknout z SQL serverů téměř maximální výkon. Problémů však bylo čím dál tím víc. Pojďme se na ně podívat.

Problémy s SQL

  • Vzhledem k tomu, že jsme používali samostatně psaný sharding, přidávání nových shardů prováděli administrátoři ručně. Po celou tuto dobu repliky škálovatelných dat neobsluhovaly požadavky.
  • S narůstajícím počtem záznamů v tabulce se snižuje rychlost vkládání a modifikace, při přidávání indexů do existující tabulky rychlost násobně klesá, vytváření a opětovné vytváření indexů trvá odstávku.
  • Vzhledem k malému množství Windows pro SQL Server ve výrobě je správa infrastruktury obtížná

Ale hlavní problém je

odolnost proti chybám

Klasický SQL Server má špatnou odolnost proti chybám. Řekněme, že máte pouze jeden databázový server a ten selže každé tři roky. V tuto chvíli je stránka mimo provoz na 20 minut, což je přijatelné. Pokud máte 64 serverů, pak je stránka mimo provoz jednou za tři týdny. A pokud máte 200 serverů, pak web nefunguje každý týden. To je problém.

Co lze udělat pro zlepšení odolnosti serveru SQL proti chybám? Wikipedia nás zve ke stavbě vysoce dostupný cluster: kde v případě poruchy některé z komponent existuje záloha.

To vyžaduje flotilu drahého vybavení: četná duplikace, optická vlákna, sdílené úložiště a zahrnutí rezervy nefunguje spolehlivě: asi 10 % inkluzí končí selháním záložního uzlu s vlakem za hlavním uzlem.

Ale hlavní nevýhodou takto vysoce dostupného clusteru je nulová dostupnost v případě výpadku datového centra, ve kterém se nachází. Odnoklassniki má čtyři datová centra a my potřebujeme zajistit práci v jednom z nich pro případ úplného výpadku.

Na tohle by se mohlo hodit Multi Master replikace zabudovaná do SQL Serveru. Toto řešení je mnohem dražší kvůli ceně softwaru a trpí dobře známými problémy s replikací – nepředvídatelná zpoždění transakcí u synchronní replikace a zpoždění při aplikaci replikace (a v důsledku toho i ztracené úpravy) u asynchronní. implicitní ruční řešení konfliktů činí tuto možnost pro nás zcela nepoužitelnou.

Všechny tyto problémy vyžadovaly zásadní řešení a přistoupili jsme k jejich podrobné analýze. Zde se musíme seznámit s tím, co SQL Server v podstatě dělá – transakcemi.

Jednoduchá transakce

Zvažte nejjednodušší transakci z pohledu aplikovaného SQL programátora: přidání fotografie do alba. Alba a fotografie jsou uloženy v různých deskách. Album má veřejné počítadlo fotografií. Pak je taková transakce rozdělena do následujících kroků:

  1. Album zablokujeme klíčem.
  2. Vytvořte záznam v tabulce fotografií.
  3. Pokud má fotografie veřejný status, vytáhneme počítadlo veřejných fotografií v albu, aktualizujeme záznam a provedeme transakci.

Nebo v pseudokódu:

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

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

TX.commit();

Vidíme, že nejběžnějším scénářem obchodní transakce je načíst data z databáze do paměti aplikačního serveru, něco změnit a uložit nové hodnoty zpět do databáze. Obvykle v takové transakci aktualizujeme několik entit, několik tabulek.

Při provádění transakce může dojít k souběžné úpravě stejných dat z jiného systému. Antispam se například může rozhodnout, že je uživatel nějak podezřelý, a proto by všechny fotografie uživatele již neměly být veřejné, je třeba je poslat k moderování, což znamená změnit photo.status na nějakou jinou hodnotu a vypnout odpovídající čítače. Je zřejmé, že pokud k této operaci dojde bez záruk atomicity aplikace a izolace konkurenčních modifikací, jako v ACID, pak výsledek nebude takový, jaký potřebujete – buď počítadlo fotek ukáže špatnou hodnotu, nebo nebudou všechny fotky odeslány k moderování.

Za celou dobu existence Odnoklassniki bylo napsáno mnoho takového kódu, který manipuluje s různými obchodními subjekty v rámci jedné transakce. Podle zkušeností z migrací na NoSQL s Případná konzistence víme, že největší výzvou (a časově náročná) je potřeba vyvinout kód pro zachování konzistence dat. Za hlavní požadavek na nové úložiště jsme proto považovali zajištění aplikační logiky skutečných ACID transakcí.

Dalšími neméně důležitými požadavky byly:

  • V případě selhání datového centra musí být k dispozici čtení i zápis do nového úložiště.
  • Zachování současné rychlosti vývoje. To znamená, že při práci s novým úložištěm by množství kódu mělo být přibližně stejné, nemělo by být potřeba něco přidávat do úložiště, vyvíjet algoritmy pro řešení konfliktů, údržbu sekundárních indexů atd.
  • Rychlost nového úložiště by měla být dostatečně vysoká, a to jak při čtení dat, tak při zpracování transakcí, což ve skutečnosti znamenalo, že akademicky přísná, univerzální, ale pomalá řešení, jako např. dvoufázové závazky.
  • Automatické škálování za chodu.
  • Pomocí běžných levných serverů, bez nutnosti kupovat exotické kusy železa.
  • Možnost vývoje úložiště vývojáři společnosti. Jinými slovy, přednost byla dána proprietárním nebo open source řešením, nejlépe v Javě.

Rozhodnutí, rozhodnutí

Při analýze možných řešení jsme přišli se dvěma možnými volbami architektury:

První je vzít jakýkoli SQL server a implementovat požadovanou odolnost proti chybám, škálovací mechanismus, klastrování při selhání, řešení konfliktů a distribuované, spolehlivé a rychlé ACID transakce. Tuto možnost jsme vyhodnotili jako vysoce netriviální a časově náročnou.

Druhou možností je vzít si hotové úložiště NoSQL s implementovaným škálováním, failover clustering, řešením konfliktů a implementací transakcí a SQL sami. Na první pohled i úkol implementace SQL, nemluvě o transakcích ACID, vypadá jako úkol na roky. Pak jsme si ale uvědomili, že soubor funkcí SQL, který používáme v praxi, je stejně vzdálený od ANSI SQL jako Cassandra CQL daleko od ANSI SQL. Při bližším pohledu na CQL jsme si uvědomili, že je dostatečně blízko tomu, co potřebujeme.

Cassandra a CQL

Co je tedy na Cassandře zajímavé, jaké funkce má?

Za prvé, zde můžete vytvářet tabulky s podporou různých datových typů, můžete provést SELECT nebo UPDATE pomocí primárního klíče.

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

K zajištění konzistence dat replik používá Cassandra přístup kvora. V nejjednodušším případě to znamená, že při umístění tří replik stejného řádku na různé uzly clusteru je zápis považován za úspěšný, pokud většina uzlů (tj. dva ze tří) potvrdila úspěch této operace zápisu. Data série jsou považována za konzistentní, pokud při čtení byla většina uzlů dotazována a potvrzena. Pokud tedy existují tři repliky, je zaručena úplná a okamžitá konzistence dat, pokud jeden uzel selže. Tento přístup nám umožnil implementovat ještě spolehlivější schéma: vždy posílat požadavky všem třem replikám a čekat na odpověď od dvou nejrychlejších. Pozdní odpověď třetí repliky je pak zahozena. Zároveň uzel, který se opozdí s odpovědí, může mít vážné problémy – brzdy, garbage collection v JVM, přímá rekultivace paměti v linuxovém jádře, selhání hardwaru, odpojení od sítě. Operace a data klientů však nejsou nijak ovlivněny.

Zavolá se přístup, kdy přistupujeme ke třem uzlům a dostáváme odpověď od dvou spekulace: požadavek na extra repliky je odeslán dříve, než „spadne“.

Další výhodou Cassandry je Batchlog, mechanismus, který zajišťuje, že změny, které provedete, budou buď plně aplikovány, nebo ne úplně aplikovány na balíček. To nám umožňuje vyřešit A v ACID - atomicita po vybalení.

Nejblíže k transakcím v Cassandře je tzv.lehké transakce". Ale nejsou ani zdaleka „skutečnými“ transakcemi ACID: ve skutečnosti je to příležitost udělat CAS na údajích pouze jednoho záznamu s použitím konsenzu protokolu těžké váhy Paxos. Rychlost takových transakcí je proto nízká.

Co nám v Cassandře chybělo

Takže jsme museli implementovat skutečné ACID transakce v Cassandře. S jejichž pomocí bychom mohli snadno implementovat další dvě pohodlné funkce klasického DBMS: konzistentní rychlé indexy, které by nám umožnily vybírat data nejen podle primárního klíče, a obvyklý generátor monotónních ID automatického přírůstku.

Kužel

Tak se zrodil nový DBMS Kužel, skládající se ze tří typů serverových uzlů:

  • Úložiště jsou (téměř) standardní servery Cassandra zodpovědné za ukládání dat na místní disky. S rostoucí zátěží a objemem dat lze jejich počet snadno škálovat až na desítky a stovky.
  • Transakční koordinátoři - zajišťují realizaci transakcí.
  • Klienti jsou aplikační servery, které implementují obchodní operace a iniciují transakce. Takových klientů mohou být tisíce.

NewSQL = NoSQL+ACID

Servery všech typů jsou ve společném clusteru, ke komunikaci mezi sebou používají interní protokol zpráv Cassandra drby pro výměnu klastrových informací. S pomocí Heartbeat se servery dozvídají o vzájemných selháních, udržují jednotné datové schéma - tabulky, jejich strukturu a replikaci; schéma rozdělení, topologie clusteru atd.

Zákazníci

NewSQL = NoSQL+ACID

Místo standardních ovladačů je použit režim Fat Client. Takový uzel neukládá data, ale může fungovat jako koordinátor provádění požadavků, to znamená, že sám Klient působí jako koordinátor jeho požadavků: dotazuje se na repliky úložiště a řeší konflikty. Ten je nejen spolehlivější a rychlejší než standardní ovladač, který vyžaduje komunikaci se vzdáleným koordinátorem, ale také umožňuje řídit přenos požadavků. Mimo transakci otevřenou na klientovi jsou požadavky odesílány do úložišť. Pokud klient otevřel transakci, jsou všechny požadavky v rámci transakce odeslány koordinátorovi transakce.
NewSQL = NoSQL+ACID

C*One Transaction Coordinator

Koordinátor je to, co jsme pro C*One implementovali od nuly. Je zodpovědný za správu transakcí, zámků a pořadí, ve kterém jsou transakce aplikovány.

Pro každou obsluhovanou transakci generuje koordinátor časové razítko: každé následující je větší než časové razítko předchozí transakce. Protože systém řešení konfliktů v Cassandře je založen na časových razítkách (ze dvou konfliktních záznamů se považuje za relevantní poslední časové razítko), konflikt bude vždy vyřešen ve prospěch následné transakce. Tak jsme implementovali lamportové hodinky je levný způsob řešení konfliktů v distribuovaném systému.

Zámky

Pro zajištění izolace jsme se rozhodli použít nejjednodušší způsob – pesimistické zámky na primárním klíči záznamu. Jinými slovy, v transakci musí být záznam nejprve uzamčen, teprve poté načten, upraven a uložen. Pouze po úspěšném odevzdání lze záznam odemknout, aby jej mohly používat konkurenční transakce.

Implementace takového zámku je v nedistribuovaném prostředí jednoduchá. V distribuovaném systému existují dva hlavní způsoby: buď implementovat distribuované zamykání na clusteru, nebo distribuovat transakce tak, aby transakce zahrnující stejný záznam byly vždy obsluhovány stejným koordinátorem.

Protože v našem případě jsou data již distribuována mezi skupiny lokálních transakcí v SQL, bylo rozhodnuto přiřadit skupiny lokálních transakcí koordinátorům: jeden koordinátor provádí všechny transakce s tokenem od 0 do 9, druhý - s tokenem od 10 až 19 a tak dále. Výsledkem je, že každá z instancí koordinátora se stane velitelem transakční skupiny.

Poté lze zámky implementovat jako banální HashMap v paměti koordinátora.

Odmítnutí koordinátorů

Vzhledem k tomu, že jeden koordinátor obsluhuje výhradně skupinu transakcí, je velmi důležité rychle zjistit skutečnost jeho selhání, aby opakovaný pokus o provedení transakce byl v časovém limitu. Aby to bylo rychlé a spolehlivé, použili jsme plně propojený protokol tepové frekvence kvora:

Každé datové centrum hostí alespoň dva koordinační uzly. Každý koordinátor pravidelně zasílá zprávu o srdečním tepu ostatním koordinátorům a informuje je o svém fungování a také o tom, kdy naposledy od kterých koordinátorů v clusteru obdržel srdeční zprávy.

NewSQL = NoSQL+ACID

Po obdržení podobných informací od ostatních v rámci svých srdečních zpráv se každý koordinátor sám rozhodne, které uzly clusteru fungují a které ne, a to podle principu kvora: pokud uzel X obdržel od většiny uzlů v clusteru informace o normální příjem zpráv z uzlu Y, pak , Y funguje. A naopak, jakmile většina hlásí chybějící zprávy z uzlu Y, Y selhalo. Zajímavé je, že pokud kvorum sdělí uzlu X, že od něj nedostává žádné další zprávy, pak samotný uzel X se bude považovat za selhání.

Zprávy srdečního tepu jsou odesílány s vysokou frekvencí, asi 20krát za sekundu, s periodou 50 ms. V Javě je obtížné zaručit odezvu aplikace do 50 ms kvůli srovnatelným pauzám způsobeným garbage collectorem. Této doby odezvy se nám podařilo dosáhnout pomocí sběrače odpadu G1, který umožňuje určit cíl pro dobu trvání GC pauz. Někdy, zcela výjimečně, však pauzy kolektoru překročí 50 ms, což může vést k falešné detekci poruchy. Aby se tomu zabránilo, koordinátor nehlásí selhání vzdáleného uzlu, když se ztratí první srdeční zpráva z něj, pouze pokud jich chybí několik za sebou.Takže se nám podařilo detekovat selhání uzlu koordinátora za 200 ms.

Nestačí však rychle pochopit, který uzel přestal fungovat. Je potřeba s tím něco udělat.

Rezervace

Klasické schéma předpokládá, že v případě selhání velitele spustí volbu nového pomocí jednoho z módní univerzální algoritmy. Tyto algoritmy však mají dobře známé problémy s konvergencí v čase a délkou samotného volebního procesu. Podařilo se nám vyhnout se těmto dodatečným zpožděním pomocí schématu výměny koordinátora v plně propojené síti:

NewSQL = NoSQL+ACID

Řekněme, že chceme provést transakci ve skupině 50. Definujme si předem náhradní schéma, tedy které uzly provedou transakce skupiny 50 v případě selhání hlavního koordinátora. Naším cílem je udržet systém v provozu i v případě výpadku datového centra. Definujme, že první rezerva bude uzel z jiného datového centra a druhá rezerva bude uzel ze třetího. Toto schéma se vybere jednou a nezmění se, dokud se nezmění topologie clusteru, tedy dokud do něj nevstoupí nové uzly (což se stává velmi zřídka). Pořadí výběru nového aktivního masteru v případě poruchy starého bude vždy následující: první rezerva se stane aktivním masterem a pokud přestane fungovat, stane se druhá rezerva.

Takové schéma je spolehlivější než univerzální algoritmus, protože k aktivaci nového masteru stačí určit fakt selhání starého.

Jak ale zákazníci pochopí, který z mistrů právě pracuje? Není možné odeslat informace tisícům klientů za 50 ms. Je možné, že klient odešle požadavek na otevření transakce, aniž by ještě věděl, že tento hlavní server již nefunguje, a požadavek se zablokuje po vypršení časového limitu. Aby k tomu nedocházelo, klienti spekulativně posílají požadavek na otevření transakce masterovi skupiny a oběma jejím rezervám najednou, ale na tuto žádost bude reagovat pouze ten, kdo je v danou chvíli aktivním masterem. Veškerou následnou komunikaci v rámci transakce bude klient provádět pouze s aktivním masterem.

Pohotovostní master zařadí přijaté požadavky na transakce, které nejsou jejich vlastní, do fronty nenarozených transakcí, kde jsou na nějakou dobu uloženy. Pokud aktivní hlavní server zemře, nový hlavní server zpracuje požadavky na otevření transakcí ze své fronty a odpoví klientovi. Pokud se klientovi již podařilo otevřít transakci se starým masterem, pak je druhá odpověď ignorována (a samozřejmě taková transakce nebude dokončena a bude klientem opakována).

Jak transakce funguje

Předpokládejme, že klient poslal koordinátorovi požadavek na otevření transakce pro ten a ten subjekt s takovým a takovým primárním klíčem. Koordinátor uzamkne tuto entitu a umístí ji do tabulky zámků v paměti. V případě potřeby koordinátor načte tuto entitu z úložiště a přijatá data uloží do transakčního stavu v paměti koordinátora.

NewSQL = NoSQL+ACID

Když chce klient změnit data v transakci, odešle koordinátorovi požadavek na úpravu entity a koordinátor umístí nová data do tabulky stavu transakce v paměti. Tím je nahrávání dokončeno – do úložiště se nezapisuje.

NewSQL = NoSQL+ACID

Když klient požaduje svá vlastní upravená data jako součást aktivní transakce, koordinátor jedná takto:

  • pokud je ID již v transakci, pak se data převezmou z paměti;
  • pokud v paměti není žádné ID, pak se chybějící data přečtou z uzlů úložiště, zkombinují se s těmi, která jsou již v paměti, a výsledek se vrátí klientovi.

Klient tedy může číst své vlastní změny a ostatní klienti tyto změny nevidí, protože jsou uloženy pouze v paměti koordinátora, nejsou ještě v uzlech Cassandra.

NewSQL = NoSQL+ACID

Když klient odešle potvrzení, stav, který služba měla v paměti, uloží koordinátor do protokolované dávky a odešle do úložiště Cassandra jako protokolovanou dávku. Úložiště udělají vše potřebné, aby byl tento balíček atomicky (plně) aplikován, a vrátí odpověď koordinátorovi, který uvolní zámky a potvrdí klientovi úspěšnost transakce.

NewSQL = NoSQL+ACID

A pro vrácení zpět potřebuje koordinátor pouze uvolnit paměť obsazenou stavem transakce.

V důsledku výše popsaných vylepšení jsme implementovali principy ACID:

  • Atomicita. To je záruka, že žádná transakce nebude v systému částečně opravena, buď budou provedeny všechny jeho dílčí operace, nebo nebude provedena žádná z nich. V našem případě je tento princip dodržen díky logované dávce v Cassandře.
  • Konzistence. Každá úspěšná transakce podle definice potvrzuje pouze platné výsledky. Pokud se po otevření transakce a provedení některé z operací zjistí, že výsledek je neplatný, provede se rollback.
  • Izolace. Když je transakce provedena, paralelní transakce by neměly ovlivnit její výsledek. Souběžné transakce jsou izolovány pesimistickými zámky na koordinátoru. Pro čtení mimo transakci je respektován princip izolace na úrovni Read Committed.
  • Stabilita. Bez ohledu na problémy na nižších úrovních – výpadek systému, porucha hardwaru – by změny provedené úspěšně dokončenou transakcí měly zůstat po obnovení fungování uloženy.

Čtení podle indexů

Vezměme si jednoduchou tabulku:

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

Má ID (primární klíč), vlastníka a datum změny. Je potřeba podat velmi jednoduchý požadavek – vybrat údaje o vlastníkovi s datem změny „za poslední den“.

SELECT *
WHERE owner=?
AND modified>?

Pro rychlé zpracování takového dotazu je v klasickém SQL DBMS potřeba postavit index na sloupcích (vlastník, upraveno). Můžeme to udělat docela jednoduše, protože nyní máme záruky ACID!

Indexy v C*One

Je zde úvodní tabulka s fotografiemi, ve kterých je primárním klíčem ID záznamu.

NewSQL = NoSQL+ACID

Pro index vytvoří C*One novou tabulku, která je kopií původní tabulky. Klíč je stejný jako výraz indexu, ale obsahuje také primární klíč záznamu ze zdrojové tabulky:

NewSQL = NoSQL+ACID

Nyní lze dotaz na „vlastníka za posledních XNUMX hodin“ přepsat jako výběr z jiné tabulky:

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

Konzistence dat mezi fotografiemi zdrojové tabulky a indexem i1 je automaticky udržována koordinátorem. Na základě samotného datového schématu, když je přijata změna, koordinátor vygeneruje a zapamatuje si změnu nejen v hlavní tabulce, ale také změny v kopiích. S indexovou tabulkou se neprovádějí žádné další akce, protokoly se nečtou, zámky se nepoužívají. To znamená, že přidávání indexů téměř nespotřebovává zdroje a prakticky neovlivňuje rychlost aplikace úprav.

S pomocí ACID se nám podařilo implementovat indexy „jako v SQL“. Jsou konzistentní, škálovatelné, rychlé, sestavitelné a zabudované do dotazovacího jazyka CQL. Podpora indexu nevyžaduje žádné změny kódu aplikace. Vše je jednoduché, jako v SQL. A co je nejdůležitější, indexy nemají vliv na rychlost provádění úprav původní tabulky transakcí.

Co se stalo

C*One jsme vyvinuli před třemi lety a uvedli do komerčního provozu.

S čím jsme to skončili? Zhodnoťme to na příkladu subsystému pro zpracování a ukládání fotografií, jednoho z nejdůležitějších typů dat v sociální síti. Tady nejde o těla samotných fotografií, ale o všemožné metainformace. Nyní je v Odnoklassniki asi 20 miliard takových záznamů, systém zpracovává 80 tisíc požadavků na čtení za sekundu, až 8 tisíc ACID transakcí za sekundu souvisejících s úpravou dat.

Když jsme použili SQL s replikačním faktorem = 1 (ale v RAID 10), byly metainformace fotografie uloženy na vysoce dostupném clusteru 32 strojů Microsoft SQL Server (plus 11 náhradních). Také bylo přiděleno 10 serverů pro ukládání záloh. Celkem 50 drahých aut. Systém přitom pracoval při jmenovité zátěži, bez rezervy.

Po migraci na nový systém jsme dostali replikační faktor = 3 – kopie v každém datovém centru. Systém se skládá z 63 úložišť Cassandra a 6 koordinačních strojů, celkem tedy 69 serverů. Ale tyto stroje jsou mnohem levnější, celkem asi 30 % nákladů na SQL systém. V tomto případě je zatížení udržováno na úrovni 30 %.

Se zavedením C*One se také snížila latence: v SQL trvala operace zápisu asi 4,5 ms. V C * Jedna - asi 1,6 ms. Trvání transakce je v průměru méně než 40 ms, potvrzení je dokončeno za 2 ms, délka čtení a zápisu je v průměru 2 ms. 99. percentil je pouze 3-3,1 ms, počet timeoutů se snížil 100krát – to vše kvůli rozšířenému používání spekulací.

K dnešnímu dni byla většina uzlů SQL Serveru vyřazena z provozu, nové produkty jsou vyvíjeny pouze pomocí C * One. Upravili jsme C*One pro práci v našem cloudu jeden-mrak, což umožnilo urychlit nasazování nových clusterů, zjednodušit konfiguraci a automatizovat provoz. Bez zdrojového kódu by to bylo mnohem obtížnější a otřesnější.

Nyní pracujeme na přesunu našich dalších úložišť do cloudu – ale to je úplně jiný příběh.

Zdroj: www.habr.com

Přidat komentář