HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Следващата конференция HighLoad++ ще се проведе на 6 и 7 април 2020 г. в Санкт Петербург.
Подробности и билети по ссылке. HighLoad++ Siberia 2019. Зала "Красноярск". 25 юни, 12:00 часа. Тезиси и представяне.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Случва се практическите изисквания да противоречат на теорията, където не се вземат предвид аспекти, важни за търговския продукт. Тази лекция представя процес за избор и комбиниране на различни подходи за създаване на компоненти за причинно-следствена последователност въз основа на академични изследвания, базирани на изискванията на търговски продукт. Слушателите ще научат за съществуващите теоретични подходи към логически часовници, проследяване на зависимости, сигурност на системата, синхронизация на часовника и защо MongoDB се спря на определени решения.

Михаил Тюленев (наричан по-долу МТ): – Ще говоря за причинно-следствената консистенция – това е функция, върху която работихме в MongoDB. Работя в група за разпределени системи, направихме го преди около две години.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

В процеса трябваше да се запозная с много академични изследвания, тъй като тази функция е проучена доста добре. Оказа се, че нито една статия не се вписва в това, което се изисква в производствена база данни поради много специфични изисквания, които вероятно присъстват във всяко производствено приложение.

Ще говоря за това как ние, като потребители на академични изследвания, приготвяме нещо от тях, което след това можем да представим на нашите потребители като готово ястие, удобно и безопасно за употреба.

Причинно-следствена последователност. Нека дефинираме понятията

Като начало искам да кажа най-общо какво е причинно-следствена консистенция. Има два героя - Леонард и Пени (сериал "Теория за Големия взрив"):

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Да приемем, че Пени е в Европа и Леонард иска да й организира парти изненада. И той не може да измисли нищо по-добро от това да я изхвърли от списъка си с приятели, като изпрати на всичките си приятели актуална информация във емисия: „Нека направим Пени щастлива!“ (тя е в Европа, докато спи, не вижда всичко това и не може да го види, защото я няма). В крайна сметка тя изтрива тази публикация, изтрива я от Feed и възстановява достъпа, така че да не забелязва нищо и да няма скандал.
Всичко това е добре, но нека приемем, че системата е разпределена и нещата са се объркали малко. Може например да се случи ограничаването на достъпа на Penny да е настъпило след появата на тази публикация, ако събитията не са свързани по причина и следствие. Всъщност това е пример за това, когато се изисква причинно-следствена последователност, за да се изпълни бизнес функция (в този случай).

Всъщност това са доста нетривиални свойства на базата данни - много малко хора ги поддържат. Да преминем към моделите.

Модели на последователност

Какво точно представлява моделът на последователност в базите данни? Това са някои от гаранциите, които разпределената система дава за това какви данни може да получи клиентът и в каква последователност.

По принцип всички модели на последователност се свеждат до това колко подобна е една разпределена система на система, която работи например на един възел на лаптоп. И това е колко подобна е система, която работи на хиляди географски разпределени „възли“, на лаптоп, в който всички тези свойства се изпълняват автоматично по принцип.

Следователно моделите на съгласуваност се прилагат само към разпределени системи. Всички системи, които преди са съществували и са работили при едно и също вертикално мащабиране, не са имали подобни проблеми. Имаше един буферен кеш и всичко винаги се четеше от него.

Модел Стронг

Всъщност първият модел е Strong (или линията на възможностите за издигане, както често се нарича). Това е модел на последователност, който гарантира, че всяка промяна, веднъж потвърдена, че е настъпила, е видима за всички потребители на системата.

Това създава глобален ред на всички събития в базата данни. Това е свойство на много силна консистенция и обикновено е много скъпо. Въпреки това е много добре поддържан. Просто е много скъпо и бавно - просто рядко се използва. Това се нарича способност за издигане.

Има друго, по-силно свойство, което се поддържа в Spanner - наречено външна съгласуваност. Ще поговорим за това малко по-късно.

причинен

Следващият е Causal, точно за който говорех. Има още няколко поднива между Силно и Причинно, за които няма да говоря, но всички те се свеждат до Причинно. Това е важен модел, защото е най-силният от всички модели, най-силната консистенция при наличие на мрежа или дялове.

Причинно-следствените връзки всъщност са ситуация, в която събитията са свързани чрез причинно-следствена връзка. Много често те се възприемат като Read your on права от гледна точка на клиента. Ако клиентът е наблюдавал някакви стойности, той не може да види стойности, които са били в миналото. Той вече започва да вижда представки. Всичко се свежда до едно и също.
Причинно-следствените връзки като модел на последователност са частично подреждане на събития на сървъра, при което събитията от всички клиенти се наблюдават в една и съща последователност. В този случай Леонард и Пени.

евентуален

