NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Соңғы уақытқа дейін Одноклассники нақты уақыт режимінде SQL серверінде өңделген шамамен 50 ТБ деректерді сақтады. Мұндай көлем үшін SQL ДҚБЖ көмегімен жылдам және сенімді, тіпті деректер орталығының істен шығуына төзімді қол жеткізуді қамтамасыз ету мүмкін емес. Әдетте, мұндай жағдайларда NoSQL қоймаларының бірі пайдаланылады, бірақ барлығын NoSQL-ге көшіру мүмкін емес: кейбір нысандар ACID транзакция кепілдіктерін талап етеді.

Бұл бізді NewSQL қоймасын, яғни ақауларға төзімділікті, масштабтауды және NoSQL жүйелерінің өнімділігін қамтамасыз ететін, бірақ сонымен бірге классикалық жүйелерге таныс ACID кепілдіктерін сақтайтын ДҚБЖ пайдалануға әкелді. Бұл жаңа класстың жұмыс істейтін өндірістік жүйелері аз, сондықтан біз мұндай жүйені өзіміз енгізіп, оны коммерциялық пайдалануға бердік.

Бұл қалай жұмыс істейді және не болды - кесінді астында оқыңыз.

Бүгінгі таңда Одноклассники ай сайынғы аудиториясы 70 миллионнан астам бірегей келушілерді құрайды. Біз Үздік бестікке ендік әлемдегі ең ірі әлеуметтік желілер және пайдаланушылар ең көп уақыт өткізетін жиырма сайттың қатарында. OK инфрақұрылымы өте жоғары жүктемелерді өңдейді: бір фронтқа миллионнан астам HTTP сұраулары/сек. 8000-нан астам бөліктен тұратын серверлік флоттың бөліктері бір-біріне жақын орналасқан - төрт Мәскеу деректер орталығында, бұл олардың арасындағы 1 мс-ден аз желілік кешігуге мүмкіндік береді.

Біз Кассандраны 2010 жылдан бастап 0.6 нұсқасынан бастап қолданамыз. Бүгінде бірнеше ондаған кластерлер жұмыс істейді. Ең жылдам кластер секундына 4 миллионнан астам операцияны өңдейді, ал ең үлкені 260 ТБ сақтайды.

Дегенмен, бұл барлық сақтау үшін пайдаланылатын қарапайым NoSQL кластерлері әлсіз үйлестірілген деректер. Біз Одноклассники негізі қаланған кезден бері қолданылып келе жатқан Microsoft SQL Server негізгі тұрақты сақтау орнын ауыстырғымыз келді. Сақтау 300-ден астам SQL Server Standard Edition машиналарынан тұрды, оларда 50 ТБ деректер - шаруашылық субъектілері болды. Бұл деректер ACID транзакцияларының бөлігі ретінде өзгертілген және талап етеді жоғары консистенциясы.

Деректерді SQL Server түйіндері бойынша тарату үшін біз тік және көлденең екеуін де қолдандық бөлу (бөлшектеу). Тарихи түрде біз қарапайым деректерді бөлу схемасын қолдандық: әрбір нысан токенмен байланысты болды - нысан идентификаторының функциясы. Бірдей таңбалауышы бар нысандар бір SQL серверіне орналастырылды. Негізгі және еншілес жазбалардың таңбалауыштары әрқашан сәйкес келетін және бір серверде орналасуы үшін негізгі-деталь қатынасы жүзеге асырылды. Әлеуметтік желіде барлық дерлік жазбалар пайдаланушы атынан жасалады - бұл бір функционалды ішкі жүйедегі барлық пайдаланушы деректері бір серверде сақталады дегенді білдіреді. Яғни, іскерлік транзакцияға әрқашан дерлік бір SQL серверінің кестелері қатысты, бұл жергілікті ACID транзакцияларын пайдалана отырып, пайдалануды қажет етпей, деректердің сәйкестігін қамтамасыз етуге мүмкіндік берді. баяу және сенімсіз таратылған ACID транзакциялары.

