Повече разработчици трябва да знаят това за базите данни

Забележка. превод: Яана Доган е опитен инженер в Google, който в момента работи върху видимостта на производствените услуги на компанията, написани на Go. В тази статия, която придоби голяма популярност сред англоговорящата аудитория, тя събра в 17 точки важни технически подробности относно СУБД (и понякога разпределени системи като цяло), които е полезно да се вземат предвид от разработчиците на големи/взискателни приложения.

Повече разработчици трябва да знаят това за базите данни

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

  1. Късметлии сте, ако 99,999% от времето мрежата не създава проблеми.
  2. ACID означава много различни неща.
  3. Всяка база данни има свои собствени механизми за осигуряване на последователност и изолация.
  4. Оптимистичното блокиране идва на помощ, когато е трудно да се поддържа обичайното.
  5. Има и други аномалии освен мръсни четения и загуба на данни.
  6. Базата данни и потребителят не винаги са съгласни относно начина на действие.
  7. Шардингът на ниво приложение може да бъде преместен извън приложението.
  8. Автоматичното увеличаване може да бъде опасно.
  9. Остарелите данни могат да бъдат полезни и не е необходимо да се заключват.
  10. Изкривяванията са типични за всякакви източници на време.
  11. Забавянето има много значения.
  12. Изискванията за ефективност трябва да бъдат оценени за конкретна транзакция.
  13. Вложените транзакции могат да бъдат опасни.
  14. Транзакциите не трябва да се обвързват със състоянието на приложението.
  15. Планиращите заявки могат да ви кажат много за базите данни.
  16. Онлайн миграцията е трудна, но възможна.
  17. Значителното увеличение на базата данни води до увеличаване на непредсказуемостта.

Бих искал да благодаря на Emmanuel Odeke, Rein Henrichs и други за техните отзиви за по-ранна версия на тази статия.

Късметлии сте, ако 99,999% от времето мрежата не създава проблеми.

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

Със степен на наличност от 99,999% за Spanner (глобално разпространената база данни на Google), Google твърди, че само 7,6% проблемите са свързани с мрежата. В същото време компанията нарича своята специализирана мрежа „основен стълб“ на високата достъпност. Проучване Бейлис и Кингсбъри, проведено през 2014 г., предизвиква една от „погрешни схващания за разпределените изчисления“, който Питър Дойч формулира през 1994 г. Мрежата наистина ли е надеждна?

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

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

ACID означава много различни неща

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

Когато за първи път влязох в индустрията, нашият технически ръководител говореше колко подходяща е концепцията ACID. За да бъдем честни, ACID се счита за грубо описание, а не за строг стандарт за изпълнение. Днес го намирам за най-вече полезен, защото повдига специфична категория проблеми (и предлага набор от възможни решения).

Не всяка СУБД е съвместима с ACID; В същото време реализациите на бази данни, които поддържат ACID, разбират набора от изисквания по различен начин. Една от причините, поради които внедряванията на ACID са неравномерни, се дължи на многото компромиси, които трябва да се направят, за да се внедрят изискванията на ACID. Създателите могат да представят своите бази данни като ACID-съвместими, но тълкуването на крайните случаи може да варира драстично, както и механизмът за обработка на „малко вероятни“ събития. Най-малкото, разработчиците могат да придобият разбиране на високо ниво за тънкостите на базовите реализации, за да получат правилно разбиране на тяхното специално поведение и компромиси в дизайна.

Дебатът дали MongoDB отговаря на изискванията на ACID продължава дори след пускането на версия 4. MongoDB не се поддържа от дълго време дърводобив, въпреки че по подразбиране данните се предават на диска не повече от веднъж на всеки 60 секунди. Представете си следния сценарий: приложение публикува две записи (w1 и w2). MongoDB съхранява успешно w1, но w2 се губи поради хардуерен срив.

Повече разработчици трябва да знаят това за базите данни
Диаграма, илюстрираща сценария. MongoDB се срива, преди да може да запише данни на диск

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

Всяка база данни има свои собствени механизми за съгласуваност и изолация

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

СУБД често предоставят различни нива на изолация. Разработчиците на приложения могат да изберат най-ефективното въз основа на своите предпочитания. Ниската изолация позволява повишена скорост, но също така увеличава риска от надпревара с данни. Високата изолация намалява тази вероятност, но забавя работата и може да доведе до конкуренция, което ще доведе до такива спирачки в основата, че започват повреди.

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