Третият модел е Евентуална последователност. Това е, което поддържат абсолютно всички разпределени системи, минималният модел, който изобщо има смисъл. Това означава следното: когато имаме промени в данните, в един момент те стават последователни.

В такъв момент тя не казва нищо, иначе би се превърнала във Външна последователност - това би било съвсем различна история. Въпреки това, това е много популярен модел, най-често срещаният. По подразбиране всички потребители на разпределени системи използват Евентуална консистенция.

Искам да дам няколко сравнителни примера:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Какво означават тези стрелки?

  • Латентност. Тъй като силата на последователност се увеличава, тя става по-голяма по очевидни причини: трябва да направите повече записи, да получите потвърждение от всички хостове и възли, които участват в клъстера, че данните вече са там. Съответно Eventual Consistency има най-бързия отговор, защото там по правило можете дори да го запаметите и това по принцип ще бъде достатъчно.
  • Наличие. Ако разбираме това като способността на системата да реагира при наличие на мрежови прекъсвания, дялове или някакъв вид повреда, толерантността към грешки се увеличава с намаляването на модела на последователност, тъй като за нас е достатъчно един хост да живее и в същото време времето произвежда някои данни. Eventual Consistency изобщо не гарантира нищо за данните - може да бъде всичко.
  • Аномалии. В същото време, разбира се, броят на аномалиите се увеличава. При силна последователност те почти не би трябвало да съществуват изобщо, но при евентуална последователност могат да бъдат всякакви. Възниква въпросът: защо хората избират Евентуална консистенция, ако тя съдържа аномалии? Отговорът е, че моделите за евентуална съгласуваност са приложими и аномалиите съществуват, например, за кратък период от време; възможно е да използвате съветника за четене и повече или по-малко четене на последователни данни; Често е възможно да се използват модели със силна консистенция. На практика това работи и често броят на аномалиите е ограничен във времето.

CAP теорема

Когато видите думите последователност, наличност – какво ви идва наум? Точно така - CAP теорема! Сега искам да разсея мита... Не съм аз - това е Мартин Клепман, който написа прекрасна статия, прекрасна книга.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Теоремата за CAP е принцип, формулиран през 2000-те, че съгласуваност, наличност, дялове: вземете кои да е две и не можете да изберете три. Беше определен принцип. Тя беше доказана като теорема няколко години по-късно от Гилбърт и Линч. След това това започна да се използва като мантра - системите започнаха да се делят на CA, CP, AP и т.н.

Тази теорема всъщност беше доказана за следните случаи... Първо, наличността не беше разглеждана като непрекъсната стойност от нула до стотици (0 - системата е „мъртва“, 100 - реагира бързо; свикнали сме да я разглеждаме по този начин) , но като свойство на алгоритъма, което гарантира, че за всички свои изпълнения той връща данни.

Изобщо няма дума за време за реакция! Има алгоритъм, който връща данни след 100 години - абсолютно прекрасен наличен алгоритъм, който е част от теоремата на CAP.
Второ: теоремата е доказана за промени в стойностите на един и същ ключ, въпреки факта, че тези промени могат да се променят. Това означава, че в действителност те практически не се използват, тъй като моделите са различни Евентуална консистенция, Силна консистенция (може би).

За какво е всичко това? Освен това теоремата за CAP точно в този вид, в който е доказана, практически не е приложима и се използва рядко. В теоретична форма по някакъв начин ограничава всичко. Оказва се, че определен принцип е интуитивно правилен, но като цяло не е доказан.

Причинно-следствената последователност е най-силният модел

Това, което се случва сега, е, че можете да получите и трите неща: последователност, наличност с помощта на дялове. По-специално, причинно-следствената последователност е най-силният модел на последователност, който все още работи при наличието на дялове (прекъсвания в мрежата). Ето защо има такъв голям интерес и затова се заехме с него.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Първо, това опростява работата на разработчиците на приложения. По-специално, наличието на голяма поддръжка от сървъра: когато всички записи, които се срещат в един клиент, са гарантирани, че ще пристигнат в същата последователност на друг клиент. Второ, издържа на прегради.

Вътрешна кухня на MongoDB

Спомняйки си, че е обяд, се преместваме в кухнята. Ще ви разкажа за модела на системата, а именно какво е MongoDB за тези, които за първи път чуват за такава база данни.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

MongoDB (наричана по-нататък „MongoDB“) е разпределена система, която поддържа хоризонтално мащабиране, тоест шардинг; и в рамките на всеки шард също така поддържа излишък на данни, тоест репликация.

Шардингът в MongoDB (не релационна база данни) извършва автоматично балансиране, тоест всяка колекция от документи (или „таблица“ по отношение на релационни данни) се разделя на части и сървърът автоматично ги премества между шардове.