Sharding және SQL жылдамдату арқасында:

  • Біз Шетелдік кілт шектеулерін қолданбаймыз, өйткені бөлшектеу кезінде нысан идентификаторы басқа серверде орналасуы мүмкін.
  • ДҚБЖ процессорындағы қосымша жүктемеге байланысты біз сақталған процедуралар мен триггерлерді қолданбаймыз.
  • Жоғарыда айтылғандардың барлығына және дискіден кездейсоқ оқулардың көп болуына байланысты біз JOIN қолданбаймыз.
  • Транзакциядан тыс біз тығырықтан шығуды азайту үшін Read Uncommitted оқшаулау деңгейін қолданамыз.
  • Біз тек қысқа транзакцияларды орындаймыз (орта есеппен 100 мс қысқа).
  • Біз көп жолды UPDATE және DELETE қолданбаймыз, себебі тығырықтан шығулар саны көп - біз бір уақытта тек бір жазбаны жаңартамыз.
  • Біз әрқашан сұрауларды тек индекстер бойынша орындаймыз - біз үшін кестені толық сканерлеу жоспары бар сұрау дерекқорды шамадан тыс жүктеуді және оның сәтсіздікке ұшырауын білдіреді.

Бұл қадамдар SQL серверлерінің максималды дерлік өнімділігін сығуға мүмкіндік берді. Дегенмен, проблемалар барған сайын көбейе берді. Оларды қарастырайық.

SQL-мен проблемалар

  • Біз өздігінен жазылған үзінділерді пайдаланғандықтан, жаңа үзінділерді қосу әкімшілермен қолмен жасалды. Осы уақыттың барлығында масштабталатын деректер көшірмелері сұрауларға қызмет көрсетпеді.
  • Кестедегі жазбалар саны өскен сайын кірістіру және өзгерту жылдамдығы төмендейді; бар кестеге индекстерді қосқанда, жылдамдық коэффициентке төмендейді; индекстерді құру және қайта құру бос уақытпен жүреді.
  • Өндірісте SQL Server үшін Windows жүйесінің аз көлемінің болуы инфрақұрылымды басқаруды қиындатады

Бірақ басты мәселе

ақауларға төзімділік

Классикалық SQL серверінің ақауларға төзімділігі нашар. Сізде бір ғана дерекқор сервері бар делік және ол үш жылда бір рет істен шығады. Осы уақыт ішінде сайт 20 минут бойы жұмыс істемейді, бұл қолайлы. Егер сізде 64 сервер болса, сайт үш аптада бір рет жұмыс істемейді. Ал егер сізде 200 сервер болса, онда сайт апта сайын жұмыс істемейді. Бұл мәселе.

SQL серверінің ақауларға төзімділігін жақсарту үшін не істеуге болады? Википедия бізді құруға шақырады жоғары қолжетімді кластер: құрамдастардың кез келгені істен шыққан жағдайда резервтік көшірме болады.

Бұл қымбат жабдықтар паркін қажет етеді: көптеген қайталаулар, оптикалық талшықтар, ортақ сақтау және резервті қосу сенімді жұмыс істемейді: коммутациялардың шамамен 10% негізгі түйіннің артындағы пойыз сияқты резервтік түйіннің істен шығуымен аяқталады.

Бірақ мұндай қол жетімді кластердің басты кемшілігі, егер ол орналасқан деректер орталығы істен шыққан болса, нөлдік қолжетімділік болып табылады. Одноклассникиде төрт дата орталығы бар және олардың біреуінде толық істен шыққан жағдайда жұмысты қамтамасыз ету керек.

Бұл үшін біз пайдалана аламыз Мульти-мастер SQL серверіне қондырылған репликация. Бұл шешім бағдарламалық жасақтаманың құнына байланысты әлдеқайда қымбатқа түседі және репликацияның белгілі мәселелерінен зардап шегеді - синхронды репликациямен транзакцияның болжанбаған кідірістері және асинхронды репликациямен репликацияларды қолданудағы кешігулер (және, нәтижесінде жоғалған модификациялар). Ұсынылған қақтығыстарды қолмен шешу бұл опцияны біз үшін мүлдем жарамсыз етеді.

Осы мәселелердің барлығы түбегейлі шешімді талап етті, біз оларды жан-жақты талдай бастадық. Мұнда біз SQL Server негізінен немен айналысатынымен - транзакциялармен танысуымыз керек.

Қарапайым транзакция

Қолданбалы SQL бағдарламашысының көзқарасы бойынша ең қарапайым транзакцияны қарастырайық: альбомға фотосурет қосу. Альбомдар мен фотосуреттер әртүрлі табақтарда сақталады. Альбомда жалпыға қолжетімді фото есептегіш бар. Содан кейін мұндай транзакция келесі қадамдарға бөлінеді:

  1. Біз альбомды кілт арқылы блоктаймыз.
  2. Фото кестеде жазба жасаңыз.
  3. Фотосуреттің жалпыға ортақ мәртебесі болса, альбомға жалпы фото есептегішті қосыңыз, жазбаны жаңартыңыз және транзакцияны жасаңыз.