Стандартът SQL дефинира само четири нива на изолация, въпреки че на теория и практика има много повече. Jepson.io предлага отличен преглед на съществуващите модели на едновременност. Например Google Spanner гарантира възможност за външна сериализация със синхронизация на часовника и въпреки че това е по-строг изолационен слой, той не е дефиниран в стандартните изолационни слоеве.

Стандартът SQL споменава следните нива на изолация:

  • Serializable (най-строгите и скъпи): Сериализиращото се изпълнение има същия ефект като изпълнението на някои последователни транзакции. Последователното изпълнение означава, че всяка следваща транзакция започва едва след приключване на предходната. Трябва да се отбележи, че нивото Serializable често се прилага като така наречената изолация на моментна снимка (например в Oracle) поради разлики в интерпретацията, въпреки че самата изолация на моментна снимка не е представена в стандарта SQL.
  • Повтарящи се четения: Неангажираните записи в текущата транзакция са достъпни за текущата транзакция, но промените, направени от други транзакции (като нови редове) невидим.
  • Прочетете ангажирани: Неангажираните данни не са налични за транзакции. В този случай транзакциите могат да виждат само ангажирани данни и може да възникнат фантомни четения. Ако дадена транзакция вмъква и ангажира нови редове, текущата транзакция ще може да ги види при запитване.
  • Прочетете без ангажименти (най-малко стриктно и скъпо ниво): Разрешени са мръсни четения, транзакциите могат да видят неизвършени промени, направени от други транзакции. На практика това ниво може да е полезно за груби оценки, като например заявки COUNT(*) на масата.

Ниво Serializable минимизира риска от състезания за данни, като същевременно е най-скъпият за внедряване и води до най-високото конкурентно натоварване на системата. Други нива на изолация са по-лесни за прилагане, но увеличават вероятността от състезания за данни. Някои СУБД ви позволяват да зададете персонализирано ниво на изолация, други имат силни предпочитания и не всички нива се поддържат.

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

Повече разработчици трябва да знаят това за базите данни
Преглед на аномалии в паралелността на различни нива на изолация за различни СУБД

Мартин Клепман в своя проект ермитаж Сравнява различни нива на изолация, говори за аномалии в паралелността и дали базата данни е в състояние да се придържа към определено ниво на изолация. Изследването на Kleppmann показва колко различни са разработчиците на бази данни, които мислят за нивата на изолация.

Оптимистичното блокиране идва на помощ, когато е трудно да се поддържа обичайното.

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

Оптимистично заключване е метод, при който при четене на низ се взема предвид неговата версия, контролна сума или час на последна модификация. Това ви позволява да се уверите, че няма атомарна промяна на версията, преди да промените запис:

UPDATE products
SET name = 'Telegraph receiver', version = 2
WHERE id = 1 AND version = 1

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

Има и други аномалии освен мръсни четения и загуба на данни

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

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

Например, нека разгледаме приложение за мониторинг, което изисква един оператор да бъде на повикване през цялото време:

BEGIN tx1;                      BEGIN tx2;
SELECT COUNT(*)
FROM operators
WHERE oncall = true;
0                               SELECT COUNT(*)
                                FROM operators
                                WHERE oncall = TRUE;
                                0
UPDATE operators                UPDATE operators
SET oncall = TRUE               SET oncall = TRUE
WHERE userId = 4;               WHERE userId = 2;
COMMIT tx1;                     COMMIT tx2;

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

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

Базата данни и потребителят не винаги са съгласни какво да правят

Една от ключовите характеристики на базите данни е гаранцията за реда на изпълнение, но самият този ред може да не е прозрачен за разработчика на софтуера. Базите данни изпълняват транзакции в реда, в който са получени, а не в реда, предвиден от програмистите. Редът на транзакциите е труден за прогнозиране, особено в силно натоварени паралелни системи.

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

На пръв поглед в програмата по-долу T1 и T2 се извикват последователно, но ако тези функции са неблокиращи и веднага връщат резултата във формата обещание, тогава редът на обажданията ще се определя от моментите, в които са влезли в базата данни:

result1 = T1() // реалните резултати са обещания
резултат2 = T2()

Ако се изисква атомарност (т.е. всички операции трябва да бъдат завършени или прекратени) и последователността е от значение, тогава операциите T1 и T2 трябва да бъдат изпълнени в рамките на една транзакция.

Шардингът на ниво приложение може да бъде преместен извън приложението

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

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

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

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

Автоматичното увеличаване може да бъде опасно