Query Router, който разпространява заявки, за клиент е някакъв клиент, чрез който работи. Той вече знае къде и какви данни се намират и насочва всички заявки към правилния шард.

Друг важен момент: MongoDB е един главен. Има един първичен - той може да приема записи, които поддържат ключовете, които съдържа. Не можете да правите Multi-master запис.

Направихме версия 4.2 - там се появиха нови интересни неща. По-специално, те вмъкнаха Lucene - търсене - а именно изпълнима java директно в Mongo и там стана възможно да се извършват търсения през Lucene, както в Elastica.

И направиха нов продукт - Графики, има го и в Atlas (собствения облак на Mongo). Имат безплатно ниво - можете да си поиграете с него. Много ми хареса Charts - визуализация на данни, много интуитивно.

Съставки Причинно-следствена консистенция

Преброих около 230 статии, които са публикувани по тази тема - от Лесли Ламперт. Сега по памет ще ви предам някои части от тези материали.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Всичко започна със статия на Лесли Ламперт, написана през 1970-те години. Както можете да видите, някои изследвания по тази тема все още продължават. Сега причинно-следствената последователност изпитва интерес във връзка с развитието на разпределени системи.

Ограничения

Какви ограничения има? Това всъщност е една от основните точки, тъй като ограниченията, които производствената система налага, са много различни от ограниченията, които съществуват в академичните статии. Често са доста изкуствени.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

  • Първо, „MongoDB“ е единичен мастер, както вече казах (това значително опростява).
  • Смятаме, че системата трябва да поддържа около 10 хиляди фрагмента. Не можем да вземем никакви архитектурни решения, които изрично ще ограничат тази стойност.
  • Имаме облак, но предполагаме, че човек все пак трябва да има възможност, когато изтегли двоичен файл, пусне го на лаптопа си и всичко работи чудесно.
  • Ние приемаме нещо, което Research рядко допуска: външните клиенти могат да правят каквото си искат. MongoDB е с отворен код. Съответно клиентите могат да бъдат толкова умни и ядосани - може да искат да счупят всичко. Спекулираме, че може да произхождат от византийските фейлори.
  • За външни клиенти, които са извън периметъра, има важно ограничение: ако тази функция е деактивирана, тогава не трябва да се наблюдава влошаване на производителността.
  • Друг момент е като цяло антиакадемичен: съвместимостта на предишни версии и бъдещи. Старите драйвери трябва да поддържат нови актуализации, а базата данни трябва да поддържа стари драйвери.

Като цяло всичко това налага ограничения.

Компоненти на каузална последователност

Сега ще говоря за някои от компонентите. Ако разгледаме причинно-следствената последователност като цяло, можем да избираме блокове. Избрахме от произведения, които принадлежат към определен блок: Проследяване на зависимости, избор на часовници, как тези часовници могат да бъдат синхронизирани един с друг и как гарантираме сигурност - това е грубо очертание на това, за което ще говоря:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Пълно проследяване на зависимости

Защо е необходимо? Така че когато данните се репликират, всеки запис, всяка промяна на данните съдържа информация за това от какви промени зависи. Първата и наивна промяна е, когато всяко съобщение, което съдържа запис, съдържа информация за предишни съобщения:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

В този пример числото във къдрави скоби е записаните числа. Понякога тези записи със стойности дори се прехвърлят изцяло, понякога се прехвърлят някои версии. Изводът е, че всяка промяна съдържа информация за предишната (очевидно носи всичко това в себе си).

Защо решихме да не използваме този подход (пълно проследяване)? Очевидно, защото този подход е непрактичен: всяка промяна в социална мрежа зависи от всички предишни промени в тази социална мрежа, прехвърляйки, да речем, Facebook или VKontakte при всяка актуализация. Въпреки това има много изследвания относно пълното проследяване на зависимостите – това са предсоциални мрежи; за някои ситуации наистина работи.

Изрично проследяване на зависимости

Следващият е по-ограничен. Тук се разглежда и преносът на информация, но само тази, която е явно зависима. Какво зависи от какво, като правило, се определя от приложението. Когато данните се репликират, заявката връща отговори само когато предишните зависимости са удовлетворени, т.е. показани. Това е същността на това как работи причинно-следствената последователност.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Тя вижда, че запис 5 зависи от записи 1, 2, 3, 4 - съответно тя изчаква преди клиентът да получи достъп до промените, направени от решението за достъп на Penny, когато всички предишни промени вече са преминали през базата данни.

Това също не ни устройва, защото все още има твърде много информация и това ще забави нещата. Има и друг подход...

Часовник Lamport

Те са много стари. Lamport Clock означава, че тези зависимости са сгънати в скаларна функция, която се нарича Lamport Clock.