Немесе псевдокодта:

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

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

TX.commit();

Іскерлік транзакцияның ең көп таралған сценарийі дерекқордан қолданбалы сервер жадына деректерді оқу, бір нәрсені өзгерту және жаңа мәндерді қайтадан дерекқорға сақтау екенін көреміз. Әдетте мұндай транзакцияда біз бірнеше субъектілерді, бірнеше кестелерді жаңартамыз.

Транзакцияны орындау кезінде басқа жүйеден бір деректердің бір мезгілде модификациясы орын алуы мүмкін. Мысалы, Антиспам пайдаланушы қандай да бір түрде күдікті деп шешуі мүмкін, сондықтан пайдаланушының барлық фотосуреттері бұдан былай жалпыға қолжетімді болмауы керек, олар модерацияға жіберілуі керек, бұл photo.status параметрін басқа мәнге өзгертуді және сәйкес есептегіштерді өшіруді білдіреді. Әлбетте, егер бұл операция қолдану атомдылығына кепілдіксіз және бәсекелес модификацияларды оқшаулаусыз орындалса, мысалы ACID, содан кейін нәтиже қажет болмайды - не фото есептегіш қате мәнді көрсетеді немесе барлық фотосуреттер модерацияға жіберілмейді.

Бір транзакция аясында әртүрлі бизнес субъектілерін манипуляциялайтын көптеген ұқсас кодтар Одноклассники бүкіл өмірінде жазылған. NoSQL-ге көшу тәжірибесіне негізделген Соңғы сәйкестік Біз ең үлкен қиындық (және уақытты инвестициялау) деректердің сәйкестігін сақтау үшін кодты әзірлеуден туындайтынын білеміз. Сондықтан біз жаңа жадқа қойылатын негізгі талап қолданбалы логика үшін нақты ACID транзакцияларын қамтамасыз ету деп қарастырдық.

Басқа, маңызды емес талаптар:

  • Деректер орталығы сәтсіз болса, жаңа жадқа оқу және жазу қолжетімді болуы керек.
  • Ағымдағы даму жылдамдығын сақтау. Яғни, жаңа репозиториймен жұмыс істегенде, кодтың көлемі шамамен бірдей болуы керек, репозиторийге ештеңе қосудың қажеті жоқ, қайшылықтарды шешу алгоритмдерін әзірлеу, қайталама индекстерді сақтау және т.б.
  • Жаңа жадтың жылдамдығы деректерді оқу кезінде де, транзакцияларды өңдеу кезінде де айтарлықтай жоғары болуы керек еді, бұл академиялық тұрғыдан қатаң, әмбебап, бірақ баяу шешімдердің, мысалы, қолдануға болмайтынын білдіреді. екі фазалы міндеттемелер.
  • Автоматты масштабтау.
  • Экзотикалық жабдықты сатып алудың қажеті жоқ қарапайым арзан серверлерді пайдалану.
  • Компания әзірлеушілерінің сақтауды дамыту мүмкіндігі. Басқаша айтқанда, басымдық меншікті немесе ашық бастапқы шешімдерге берілді, жақсырақ Java тілінде.

Шешімдер, шешімдер

Ықтимал шешімдерді талдай отырып, біз екі ықтимал архитектуралық таңдауға келдік:

Біріншісі - кез келген SQL серверін қабылдау және қажетті ақауларға төзімділікті, масштабтау механизмін, ауыстырып-қосу кластерін, қақтығыстарды шешуді және таратылған, сенімді және жылдам ACID транзакцияларын жүзеге асыру. Біз бұл опцияны өте тривиальды емес және көп еңбекті қажет ететін деп бағаладық.

Екінші нұсқа - іске асырылған масштабтауы бар дайын NoSQL жадын алу, ауыстырып-қосу кластері, қақтығыстарды шешу және транзакциялар мен SQL-ті өзіңіз жүзеге асыру. Бір қарағанда, тіпті ACID транзакцияларын айтпағанның өзінде, SQL-ді енгізу міндеті де жылдарға созылатын тапсырма сияқты көрінеді. Бірақ содан кейін біз тәжірибеде қолданатын SQL мүмкіндіктер жиынтығы ANSI SQL-ден соншалықты алыс екенін түсіндік Кассандра CQL ANSI SQL-ден алыс. CQL тілін тереңірек қарастыра отырып, біз оның бізге қажет нәрсеге жақын екенін түсіндік.