AUTOINCREMENT е често срещан начин за генериране на първични ключове. Често има случаи, когато базите данни се използват като генератори на идентификатори и базата данни съдържа таблици, предназначени за генериране на идентификатори. Има няколко причини, поради които генерирането на първични ключове с помощта на автоматично нарастване далеч не е идеално:

  • В разпределена база данни автоматичното увеличаване е сериозен проблем. За генериране на ID е необходимо глобално заключване. Вместо това можете да генерирате UUID: това не изисква взаимодействие между различни възли на база данни. Автоматичното увеличаване с ключалки може да доведе до конкуренция и значително да намали производителността на вмъкванията в разпределени ситуации. Някои СУБД (например MySQL) може да изискват специална конфигурация и по-внимателно внимание, за да се организира правилно репликацията master-master. Освен това е лесно да направите грешки при конфигуриране, което ще доведе до неуспешен запис.
  • Някои бази данни имат алгоритми за разделяне, базирани на първични ключове. Последователните идентификатори могат да доведат до непредвидими горещи точки и повишено натоварване на някои дялове, докато други остават неактивни.
  • Първичният ключ е най-бързият начин за достъп до ред в база данни. С по-добри начини за идентифициране на записи, последователните идентификатори могат да превърнат най-важната колона в таблиците в безполезна колона, пълна с безсмислени стойности. Затова, когато е възможно, моля, изберете глобално уникален и естествен първичен ключ (напр. потребителско име).

Преди да вземете решение за подход, помислете за въздействието на автоматично увеличаващите се идентификатори и UUID върху индексирането, разделянето и шардинга.

Остарелите данни могат да бъдат полезни и не изискват заключване

Multiversion Concurrency Control (MVCC) изпълнява много от изискванията за съгласуваност, които бяха обсъдени накратко по-горе. Някои бази данни (например Postgres, Spanner) използват MVCC за „захранване“ на транзакции с моментни снимки – по-стари версии на базата данни. Транзакциите за моментни снимки могат също да бъдат сериализирани, за да се осигури последователност. При четене от стара моментна снимка се четат остарели данни.

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

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

Повече разработчици трябва да знаят това за базите данни
Сървърът на приложения чете данни от локалната реплика, която е остаряла с 5 секунди, дори ако последната версия е налична от другата страна на Тихия океан

СУБД автоматично изчистват по-старите версии и в някои случаи ви позволяват да направите това при поискване. Например Postgres позволява на потребителите да правят VACUUM при поискване, а също така периодично извършва тази операция автоматично. Spanner стартира събирач на отпадъци, за да се отърве от моментни снимки, по-стари от един час.

Източниците по всяко време са обект на изкривяване

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

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

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

Ситуацията се утежнява от факта, че приложенията и базите данни често се намират на различни машини (ако не и в различни центрове за данни). По този начин времето ще се различава не само в DB възлите, разпределени между различни машини. Освен това ще бъде различно на сървъра за приложения.

Google TrueTime използва напълно различен подход. Повечето хора вярват, че напредъкът на Google в тази посока се обяснява с баналния преход към атомни и GPS часовници, но това е само част от голямата картина. Ето как работи TrueTime:

  • TrueTime използва два различни източника: GPS и атомни часовници. Тези часовници имат некорелирани режими на отказ. [вижте страница 5 за подробности тук - прибл. превод), така че съвместното им използване повишава надеждността.
  • TrueTime има необичаен API. Той връща времето като интервал с вградена грешка в измерването и несигурност. Действителният момент във времето е някъде между горната и долната граница на интервала. Spanner, разпределената база данни на Google, просто изчаква, докато е безопасно да се каже, че текущият час е извън диапазона. Този метод въвежда известно забавяне в системата, особено ако несигурността на главните е висока, но гарантира коректност дори в глобално разпределена ситуация.

Повече разработчици трябва да знаят това за базите данни
Компонентите на Spanner използват TrueTime, където TT.now() връща интервал, така че Spanner просто спи до точката, в която може да бъде сигурен, че текущото време е преминало определена точка

Намалената точност при определяне на текущото време означава увеличаване на продължителността на операциите на Spanner и намаляване на производителността. Ето защо е важно да се поддържа възможно най-висока точност, въпреки че е невъзможно да се получи напълно точен часовник.

Забавянето има много значения

Ако попитате дузина експерти какво е забавяне, вероятно ще получите различни отговори. В СУБД забавянето често се нарича "закъснение на базата данни" и е различно от това, което се възприема от клиента. Факт е, че клиентът наблюдава сумата от мрежовото забавяне и забавянето на базата данни. Способността да се изолира типът латентност е от решаващо значение при отстраняване на грешки при нарастващи проблеми. Когато събирате и показвате показатели, винаги се опитвайте да държите под око и двата типа.