Скаларната функция е някакво абстрактно число. Често се нарича логическо време. С всяко събитие този брояч се увеличава. Броячът, който в момента е известен на процеса, изпраща всяко съобщение. Ясно е, че процесите могат да не са синхронизирани, могат да имат напълно различно време. Въпреки това системата някак балансира часовника с такива съобщения. Какво се случва в този случай?

Разделих този голям сегмент на две, за да стане ясно: Приятелите могат да живеят в един възел, който съдържа част от колекцията, а Feed може да живее в друг възел, който съдържа част от тази колекция. Ясно ли е как могат да излязат от линията? Първата емисия ще каже: „Репликирано“, а след това Приятели. Ако системата не предоставя някаква гаранция, че Feed няма да се показва, докато зависимостите на Friends в колекцията Friends също не бъдат доставени, тогава ще имаме точно ситуацията, която споменах.

Виждате как времето на брояча на Feed логично се увеличава:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

И така, основното свойство на този часовник на Lamport и причинно-следствена консистенция (обяснено чрез часовника на Lamport) е следното: ако имаме събития A и B и събитие B зависи от събитие A*, тогава следва, че логическото време на събитие A е по-малко от Логическо време от събитие B.

* Понякога казват също, че А се е случило преди Б, тоест А се е случило преди Б - това е определена връзка, която частично подрежда целия набор от събития, които са се случили като цяло.

Обратното е неправилно. Това всъщност е един от основните недостатъци на Lamport Clock – частичният ред. Съществува концепция за едновременни събития, тоест събития, при които нито (А се е случило преди Б), нито (А се е случило преди Б). Пример би бил паралелното добавяне от Леонард на някой друг като приятел (дори не Леонард, а Шелдън, например).
Това е свойство, което често се използва при работа с часовници Lamport: те разглеждат конкретно функцията и от това заключават, че може би тези събития са зависими. Защото един начин е верен: ако LogicalTime A е по-малко от LogicalTime B, тогава B не може да се случи преди A; и ако повече, тогава може би.

Векторен часовник

Логичното развитие на часовника Lamport е Vector Clock. Те се различават по това, че всеки възел, който е тук, съдържа свой собствен отделен часовник и те се предават като вектор.
В този случай виждате, че нулевият индекс на вектора отговаря за Feed, а първият индекс на вектора е за Friends (всеки от тези възли). И сега те ще се увеличат: нулевият индекс на „Feed“ се увеличава при писане – 1, 2, 3:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Защо Vector Clock е по-добър? Тъй като те ви позволяват да разберете кои събития са едновременни и кога се случват на различни възли. Това е много важно за система за шардинг като MongoDB. Ние обаче не избрахме това, въпреки че е прекрасно нещо и работи чудесно и вероятно ще ни пасне...

Ако имаме 10 хиляди шарда, не можем да прехвърлим 10 хиляди компонента, дори ако го компресираме или измислим нещо друго - полезният товар пак ще бъде няколко пъти по-малък от обема на целия този вектор. Затова, стискайки сърца и зъби, изоставихме този подход и преминахме към друг.

Spanner TrueTime. Атомен часовник

Казах, че ще има история за Spanner. Това е страхотно нещо, направо от XNUMX-ви век: атомни часовници, GPS синхронизация.

каква е идеята “Spanner” е система на Google, която наскоро дори стана достъпна за хората (те добавиха SQL към нея). Всяка транзакция има някакво времево клеймо. Тъй като времето е синхронизирано*, на всяко събитие може да се присвои определено време - атомните часовници имат време на изчакване, след което гарантирано ще се "случи" различно време.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

По този начин, чрез просто писане в базата данни и изчакване за известен период от време, възможността за сериализиране на събитието се гарантира автоматично. Те имат най-силния модел на последователност, който може да се представи по принцип - това е външна последователност.

* Това е основният проблем с часовниците на Lampart - те никога не са синхронни в разпределени системи. Те могат да се разминават; дори и с NTP, те все още не работят много добре. "Spanner" има атомен часовник и синхронизацията, изглежда, е микросекунди.

Защо не избрахме? Ние не предполагаме, че нашите потребители имат вграден атомен часовник. Когато се появят, като са вградени във всеки лаптоп, ще има някаква супер яка GPS синхронизация - тогава да... Но засега най-доброто, което е възможно, е Amazon, Base Stations - за фанатици... Затова използвахме други часовници .

Хибриден часовник

Това всъщност е нещото, което работи в MongoDB при осигуряване на причинно-следствена последователност. Как са хибридни? Хибридът е скаларна стойност, но има два компонента:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

  • Първата е Unix епохата (колко секунди са изминали от „началото на компютърния свят“).
  • Второто е някакво увеличение, също 32-битово unsigned int.