Кассандра және CQL

Сонымен, Кассандраның не қызығы бар, оның қандай мүмкіндіктері бар?

Біріншіден, мұнда әртүрлі деректер түрлерін қолдайтын кестелер жасауға болады; негізгі кілтте ТАҢДАУ немесе ЖАҢАРТУ орындауға болады.

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

Көшіру деректерінің сәйкестігін қамтамасыз ету үшін Кассандра пайдаланады кворум тәсілі. Ең қарапайым жағдайда, бұл кластердің әртүрлі түйіндеріне бір жолдың үш репликасы орналастырылған кезде, егер түйіндердің көпшілігі (яғни, үшеуінің екеуі) осы жазу әрекетінің сәттілігін растаса, жазу сәтті деп есептеледі. . Оқу кезінде түйіндердің көпшілігі сұралған және оларды растаған болса, жол деректері дәйекті деп саналады. Осылайша, үш репликамен бір түйін сәтсіз болған жағдайда толық және жылдам деректер сәйкестігіне кепілдік беріледі. Бұл тәсіл бізге анағұрлым сенімді схеманы жүзеге асыруға мүмкіндік берді: әрқашан ең жылдам екі репликадан жауап күтіп, барлық үш репликаға сұраныс жіберіңіз. Үшінші репликаның кеш жауабы бұл жағдайда жойылады. Жауап беруді кешіктірген түйінде күрделі мәселелер болуы мүмкін - тежегіштер, JVM-де қоқыс жинау, Linux ядросындағы тікелей жадты қалпына келтіру, аппараттық құралдың ақаулығы, желіден ажырату. Дегенмен, бұл клиенттің әрекеттеріне немесе деректеріне ешқандай әсер етпейді.

Үш түйінмен байланысып, екеуінен жауап алғандағы тәсіл деп аталады алыпсатарлық: қосымша көшірмелерді сұрау тіпті «түсіп кетпей тұрып» жіберіледі.

Кассандраның тағы бір артықшылығы - Batchlog, ол сіз енгізген өзгертулер топтамасының толығымен қолданылуын немесе мүлде қолданылмауын қамтамасыз ететін механизм. Бұл бізге ACID-дегі А-ны шешуге мүмкіндік береді - қораптан тыс атомдық.

Кассандрадағы транзакцияларға ең жақын нәрсе - бұл «жеңіл транзакциялар«. Бірақ олар «нақты» ACID транзакцияларынан алыс: іс жүзінде бұл мүмкіндік CAS ауыр салмақты Paxos протоколын пайдалана отырып, консенсусты пайдалана отырып, тек бір жазбадан алынған деректер бойынша. Сондықтан мұндай транзакциялардың жылдамдығы төмен.

Кассандрада бізге не жетіспеді

Сонымен, біз Кассандрада нақты ACID транзакцияларын жүзеге асыруға тура келді. Оның көмегімен біз классикалық ДҚБЖ-ның басқа екі ыңғайлы мүмкіндігін оңай іске асыра аламыз: тек бастапқы кілт арқылы ғана емес деректерді таңдауды орындауға мүмкіндік беретін дәйекті жылдам индекстер және монотонды автоматты ұлғайтылатын идентификаторлардың тұрақты генераторы.

C*Бір

Осылайша жаңа ДҚБЖ дүниеге келді C*Бір, сервер түйіндерінің үш түрінен тұрады:

  • Сақтау – жергілікті дискілерде деректерді сақтауға жауапты (дерлік) стандартты Cassandra серверлері. Деректердің жүктемесі мен көлемі өскен сайын олардың санын ондаған және жүздегенге дейін оңай масштабтауға болады.
  • Мәміле координаторлары – транзакциялардың орындалуын қамтамасыз ету.
  • Клиенттер – іскери операцияларды жүзеге асыратын және транзакцияларды бастайтын қолданба серверлері. Мұндай клиенттер мыңдаған болуы мүмкін.

NewSQL = NoSQL+ACID

Барлық түрдегі серверлер ортақ кластердің бөлігі болып табылады, бір-бірімен байланысу үшін ішкі Cassandra хабарлама протоколын пайдаланыңыз және Сплетни кластерлік ақпарат алмасу үшін. Heartbeat көмегімен серверлер өзара сәтсіздіктер туралы біледі, деректердің бірыңғай схемасын - кестелерді, олардың құрылымын және репликациясын сақтайды; бөлу схемасы, кластер топологиясы және т.б.