Изискванията за ефективност трябва да бъдат оценени за конкретна транзакция

Понякога характеристиките на производителността на СУБД и нейните ограничения се определят от гледна точка на пропускателна способност за запис/четене и латентност. Това осигурява общ преглед на ключови системни параметри, но когато се оценява производителността на нова СУБД, много по-всеобхватен подход е да се оценят отделно критичните операции (за всяка заявка и/или транзакция). Примери:

  • Пропускателна способност и закъснение при запис при вмъкване на нов ред в таблица X (с 50 милиона реда) с определени ограничения и подпълване на редове в свързани таблици.
  • Забавяне в показването на приятели на приятели на определен потребител, когато средният брой приятели е 500.
  • Закъснение при извличане на първите 100 записа от хронологията на даден потребител, когато потребителят следва 500 други потребители с X записа на час.

Оценката и експериментирането могат да включват такива критични случаи, докато не сте сигурни, че базата данни отговаря на изискванията за производителност. Подобно основно правило също взема предвид тази разбивка при събиране на показатели за латентност и определяне на SLO.

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

Вложените транзакции могат да бъдат опасни

Не всяка СУБД поддържа вложени транзакции, но когато го направят, такива транзакции могат да доведат до неочаквани грешки, които не винаги са лесни за откриване (тоест трябва да е очевидно, че има някакъв вид аномалия).

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

Капсулирането на транзакции в различни слоеве може да доведе до неочаквани вложени транзакции и от гледна точка на четливостта на кода може да затрудни разбирането на намеренията на автора. Разгледайте следната програма:

with newTransaction():
   Accounts.create("609-543-222")
   with newTransaction():
       Accounts.create("775-988-322")
       throw Rollback();

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

Представете си слой данни с множество операции (напр. newAccount) вече е внедрен в собствените си транзакции. Какво се случва, ако ги стартирате като част от бизнес логиката на по-високо ниво, която работи в рамките на собствената си транзакция? Каква би била изолацията и последователността в този случай?

function newAccount(id string) {
  with newTransaction():
      Accounts.create(id)
}

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

function newAccount(id string) {
   Accounts.create(id)
}
// In main application:
with newTransaction():
   // Read some data from database for configuration.
   // Generate an ID from the ID service.
   Accounts.create(id)
   Uploads.create(id) // create upload queue for the user.

Транзакциите не трябва да се обвързват със състоянието на приложението

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

var seq int64
with newTransaction():
    newSeq := atomic.Increment(&seq)
    Entries.query(newSeq)
    // Other operations...

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

Планиращите заявки могат да ви кажат много за една база данни

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

SELECT * FROM articles where author = "rakyll" order by title;

Резултатите могат да бъдат извлечени по два начина:

  • Пълно сканиране на таблицата: Можете да разгледате всеки запис в таблицата и да върнете статии със съвпадащо име на автор и след това да ги подредите.
  • Индекс сканиране: Можете да използвате индекс, за да намерите съвпадащи идентификатори, да получите тези редове и след това да ги подредите.

Работата на планиращия заявки е да определи коя стратегия е най-добра. Струва си да се има предвид, че програмите за планиране на заявки имат само ограничени възможности за прогнозиране. Това може да доведе до лоши решения. Администраторите на бази данни или разработчиците могат да ги използват за диагностициране и фина настройка на неефективни заявки. Новите версии на СУБД могат да конфигурират програми за планиране на заявки и самодиагностиката може да помогне при актуализиране на базата данни, ако новата версия доведе до проблеми с производителността. Регистрационни файлове с бавни заявки, отчети за проблеми със закъснението или статистически данни за времето на изпълнение могат да помогнат при идентифицирането на заявки, които се нуждаят от оптимизация.

Някои показатели, представени от инструмента за планиране на заявки, може да са обект на шум (особено при оценка на латентност или време на процесора). Добро допълнение към планировчиците са инструментите за проследяване и проследяване на пътя на изпълнение. Те ви позволяват да диагностицирате такива проблеми (уви, не всички СУБД предоставят такива инструменти).

Онлайн миграцията е трудна, но възможна

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

Има различни модели за онлайн миграция. Ето един от тях:

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

За допълнителна информация препоръчвам да се свържете Статия, който описва подробно миграционната стратегия на Stripe, базирана на този модел.

Значителното увеличение на базата данни води до увеличаване на непредсказуемостта

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

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

...

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

PS

Прочетете също в нашия блог:

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

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