Това е всичко, всъщност. Има такъв подход: частта, която отговаря за времето, е синхронизирана с часовника през цялото време; всеки път, когато се появи актуализация, тази част се синхронизира с часовника и се оказва, че времето винаги е повече или по-малко правилно, а нарастването ви позволява да разграничите събития, настъпили в един и същи момент от време.

Защо това е важно за MongoDB? Защото ви позволява да направите някакви резервни ресторанти в определен момент от време, тоест събитието се индексира по време. Това е важно, когато са необходими определени събития; За база данни събитията са промени в базата данни, настъпили на определени интервали от време.

Ще ви кажа най-важната причина само на вас (моля, не казвайте на никого)! Направихме това, защото така изглеждат организираните, индексирани данни в MongoDB OpLog. OpLog е структура от данни, която съдържа абсолютно всички промени в базата данни: те първо отиват в OpLog, а след това се прилагат към самото хранилище в случай, че това е репликирана дата или шард.

Това беше основната причина. Все пак има и практически изисквания за разработване на база данни, което означава, че тя трябва да бъде проста - малко код, възможно най-малко счупени неща, които трябва да бъдат пренаписани и тествани. Фактът, че нашите oplogs бяха индексирани от хибридни часовници, помогна много и ни позволи да направим правилния избор. Наистина се изплати и по някакъв магически начин проработи на първия прототип. Беше много яко!

Синхронизация на часовника

Има няколко метода за синхронизация, описани в научната литература. Говоря за синхронизация, когато имаме два различни шарда. Ако има един комплект реплики, няма нужда от никаква синхронизация: това е „единичен главен“; имаме OpLog, в който попадат всички промени - в този случай всичко вече е последователно подредено в самия “Oplog”. Но ако имаме два различни шарда, времевата синхронизация е важна тук. Тук векторните часовници помогнаха много! Но ние ги нямаме.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Вторият е подходящ - това е „Heartbeats“. Възможно е да се обменят някои сигнали, които се появяват всяка единица време. Но Heartbeats са твърде бавни, не можем да предоставим латентност на нашия клиент.

Истинското време, разбира се, е прекрасно нещо. Но, отново, това вероятно е бъдещето... Въпреки че вече може да се направи в Atlas, вече има бързи времеви синхронизатори на Amazon. Но няма да е достъпно за всички.

Клюкарството е, когато всички съобщения включват време. Това е приблизително това, което използваме. Всяко съобщение между възли, драйвер, рутер на възел за данни, абсолютно всичко за MongoDB е някакъв вид елемент, компонент на база данни, който съдържа часовник, който работи. Те имат значението на хибридно време навсякъде, то се предава. 64 бита? Това позволява, това е възможно.

Как работи всичко заедно?

Тук разглеждам един комплект реплики, за да го направя малко по-лесно. Има първични и вторични. Вторичният изпълнява репликация и не винаги е напълно синхронизиран с основния.

Възниква вмъкване в „Primery“ с определена времева стойност. Това вмъкване увеличава вътрешния брой с 11, ако това е максимумът. Или ще провери стойностите на часовника и ще се синхронизира с часовника, ако стойностите на часовника са по-големи. Това ви позволява да организирате по време.

След като прави записа, настъпва важен момент. Часовникът е в "MongoDB" и се увеличава само в случай на писане в "Oplog". Това е събитието, което променя състоянието на системата. В абсолютно всички класически статии се счита за събитие, когато съобщението достигне възел: съобщението е пристигнало, което означава, че системата е променила състоянието си.

Това се дължи на факта, че по време на изследването не е напълно ясно как ще бъде интерпретирано това съобщение. Знаем със сигурност, че ако не е отразено в „Oplog“, то няма да бъде интерпретирано по никакъв начин и промяната в състоянието на системата е само запис в „Oplog“. Това опростява всичко за нас: моделът го опростява и ни позволява да го организираме в рамките на един комплект реплики и много други полезни неща.

Стойността, която вече е записана в “Oplog”, се връща - знаем, че “Oplog” вече съдържа тази стойност и времето му е 12. Сега, да речем, четенето започва от друг възел (вторичен) и той предава afterClusterTime в съобщението. Той казва: „Имам нужда от всичко, което се случи поне след 12 или по време на дванадесет“ (вижте снимката по-горе).

Това е, което се нарича причинно-следствена връзка (CAT). В теорията има такава концепция, че това е някакъв отрязък от време, който е последователен сам по себе си. В този случай можем да кажем, че това е състоянието на системата, което е наблюдавано в момент 12.

Сега тук все още няма нищо, защото този вид симулира ситуацията, когато имате нужда от вторичния, за да репликирате данни от първичния. Той чака... И сега данните са пристигнали - той връща тези стойности обратно.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

До голяма степен така работи всичко. почти.