клиенттер

NewSQL = NoSQL+ACID

Стандартты драйверлердің орнына Fat Client режимі пайдаланылады. Мұндай түйін деректерді сақтамайды, бірақ сұранысты орындау үшін үйлестіруші ретінде әрекет ете алады, яғни Клиенттің өзі оның сұрауларының үйлестірушісі ретінде әрекет етеді: ол сақтау көшірмелерін сұрайды және қайшылықтарды шешеді. Бұл қашықтағы үйлестірушімен байланысты қажет ететін стандартты драйверге қарағанда сенімдірек және жылдамырақ қана емес, сонымен қатар сұраныстарды жіберуді басқаруға мүмкіндік береді. Клиентте ашық транзакциядан тыс сұраулар репозиторийлерге жіберіледі. Егер клиент транзакцияны ашқан болса, онда транзакция ішіндегі барлық сұраулар транзакция үйлестірушісіне жіберіледі.
NewSQL = NoSQL+ACID

Транзакцияларды үйлестіруші C*One

Үйлестіруші - бұл біз C*One үшін нөлден бастап іске асырған нәрсе. Ол транзакцияларды, құлыптарды және транзакцияларды қолдану тәртібін басқаруға жауапты.

Әрбір қызмет көрсетілетін транзакция үшін үйлестіруші уақыт белгісін жасайды: әрбір келесі транзакция алдыңғы транзакциядан үлкенірек. Кассандраның қақтығыстарды шешу жүйесі уақыт белгілеріне негізделгендіктен (қайшылықты екі жазбадан, соңғы уақыт белгісі ағымдағы болып саналады), қайшылық әрқашан келесі транзакцияның пайдасына шешіледі. Осылайша жүзеге асырдық Lamport сағаты - бөлінген жүйедегі қайшылықтарды шешудің арзан әдісі.

Құлыптар

Оқшаулануды қамтамасыз ету үшін біз ең қарапайым әдісті - жазбаның бастапқы кілтіне негізделген пессимистік құлыптарды қолдануды шештік. Басқаша айтқанда, транзакцияда жазба алдымен құлыпталуы керек, содан кейін ғана оқылады, өзгертіледі және сақталады. Сәтті міндеттемеден кейін ғана жазбаны бәсекелес транзакциялар пайдалана алатындай етіп ашуға болады.

Мұндай құлыптауды жүзеге асыру таратылмаған ортада қарапайым. Бөлінген жүйеде екі негізгі опция бар: не кластерде таратылған құлыптауды енгізу немесе бір жазбаны қамтитын транзакцияларға әрқашан бірдей үйлестіруші қызмет көрсететіндей транзакцияларды тарату.

Біздің жағдайда деректер SQL-дегі жергілікті транзакциялар топтары арасында таратылғандықтан, жергілікті транзакция топтарын үйлестірушілерге тағайындау туралы шешім қабылданды: бір үйлестіруші барлық транзакцияларды 0-ден 9-ға дейінгі маркерлермен орындайды, екіншісі - 10-нан 19-ға дейінгі токендермен, және тағы басқа. Нәтижесінде үйлестіруші даналардың әрқайсысы транзакциялар тобының шебері болады.

Содан кейін құлыптарды үйлестіруші жадында банальды HashMap түрінде іске асыруға болады.

Үйлестіруші сәтсіздіктері

Бір үйлестіруші тек транзакциялар тобына қызмет көрсететіндіктен, транзакцияны орындаудың екінші әрекетінің уақыты аяқталуы үшін оның сәтсіздік фактісін жылдам анықтау өте маңызды. Мұны жылдам және сенімді ету үшін біз толық қосылған кворумдық есту протоколын қолдандық:

Әрбір деректер орталығында кемінде екі үйлестіру түйіні бар. Мерзімді түрде әрбір үйлестіруші басқа үйлестірушілерге жүрек соғысы туралы хабарлама жібереді және олардың жұмыс істеуі туралы, сондай-ақ соңғы рет кластердегі қай үйлестірушілерден жүрек соғысы туралы хабарламаларды алғаны туралы хабарлайды.

NewSQL = NoSQL+ACID

