Ръководство за разбиране на моделите за мащабиране на базата данни

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

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

Казус

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

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

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

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

Модел 1 - Оптимизиране на заявките и внедряване на пул за свързване:

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

Вие установявате, че вашата база данни вероятно е силно нормализирана, така че въвеждате някои излишни колони (тези колони често се появяват в WHEREили JOIN ONклауза в заявки) в широко използвани таблици с цел денормализация. Това намалява заявките за присъединяване, разбива голяма заявка на множество по-малки заявки и добавя резултатите им в слоя на приложението.

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

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

Измервайте латентността на вашите API и открийте вероятно 20-50% или повече намалена латентност. Това е добра оптимизация в този момент.

Сега сте мащабирали бизнеса си до още един град, повече клиенти се регистрират, бавно започвате да правите 80–100 резервации в минута. Вашата система не може да се справи с тази скала. Отново виждате, че латентността на API се е увеличила, слоят на базата данни се е отказал, но този път никаква оптимизация на заявката не ви дава значителна печалба в производителността. Проверявате системната метрика, откривате, че дисковото пространство е почти пълно, процесорът е зает 80% от времето, RAM се пълни много бързо.

Модел 2 - Вертикално мащабиране или мащабиране:

След като проучихте всички системни показатели, знаете, че няма друго лесно решение, вместо да надстроите хардуера на системата. Надграждате размера на RAM паметта си 2 пъти, надграждате дисковото пространство, да речем, 3 пъти или повече. Това се нарича вертикално мащабиране или увеличаване на вашата система. Информирате вашия инфраструктурен екип или екип на devops или агенти на центрове за данни на трети страни, за да надстроите вашата машина.

Но как да настроите машина за вертикално мащабиране?

Отделяте по-голяма машина. Един от подходите е да не се мигрира данните ръчно от стара машина, а да се настрои новата машина replicaкъм съществуващата машина ( primary) - да се направи временна primary replicaконфигурация. Нека репликацията да се случи естествено. След като репликацията приключи, популяризирайте новата машина като основна и изведете по-старата машина офлайн. Тъй като се очаква по-голямата машина да обслужва всички заявки, всички четене / запис ще се случват на тази машина.

Готино. Вашата система отново работи и работи с повишена производителност.

Вашият бизнес се справя много добре и вие решавате да мащабирате до още 3 града - вече работите в общо 5 града. Трафикът е 3 пъти по-голям от преди, очаква се да направите около 300 резервации в минута. Преди дори да постигнете тази целева резервация, отново натискате производителността, размерът на индекса на базата данни се увеличава значително в паметта, има нужда от постоянна поддръжка, сканирането на таблици с индекс става по-бавно от всякога. Вие изчислявате допълнително разходите за увеличаване на машината, но не сте убедени в цената. Какво правиш сега?

Модел 3 - Разделяне на отговорността на командните заявки (CQRS):

Вие установявате, че голямата машина не е в състояние да обработва всички read/writeзаявки. Също така в повечето случаи всяка компания се нуждае от транзакционни възможности за, writeно не и за readоперации. Освен това се чувствате добре с малко непоследователни или забавени readоперации и вашият бизнес също няма проблем с това. Виждате възможност, при която може би е добър вариант да отделите разумно физическата машина read& writeоперации. Това ще създаде възможност за отделни машини да се справят с повече read/writeоперации.

Сега взимате още две големи машини и ги настройвате replicaспрямо текущата машина. Репликацията на база данни ще се погрижи за разпространението на данни от primaryмашина към replicaмашини. Придвижвате всички заявки за четене (Query ( Q) in CQRS) до репликите - всяка replicaможе да обслужва всяка заявка за четене, навигирате всички заявки за писане (Command ( C) in CQRS) до primary. Възможно е да има малко изоставане в репликацията, но според случая на вашата бизнес употреба това е добре.

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

Сега мащабирате до още 2 града, виждате, че вашият primaryне е в състояние да обработва всички writeзаявки. Много writeзаявки имат латентност. Нещо повече, забавянето между primaryи replicaпонякога влияе върху клиентите и шофьорите - когато пътуването приключи, клиентът плаща на водача успешно, но водачът не може да види плащането, тъй като дейността на клиента е writeзаявка, която се отправя към primary, докато дейността на водача е readзаявка който отива към една от репликите. Цялата ви система е толкова бавна, че водачът не може да види плащането поне половин минута - разочароващо както за шофьора, така и за клиента. Как го решавате?

Модел 4 - Мулти първична репликация

Намалихте много добре с primary-replicaконфигурацията, но сега се нуждаете от повече производителност при запис. Може да сте готови да компрометирате малко при readизпълнението на заявката. Защо не раздадете заявката за запис replicaсъщо на?