Какво значи "почти"? Да приемем, че има някой, който е прочел и разбрал как работи всичко това. Разбрах, че всеки път, когато се появи ClusterTime, той актуализира вътрешния логически часовник и след това следващият запис се увеличава с единица. Тази функция отнема 20 реда. Да кажем, че този човек предава най-голямото 64-битово число, минус едно.

Защо "минус едно"? Тъй като вътрешният часовник ще бъде заменен с тази стойност (очевидно, това е най-голямата възможна и по-голяма от текущото време), тогава ще се появи запис в „Oplog“ и часовникът ще бъде увеличен с друга единица - и вече ще има бъде максимална стойност (просто има всички единици, няма къде другаде да отидете), unsaint ints).

Ясно е, че след това системата става абсолютно недостъпна за нищо. Може само да се разтовари и почисти - много ръчна работа. Пълна наличност:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Освен това, ако това се репликира някъде другаде, тогава целият клъстер просто пада. Абсолютно недопустима ситуация, която всеки може да организира много бързо и лесно! Затова сметнахме този момент за един от най-важните. Как да го предотвратим?

Нашият начин е да подпишем clusterTime

Така се предава в съобщението (преди синия текст). Но ние също започнахме да генерираме подпис (син текст):

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Подписът се генерира от ключ, който се съхранява в базата данни, в защитен периметър; се генерира и актуализира (потребителите не виждат нищо за него). Генерира се хеш и всяко съобщение се подписва при създаване и се валидира при получаване.
Вероятно в съзнанието на хората възниква въпросът: „Колко това забавя нещата?“ Казах ви, че трябва да работи бързо, особено при липсата на тази функция.

Какво означава да се използва причинно-следствена последователност в този случай? Това показва параметъра afterClusterTime. Без това той просто ще предаде стойности така или иначе. Клюкарството, започвайки от версия 3.6, винаги работи.

Ако оставим постоянното генериране на подписи, това ще забави системата дори при липса на функция, която не отговаря на нашите подходи и изисквания. И така, какво направихме?

Направи го бързо!

Това е доста просто нещо, но трикът е интересен - ще го споделя, може някой да се интересува.
Имаме хеш, който съхранява подписаните данни. Всички данни минават през кеша. Кешът не подписва конкретното време, а диапазона. Когато пристигне някаква стойност, ние генерираме Range, маскираме последните 16 бита и подписваме тази стойност:

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Получавайки такъв подпис, ние ускоряваме системата (сравнително) 65 хиляди пъти. Работи чудесно: когато проведохме експерименти, времето всъщност намаля с 10 хиляди пъти, когато имахме последователна актуализация. Ясно е, че когато са в конфликт, това не работи. Но в повечето практически случаи работи. Комбинацията от подписа Range заедно с подписа реши проблема със сигурността.

Какво научихме?

Уроци, които научихме от това:

  • Трябва да четем материали, истории, статии, защото имаме много интересни неща. Когато работим върху някаква функция (особено сега, когато направихме транзакции и т.н.), трябва да прочетем и разберем. Отнема време, но всъщност е много полезно, защото изяснява къде се намираме. Изглежда не измислихме нищо ново – просто взехме съставките.

    Като цяло има известна разлика в мисленето, когато има академична конференция (Sigmon, например) - всеки се фокусира върху нови идеи. Какво е новото в нашия алгоритъм? Тук няма особена новост. Новото по-скоро се крие в начина, по който смесихме съществуващите подходи. Следователно, първото нещо е да прочетете класиката, като започнете с Лампарт.

  • При производството изискванията са съвсем различни. Сигурен съм, че много от вас не се сблъскват със „сферични“ бази данни в абстрактен вакуум, а с нормални, реални неща, които имат проблеми с наличността, латентността и толерантността към грешки.
  • Последното нещо е, че трябваше да разгледаме различни идеи и да комбинираме няколко напълно различни статии в един подход, заедно. Идеята за подписване например идва от статия, която разглежда протокола Paxos, който за не-Byzantine Failors е вътре в протокола за авторизация, за Byzantine - извън протокола за авторизация... Общо взето, точно това е, което ние в крайна сметка направи.

    Тук няма абсолютно нищо ново! Но щом смесихме всичко... Това е все едно да кажем, че рецептата за салата Оливие е глупост, защото яйцата, майонезата и краставиците вече са измислени... Става дума за същата история.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Ще приключа с това. Благодаря ти!

Въпроси

Въпрос от публиката (наричан по-долу Б): – Благодаря ти, Михаил, за репортажа! Интересна е темата за времето. Вие използвате Gosiping. Те казаха, че всеки има свое време, всеки знае своето местно време. Както разбрах, имаме драйвер - може да има много клиенти с драйвери, query-planner също, shards също... И до какво се свежда системата, ако изведнъж имаме несъответствие: някой реши, че е за минута напред, някой минута назад? Къде ще стигнем?