Жүрек соғуы туралы хабарламалардың бөлігі ретінде басқалардан ұқсас ақпаратты ала отырып, әрбір үйлестіруші кворум принципін басшылыққа ала отырып, қандай кластер түйіндері жұмыс істейтінін және қайсысы жұмыс істемейтінін өзі шешеді: егер X түйіні кластердегі түйіндердің көпшілігінен қалыпты жағдай туралы ақпаратты алған болса. Y түйінінен хабарламаларды қабылдау, содан кейін , Y жұмыс істейді. Және керісінше, көпшілігі Y түйінінен хабарлар жоқ деп хабарлаған кезде, Y бас тартты. Бір қызығы, егер кворум X түйініне одан хабарларды қабылдамайтыны туралы хабарласа, X түйінінің өзі өзін сәтсіз деп санайды.

Жүрек соғуы туралы хабарламалар жоғары жиілікте, шамамен секундына 20 рет, 50 мс периодымен жіберіледі. Java-да қоқыс жинағыштан туындаған үзілістердің салыстырмалы ұзақтығына байланысты қолданбаның 50 мс ішінде жауап беруіне кепілдік беру қиын. Біз бұл жауап уақытына GC үзілістерінің ұзақтығына мақсатты анықтауға мүмкіндік беретін G1 қоқыс жинағышын пайдалана алдық. Дегенмен, кейде, өте сирек, коллектордың үзілістері 50 мс-ден асады, бұл қате ақауларды анықтауға әкелуі мүмкін. Бұған жол бермеу үшін координатор қашықтағы түйіннің ақауы туралы оның бірінші жүрек соғу хабары жоғалған кезде хабар бермейді, тек бірнешеу қатарынан жоғалып кетсе ғана, біз 200-де үйлестіруші түйінінің ақаулығын анықтай алдық. Ханым.

Бірақ қай түйіннің жұмысын тоқтатқанын тез түсіну жеткіліксіз. Біз бұл туралы бірдеңе істеуіміз керек.

Брондау

Классикалық схема шебер сәтсіздікке ұшыраған жағдайда біреуін пайдаланып жаңа сайлауды бастауды қамтиды сәнді әмбебап алгоритмдер. Дегенмен, мұндай алгоритмдерде уақыттың жақындасуына және сайлау процесінің ұзақтығына қатысты белгілі проблемалар бар. Толық қосылған желіде координаторды ауыстыру схемасын пайдаланып, мұндай қосымша кідірістерді болдырмай алдық:

NewSQL = NoSQL+ACID

50-ші топтағы транзакцияны орындағымыз келеді делік. Алдын ала ауыстыру схемасын анықтайық, яғни негізгі координатор істен шыққан жағдайда 50-топтағы транзакцияларды қандай түйіндер орындайды. Біздің мақсатымыз – деректер орталығының істен шығуы жағдайында жүйенің функционалдығын сақтау. Бірінші резерв басқа деректер орталығының түйіні, ал екінші резерв үшіншіден түйін болатынын анықтайық. Бұл схема бір рет таңдалады және кластердің топологиясы өзгермейінше өзгермейді, яғни оған жаңа түйіндер енгенше (бұл өте сирек болады). Егер ескісі сәтсіз болса, жаңа белсенді шеберді таңдау тәртібі әрқашан келесідей болады: бірінші резерв белсенді шеберге айналады, ал егер ол жұмысын тоқтатқан болса, екінші резерв белсенді шеберге айналады.

Бұл схема әмбебап алгоритмге қарағанда сенімдірек, өйткені жаңа шеберді іске қосу үшін ескінің сәтсіздігін анықтау жеткілікті.

Клиенттер қай шебердің қазір жұмыс істеп жатқанын қалай түсінеді? 50 мс ішінде мыңдаған клиенттерге ақпаратты жіберу мүмкін емес. Клиент транзакцияны ашуға сұрау жібергенде, бұл шебердің енді жұмыс істемейтінін және сұраудың күту уақыты бітетінін әлі білмеген жағдай болуы мүмкін. Бұған жол бермеу үшін клиенттер топтық шеберге және оның екі резервіне бірден транзакцияны ашу туралы сұрау жібереді, бірақ бұл сұранысқа тек қазіргі уақытта белсенді шебер болып табылатын адам ғана жауап береді. Клиент транзакция аясындағы барлық кейінгі байланысты тек белсенді шебермен ғана жасайды.

Сақтық көшірме жасаушылар өздеріне тиесілі емес транзакциялар үшін алынған сұрауларды олар біраз уақыт сақталатын туылмаған транзакциялар кезегіне қояды. Белсенді шебер өлсе, жаңа шебер өз кезегінде транзакцияларды ашу сұрауларын өңдейді және клиентке жауап береді. Егер клиент ескі мастермен транзакцияны ашқан болса, онда екінші жауап еленбейді (және, анық, мұндай транзакция аяқталмайды және клиент қайталайды).