В multi-primaryконфигурация всички машини могат да работят и като primary& replica. Можете да мислите, multi-primaryкакто казва кръг от машини A->B->C->D->A. Bможе да репликира данни от A, Cможе да репликира данни от B, Dможе да репликира данни от C, Aможе да репликира данни от D. Можете да пишете данни на всеки възел, докато четете данни, можете да излъчвате заявката на всички възли, който отговори, върне това. Всички възли ще имат една и съща схема на база данни, един и същ набор от таблици, индекс и т.н. Така че трябва да сте сигурни, че няма сблъсък idмежду възлите в една и съща таблица, в противен случай по време на излъчването множество възли ще връщат различни данни за едни и същи id.

Като цяло е по-добре да се използва UUIDили GUIDза id. Още един недостатък на тази техника е - readзаявките могат да бъдат неефективни, тъй като включва излъчване на заявка и получаване на правилния резултат - в основата си подходът на разпръскване.

Сега мащабирате до още 5 града и системата ви отново страда. Очаква се да обработите около 50 заявки в секунда. Вие отчаяно се нуждаете да се справите с голям брой едновременни заявки. Как постигате това?

Модел 5 - Разделяне:

Знаете, че locationбазата данни е нещо, което става все по-високо writeи readтрафик. Вероятно write:readсъотношението е 7:3. Това оказва голям натиск върху съществуващите бази данни. В locationтаблиците съдържат няколко основни данни като longitude, latitude, timestamp, driver id, trip idи т.н. Това не разполага с много общо с потребителски пътувания, потребителски данни, данни за плащане и т.н. Какво за разделяне на locationтаблици в отделна схема на база данни? Какво ще кажете за поставянето на тази база данни в отделни машини с правилна primary-replicaили multi-primaryконфигурация?

Това се нарича разделяне на данни по функционалност. Различната база данни може да хоства данни, категоризирани по различна функционалност, ако е необходимо резултатът може да бъде обобщен в задния краен слой. Използвайки тази техника, можете да се съсредоточите върху мащабирането на онези функционалности, които изискват високи read/writeзаявки. Въпреки че задният край или слоят на приложението трябва да поеме отговорността да присъедини резултатите, когато е необходимо, което вероятно ще доведе до повече промени в кода.

Сега си представете, че сте разширили бизнеса си до общо 20 града във вашата страна и планирате скоро да се разширите в Австралия. Нарастващото ви търсене на приложение изисква по-бърза и по-бърза реакция. Нито един от горните методи не може да ви помогне до крайност сега. Трябва да мащабирате системата си по такъв начин, че разширяването до други държави / региони не винаги се нуждае от чести инженерни или архитектурни промени. Как правиш това?

Модел 6 - Хоризонтално мащабиране:

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

Тъй като всички бази данни съдържат един и същ набор от таблици, можете да проектирате системата по такъв начин, че да има местоположение на данни, т.е. всички свързани данни попадат в една и съща машина. Всяка машина може да има свои собствени реплики, репликите могат да се използват при възстановяване на неизправности. Извиква се всяка от базите данни shard. Физическата машина може да има една или няколко shards- зависи от вашия дизайн как искате. Трябва да вземете решение по sharding keyтакъв начин, че единичен sharding keyвинаги да се отнася за една и съща машина. Така че можете да си представите много машини, всички съдържащи свързани данни в един и същ набор от таблици, read/writeзаявки за един и същи ред или същия набор от земя на ресурси в една и съща машина за бази данни.

Изострянето като цяло е трудно - поне инженери от различни компании казват това. Но когато обслужвате милиони или милиарди искания, трябва да вземете толкова трудно решение.

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

Сега, след като вече разполагате с шардинг, сте уверени, че можете да мащабирате до много страни. Вашият бизнес е нараснал толкова много, че инвеститорите ви тласкат да разширите бизнеса си на континентите. Тук отново виждате някакъв проблем. Отново латентност на API. Вашата услуга се хоства в САЩ, а хората от Виетнам изпитват трудни пътувания с книги. Защо? Какво правите по въпроса?

Модел 7 - Мъдър дял в центъра за данни:

Вашият бизнес се разраства в Америка, Южна Азия и в няколко страни в Европа. Правите милиони резервации всеки ден с милиарди заявки, които удрят вашия сървър. Поздравления - това е пиков момент за вашия бизнес.

Но тъй като заявките от приложението трябва да пътуват през континенти през стотици или хиляди сървъри в интернет, възниква латентност. Ами разпределението на трафика между центровете за данни? Можете да настроите център за данни в Сингапур, който обработва всички заявки от Южна Азия, центърът за данни в Германия може да обработва всички искания от европейски страни, а център за данни от Калифорния може да обработва всички искания от САЩ.

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

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

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

В следващите си статии ще се опитам да обсъдя подробно някои от концепциите. Моля, не се колебайте да дадете подходяща обратна връзка за тази публикация, ако има такава.

Статията първоначално е публикувана в средния акаунт на автора: //medium.com/@kousiknath/understanding-database-scaling-patterns-ac24e5223522