MT: – Страхотен въпрос наистина! Просто исках да поговорим за парчетата. Ако разбирам правилно въпроса, имаме следната ситуация: има шард 1 и шард 2, четенето става от тези два шарда - те имат несъответствие, не взаимодействат помежду си, защото времето, което знаят, е различно, особено времето, през което съществуват в oplogs.
Да кажем, че шард 1 е направил милион записа, фрагмент 2 не е направил нищо и заявката е дошла до два фрагмента. И първият има afterClusterTime над милион. В такава ситуация, както обясних, shard 2 изобщо няма да отговори.

AT: – Исках да знам как се синхронизират и избират едно логично време?

MT: - Много лесен за синхронизиране. Shard, когато afterClusterTime дойде при него и той не намери време в „Oplog“, инициира не одобрен. Тоест той вдига времето си с ръцете си до тази стойност. Това означава, че няма събития, съответстващи на тази заявка. Той създава това събитие изкуствено и по този начин става Причинно-последователен.

AT: – Ами ако след това при него дойдат други събития, които са били изгубени някъде в мрежата?

MT: – Shard е проектиран по такъв начин, че те няма да дойдат отново, тъй като е единичен господар. Ако вече се е записал, тогава те няма да дойдат, но ще дойдат по-късно. Не може да се случи нещо да заседне някъде, след това той да не пише и тогава да пристигнат тези събития - и Причинно-следствената консистенция да бъде нарушена. Когато не пише, всички трябва да дойдат (той ще ги чака).

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

AT: – Имам няколко въпроса относно опашките. Причинно-следствената последователност предполага, че има определена опашка от действия, които трябва да бъдат извършени. Какво се случва, ако един от нашите пакети изчезне? Идва 10-ти, 11-ти... 12-ти изчезна, а всички останали чакат да се сбъдне. И изведнъж колата ни умря, не можем да направим нищо. Има ли максимална дължина на опашката, която се натрупва, преди да бъде изпълнена? Какъв фатален провал възниква, когато се загуби някое състояние? Освен това, ако запишем, че има някакво предишно състояние, тогава трябва някак да започнем от него? Но не го отблъснаха!

MT: – Също страхотен въпрос! Какво правим? MongoDB има концепцията за кворум пише, кворум чете. В какви случаи може да се загуби съобщение? Когато записът не е кворум или когато четенето не е кворум (може също да се залепи някакъв боклук).
По отношение на последователността на причинно-следствената връзка беше проведен голям експериментален тест, резултатът от който беше, че в случай, че записите и четенията са без кворум, възникват нарушения на последователността на причинно-следствената връзка. Точно това, което казвате!

Нашият съвет: използвайте поне четене на кворум, когато използвате причинно-следствена последователност. В този случай нищо няма да бъде загубено, дори ако записът за кворум бъде загубен... Това е ортогонална ситуация: ако потребителят не иска данните да бъдат загубени, той трябва да използва запис за кворум. Причинно-следствената последователност не гарантира дълготрайност. Устойчивостта е гарантирана от репликацията и машините, свързани с репликацията.

AT: – Когато създаваме екземпляр, който извършва шардинг вместо нас (съответно не master, а slave), той разчита на Unix времето на собствената си машина или на времето на „master“; За първи път ли се синхронизира или периодично?

MT: — Сега ще изясня. Shard (т.е. хоризонтален дял) – там винаги има първичен. И един шард може да има „главен“ и може да има реплики. Но шардът винаги поддържа запис, защото трябва да поддържа някакъв домейн (шардът има Primary).

AT: – Значи всичко зависи само от „господаря“? Винаги ли се използва основното време?

MT: - да Образно можете да кажете: часовникът тиктака, когато настъпи влизане в „майстора“, в „Оплог“.

AT: – Имаме клиент, който се свързва и не трябва да знае нищо за времето?

MT: – Изобщо не е нужно да знаете нищо! Ако говорим за това как работи върху клиента: когато клиентът иска да използва причинно-следствената консистенция, той трябва да отвори сесия. Вече всичко е налице: транзакции в сесията и извличане на права... Сесията е подреждането на логически събития, случващи се с клиента.

Ако той отвори тази сесия и каже там, че иска причинно-следствена последователност (ако сесията поддържа причинно-следствена последователност по подразбиране), всичко работи автоматично. Водачът помни това време и го увеличава, когато получи ново съобщение. Той помни какъв отговор е върнал предишният от сървъра, върнал данните. Следващата заявка ще съдържа afterCluster("време, по-голямо от това").