Транзакция қалай жұмыс істейді

Клиент үйлестірушіге анандай және мынадай негізгі кілті бар осындай субъектіге транзакцияны ашу туралы сұрау жіберді делік. Координатор бұл нысанды құлыптайды және оны жадтағы құлыптау кестесіне орналастырады. Қажет болса, үйлестіруші бұл нысанды сақтаудан оқиды және алынған деректерді координатордың жадында транзакция күйінде сақтайды.

NewSQL = NoSQL+ACID

Клиент транзакциядағы деректерді өзгерткісі келгенде, ол үйлестірушіге нысанды өзгертуге сұраныс жібереді және үйлестіруші жадтағы транзакция күйі кестесіне жаңа деректерді орналастырады. Бұл жазуды аяқтайды - жадқа ешқандай жазба жасалмайды.

NewSQL = NoSQL+ACID

Клиент белсенді транзакцияның бөлігі ретінде өзінің өзгертілген деректерін сұрағанда, үйлестіруші келесідей әрекет етеді:

  • егер идентификатор транзакцияда болса, онда деректер жадтан алынады;
  • егер жадта идентификатор болмаса, онда жетіспейтін деректер жадтағылармен біріктіріліп, сақтау түйіндерінен оқылады және нәтиже клиентке беріледі.

Осылайша, клиент өзінің өзгерістерін оқи алады, бірақ басқа клиенттер бұл өзгерістерді көрмейді, өйткені олар тек координатордың жадында сақталады, олар әлі Кассандра түйіндерінде жоқ.

NewSQL = NoSQL+ACID

Клиент міндеттемені жіберген кезде, қызмет жадында болған күйді үйлестіруші журналға жазылған бумада сақтайды және журналға жазылған бума ретінде Кассандра қоймасына жіберіледі. Дүкендер бұл пакеттің атомдық (толығымен) қолданылуын қамтамасыз ету үшін қажеттінің бәрін жасайды және құлыптарды босатып, клиентке транзакцияның сәттілігін растайтын үйлестірушіге жауап қайтарады.

NewSQL = NoSQL+ACID

Ал кері қайтару үшін үйлестірушіге транзакция күйі алып жатқан жадты босату қажет.

Жоғарыда аталған жақсартулар нәтижесінде біз ACID принциптерін іске асырдық:

  • Атомдық. Бұл жүйеде ешқандай транзакцияның ішінара жазылмайтынының кепілі; оның барлық қосалқы операциялары аяқталады немесе ешқайсысы аяқталмайды. Біз бұл принципті Кассандрадағы журналдан жасалған партия арқылы ұстанамыз.
  • Жүйелілік. Әрбір сәтті транзакция анықтамасы бойынша тек жарамды нәтижелерді жазады. Егер транзакцияны ашып, операциялардың бір бөлігін орындағаннан кейін нәтиже жарамсыз екені анықталса, кері қайтару орындалады.
  • Оқшаулау. Мәміле орындалғанда, бір мезгілде жасалатын операциялар оның нәтижесіне әсер етпеуі керек. Бәсекелес транзакциялар үйлестірушіде пессимистік құлыптар арқылы оқшауланады. Транзакциядан тыс оқулар үшін оқшаулау принципі Оқу орындалды деңгейінде сақталады.
  • Тұрақтылық. Төменгі деңгейлердегі мәселелерге қарамастан — жүйенің өшірілуі, аппараттық құралдың ақаулығы — операциялар қайта басталған кезде сәтті аяқталған транзакция арқылы жасалған өзгерістер сақталуы керек.

Көрсеткіштер бойынша оқу

Қарапайым кестені алайық:

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

Оның идентификаторы (негізгі кілт), иесі және өзгерту күні бар. Сізге өте қарапайым сұраныс жасау керек - өзгерту күні «соңғы күн үшін» иесі туралы деректерді таңдаңыз.

SELECT *
WHERE owner=?
AND modified>?

Мұндай сұранысты жылдам өңдеу үшін классикалық SQL ДҚБЖ-да индексті бағандар бойынша құру керек (иесі, өзгертілген). Біз мұны өте оңай жасай аламыз, өйткені бізде қазір ACID кепілдіктері бар!

C*One ішіндегі индекстер

Жазба идентификаторы негізгі кілт болып табылатын фотосуреттері бар бастапқы кесте бар.

NewSQL = NoSQL+ACID

Индекс үшін C*One түпнұсқаның көшірмесі болып табылатын жаңа кестені жасайды. Кілт индекс өрнегімен бірдей және ол бастапқы кестедегі жазбаның негізгі кілтін де қамтиды:

NewSQL = NoSQL+ACID

Енді «соңғы күн иесі» сұрауын басқа кестеден таңдау ретінде қайта жазуға болады:

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

Бастапқы кесте фотосуреттері мен i1 индекс кестесіндегі деректердің сәйкестігін үйлестіруші автоматты түрде сақтайды. Тек деректер схемасына сүйене отырып, өзгерту қабылданған кезде координатор өзгерісті тек негізгі кестеде ғана емес, көшірмелерде де жасайды және сақтайды. Индекс кестесінде ешқандай қосымша әрекеттер орындалмайды, журналдар оқылмайды және құлыптар пайдаланылмайды. Яғни, индекстерді қосу дерлік ресурстарды тұтынбайды және модификацияларды қолдану жылдамдығына іс жүзінде әсер етпейді.

ACID көмегімен біз SQL-тәрізді индекстерді жүзеге асыра алдық. Олар дәйекті, масштабталатын, жылдам, құрастырылатын және CQL сұрау тіліне енгізілген. Индекстерді қолдау үшін қолданба кодына өзгертулер қажет емес. Барлығы SQL-дегідей қарапайым. Ең бастысы, индекстер бастапқы транзакция кестесіне өзгертулердің орындалу жылдамдығына әсер етпейді.

Не болды

Біз үш жыл бұрын C*One әзірлеп, оны коммерциялық пайдалануға енгіздік.

Соңында не алдық? Мұны әлеуметтік желідегі деректердің ең маңызды түрлерінің бірі болып табылатын фотосуреттерді өңдеу және сақтау ішкі жүйесінің мысалы арқылы бағалайық. Біз фотосуреттердің денелері туралы емес, мета-ақпараттың барлық түрлері туралы айтып отырмыз. Қазір Одноклассники-де 20 миллиардқа жуық осындай жазбалар бар, жүйе секундына 80 мың оқу сұрауын өңдейді, деректерді өзгертуге байланысты секундына 8 мың ACID транзакциясына дейін.

Біз репликация коэффициенті = 1 (бірақ RAID 10-да) SQL-ді пайдаланған кезде, фотосуреттің метаақпараты Microsoft SQL Server-мен жұмыс істейтін 32 машинаның жоғары қолжетімді кластерінде сақталды (плюс 11 сақтық көшірме). Сондай-ақ сақтық көшірмелерді сақтау үшін 10 сервер бөлінді. Барлығы 50 қымбат көлік. Бұл ретте жүйе резервсіз, номиналды жүктемеде жұмыс істеді.

Жаңа жүйеге көшкеннен кейін біз репликация коэффициенті = 3 алдық - әрбір деректер орталығында көшірме. Жүйе 63 Cassandra сақтау түйінінен және 6 үйлестіруші машинадан, барлығы 69 серверден тұрады. Бірақ бұл машиналар әлдеқайда арзан, олардың жалпы құны SQL жүйесі құнының шамамен 30% құрайды. Бұл ретте жүктеме 30% деңгейінде сақталады.

C*One енгізген кезде кідіріс те төмендеді: SQL тілінде жазу операциясы шамамен 4,5 мс уақытты алды. C*One ішінде – шамамен 1,6 мс. Транзакция ұзақтығы орта есеппен 40 мс-тен аз, міндеттеме 2 мс-те аяқталады, оқу және жазу ұзақтығы орта есеппен 2 мс. 99-процентиль – небәрі 3-3,1 мс, тайм-аут саны 100 есеге азайды – барлығы алыпсатарлықтардың кең таралуына байланысты.

Қазіргі уақытта SQL Server түйіндерінің көпшілігі қолданыстан шығарылды; жаңа өнімдер тек C*One көмегімен әзірленуде. Біз C*One қолданбасын бұлтта жұмыс істеуге бейімдедік бір бұлт, бұл жаңа кластерлерді орналастыруды жылдамдатуға, конфигурацияны жеңілдетуге және жұмысты автоматтандыруға мүмкіндік берді. Бастапқы кодсыз мұны істеу әлдеқайда қиын және ауыр болар еді.

Қазір біз басқа сақтау орындарын бұлтқа көшіру үстіндеміз, бірақ бұл мүлдем басқа әңгіме.

Ақпарат көзі: www.habr.com

пікір қалдыру