Клиентът не трябва да знае абсолютно нищо! Това е напълно непрозрачно за него. Ако хората използват тези функции, какво могат да направят? Първо, можете безопасно да четете вторични: можете да пишете в Primary и да четете от географски репликирани вторични и да сте сигурни, че работи. В същото време сесиите, които са записани на Primary, могат дори да бъдат прехвърлени на Secondary, т.е. можете да използвате не една сесия, а няколко.

AT: – Нов слой на компютърните науки – типове данни CRDT (Безконфликтни репликирани типове данни) – е тясно свързан с темата за Евентуалната последователност. Обмисляли ли сте да интегрирате тези видове данни в базата данни и какво можете да кажете за това?

MT: - Добър въпрос! CRDT има смисъл за конфликти при запис: в MongoDB, един главен.

AT: – Имам въпрос от devops. В реалния свят има такива йезуитски ситуации, когато се случи византийският провал и злите хора вътре в защитения периметър започват да бъркат в протокола, да изпращат занаятчийски пакети по специален начин?

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

MT: – Злите хора вътре в периметъра са като троянски кон! Злите хора вътре в периметъра могат да направят много лоши неща.

AT: – Ясно е, че оставяйки, грубо казано, дупка в сървъра, през която можете да поставите зоопарк от слонове и да сринете целия клъстер завинаги... Ще отнеме време за ръчно възстановяване... Това, меко казано, е грешно. От друга страна, това е интересно: в реалния живот, на практика, има ситуации, когато естествено възникват подобни вътрешни атаки?

MT: – Тъй като рядко се сблъсквам с пробиви в сигурността в реалния живот, не мога да кажа дали се случват. Но ако говорим за философията на развитие, ние мислим така: имаме периметър, който осигурява момчетата, които се грижат за сигурността - това е замък, стена; и вътре в периметъра можете да правите каквото искате. Ясно е, че има потребители с възможност само за преглед и има потребители с възможност за изтриване на директорията.

В зависимост от правата, щетата, която потребителите могат да нанесат, може да бъде мишка или може да бъде слон. Ясно е, че потребител с пълни права може да направи всичко. Потребител с ограничени права може да причини значително по-малко вреда. По-специално, не може да счупи системата.

AT: – В защитения периметър някой се опита да създаде неочаквани протоколи за сървъра, за да унищожи напълно сървъра, а ако имате късмет, и целия клъстер... Става ли някога толкова „добре“?

MT: "Никога не съм чувал за такива неща." Фактът, че можете да сринете сървър по този начин, не е тайна. Фаил вътре, тъй като е от протокола, че е оторизиран потребител, който може да напише нещо подобно в съобщението... Всъщност е невъзможно, защото все пак ще бъде проверено. Възможно е да деактивирате това удостоверяване за потребители, които не го искат - тогава това е техен проблем; те, грубо казано, разрушиха самите стени и там можете да бутнете слон, който ще стъпче... Но като цяло можете да се облечете като ремонтник, елате и го издърпайте!

AT: – Благодаря за доклада. Сергей (Яндекс). В Mong има константа, която ограничава броя на членовете с право на глас в комплекта реплики и тази константа е 7 (седем). Защо това е константа? Защо това не е някакъв параметър?

MT: – Имаме комплекти реплики с 40 възела. Винаги има мнозинство. Не знам коя версия...

AT: – В Replica Set можете да управлявате членове без право на глас, но има максимум 7 члена с право на глас. Как можем да оцелеем при спирането в този случай, ако нашият Replica Set е разпръснат в 3 центъра за данни? Един център за данни може лесно да се изключи и друга машина може да падне.

MT: – Това вече е малко извън рамките на доклада. Това е общ въпрос. Може би ще мога да ви разкажа за това по-късно.

HighLoad++, Михаил Тюленев (MongoDB): Причинно-следствена последователност: от теория към практика

Малко реклами 🙂

Благодарим ви, че останахте с нас. Харесвате ли нашите статии? Искате ли да видите още интересно съдържание? Подкрепете ни, като направите поръчка или препоръчате на приятели, облачен VPS за разработчици от $4.99, уникален аналог на сървъри от начално ниво, който беше изобретен от нас за вас: Цялата истина за VPS (KVM) E5-2697 v3 (6 ядра) 10GB DDR4 480GB SSD 1Gbps от $19 или как да споделите сървър? (предлага се с RAID1 и RAID10, до 24 ядра и до 40GB DDR4).

Dell R730xd 2 пъти по-евтин в центъра за данни Equinix Tier IV в Амстердам? Само тук 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV от $199 в Холандия! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - от $99! Прочети за Как да изградим инфраструктура Corp. клас с използване на сървъри Dell R730xd E5-2650 v4 на стойност 9000 евро за стотинка?

Източник: www.habr.com

Добавяне на нов коментар