Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных

Заўв. перав.: Jaana Dogan - вопытны інжынер з Google, якая ў дадзены момант займаецца пытаннямі назіральнасці production-сэрвісаў кампаніі, напісаных на Go. У гэтым артыкуле, які набыў вялікую папулярнасць у англамоўнай аўдыторыі, яна ў 17 пунктах сабрала важныя тэхнічныя дэталі, якія тычацца СКБД (а часам - размеркаваных сістэм у цэлым), якія карысна ўлічваць распрацоўшчыкам буйных / патрабавальных прыкладанняў.

Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных

Пераважная большасць кампутарных сістэм адсочваюць свой стан і, адпаведна, маюць патрэбу ў нейкай сістэме захоўвання дадзеных. Я назапашвала веды аб базах дадзеных на працягу доўгага часу, адначасна здзяйсняючы памылкі пры праектаванні, якія прыводзілі да страты дадзеных і перабоям у працы. У сістэмах, якія апрацоўваюць вялікія аб'ёмы інфармацыі, базы дадзеных ляжаць у сэрцы сістэмнай архітэктуры і выступаюць ключавым элементам пры выбары аптымальнага рашэння. Нягледзячы на ​​тое, што працы БД надаецца пільная ўвага, праблемы, якія спрабуюць прадугледзець распрацоўшчыкі прыкладанняў, часта аказваюцца толькі верхавінай айсберга. У гэтай серыі артыкулаў я дзялюся некаторымі ідэямі, якія будуць карысныя для распрацоўшчыкаў, якія не спецыялізуюцца ў гэтай галіне.

  1. Вам пашанцавала, калі 99,999% часу сетка не з'яўляецца крыніцай праблем.
  2. ACID мае на ўвазе мноства розных рэчаў.
  3. У кожнай базы свае механізмы забеспячэння ўзгодненасці і ізаляцыі.
  4. Аптымістычнае блакаванне прыходзіць на выручку, калі складана ўтрымліваць звычайную.
  5. Існуюць і іншыя анамаліі акрамя "брудных" чытанняў і страты дадзеных.
  6. База дадзеных і карыстач не заўсёды прыходзяць да адзінага меркавання з нагоды парадку дзеянняў.
  7. Шардынг на ўзроўні прыкладання можна вынесці за межы прыкладання.
  8. Аўтаінкрыментаванне можа быць небяспечным.
  9. Састарэлыя (stale) дадзеныя могуць быць карысныя і не патрабуюцца ў блакаванні.
  10. Скажэнні тыповыя для любых крыніц часу.
  11. У затрымкі мноства значэнняў.
  12. Патрабаванні да прадукцыйнасці трэба ацэньваць для канкрэтнай транзакцыі.
  13. Укладзеныя транзакцыі могуць быць небяспечныя.
  14. Транзакцыі не павінны быць прывязаны да стану прыкладання.
  15. Планавальнікі запытаў могуць шмат распавесці аб базах дадзеных.
  16. Анлайн-міграцыя складаная, але магчымая.
  17. Значнае павелічэнне базы даных цягне за сабой рост непрадказальнасці.

Жадаю выказаць удзячнасць Emmanuel Odeke, Rein Henrichs і іншым за іх зваротную сувязь па ранняй версіі гэтага артыкула.

Вам пашанцавала, калі 99,999% часу сетка не з'яўляецца крыніцай праблем

Застаецца адкрытым пытанне аб тым, наколькі надзейныя сучасныя сеткавыя тэхналогіі і як часта сістэмы прастойваюць з-за збояў у працы сеткі. Інфармацыі па гэтым пытанні недастаткова, і часта ў даследаваннях пераважаюць буйныя арганізацыі са спецыялізаванымі сеткамі, абсталяваннем і персаналам.

Маючы паказчык даступнасці ў 99,999% для Spanner (глабальна размеркаванай базы дадзеных Google), Google сцвярджае, што толькі 7,6% праблем звязаныя з сеткай. Пры гэтым кампанія называе сваю спецыялізаваную сетку "галоўнай апорай" высокай даступнасці. Даследаванне Bailis і Kingsbury, праведзенае ў 2014 годзе, кідае выклік аднаму зпамылак аб размеркаваных вылічэннях», якія Peter Deutsch сфармуляваў у 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 мс. Гэта значыць падобны сцэнар усё яшчэ магчымы для часопісаў і змен, прадстаўленых у іх, хоць рызыка значна ніжэй.

У кожнай базы свае механізмы забеспячэння ўзгодненасці і ізаляцыі.

З ліку патрабаванняў ACID узгодненасць і ізаляцыя могуць пахваліцца самай вялікай колькасцю розных рэалізацый, паколькі спектр кампрамісаў шырэй. Трэба сказаць, што ўзгодненасць і ізаляцыя - даволі затратныя функцыі. Яны патрабуюць каардынацыі і ўзмацняюць канкурэнцыю дзеля ўзгодненасці звестак. Складанасць праблемы значна ўзрастае пры неабходнасці гарызантальна маштабаваць базу па некалькіх ЦАД (асабліва калі яны знаходзяцца ў розных геаграфічных рэгіёнах). Дамагчыся высокага ўзроўню ўзгодненасці вельмі складана, паколькі адначасна зніжаецца даступнасць і павялічваецца сегментацыя сеткі. Для больш агульнага тлумачэння гэтага феномену раю звярнуцца да тэарэме САР. Варта таксама адзначыць, што прыкладанні здольныя спраўляцца з невялікай няўзгодненасцю, а праграмісты могуць дастаткова добра ўяўляць нюансы праблемы, каб рэалізаваць дадатковую логіку ў дадатку, якая дазваляе спраўляцца з няўзгодненасцю, не моцна належачы на ​​БД у гэтым пытанні.

СКБД часта падаюць розныя ўзроўні ізаляцыі. Распрацоўнікі прыкладанняў могуць абраць найболей эфектыўны з іх, засноўваючыся на сваіх перавагах. Нізкая ізаляцыя дазваляе павялічыць хуткасць, адначасна падвышаючы рызыку ўзнікнення гонкі дадзеных (data race). Высокая ізаляцыя зніжае гэтую верагоднасць, але запавольвае працу і можа прывесці да канкурэнцыі, якая прывядзе да такіх тормазаў у базе, што пачнуцца збоі.

Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных
Агляд існуючых мадэляў паралелізму і ўзаемасувязяў паміж імі

Стандарт SQL вызначае толькі чатыры ўзроўня ізаляцыі, хоць у тэорыі і на практыку іх лік значна больш. Jepson.io прапануе выдатны агляд існуючых мадэляў паралелізму. Напрыклад, Google Spanner гарантуе вонкавую серыялізаванасць з сінхранізацыяй гадзін, і хоць гэта стражэйшы пласт ізаляцыі, ён не вызначаны ў стандартных пластах ізаляцыі.

У стандарце SQL згадваюцца наступныя ўзроўні ізаляцыі:

  • Сериализуемы (самы цвёрды і затратны): серыялізаванае выкананне мае эфект, аналагічны некатораму паслядоўнаму выкананню транзакцый. Паслядоўнае выкананне азначае, што кожная наступная транзакцыя пачынаецца толькі пасля поўнага завяршэння папярэдняй. Варта адзначыць, што ўзровень Сериализуемы часта рэалізуецца ў выглядзе так званай snapshot-ізаляцыі (напрыклад, у Oracle) з-за адрозненняў у інтэрпрэтацыі, хоць сама snapshot-ізаляцыя не прадстаўлена ў стандарце SQL.
  • Repeatable reads: незафіксаваныя запісы ў бягучай транзакцыі даступныя для бягучай транзакцыі, але змены, зробленыя іншымі транзакцыямі (напрыклад, новыя радкі), не бачныя.
  • Read committed: незафіксаваныя дадзеныя недаступныя для транзакцый. У гэтым выпадку транзакцыі могуць бачыць толькі зафіксаваныя дадзеныя, пры гэтым могуць адбывацца фантомныя чытанні. Калі нейкая транзакцыя ўставіць і зафіксуе новыя радкі, бягучая транзакцыя зможа ўбачыць іх пры запыце.
  • Read uncommitted (найменш строгі і затратны ўзровень): "бруднае" чытанне дапушчальна, транзакцыі могуць бачыць незафіксаваныя змены, унесеныя іншымі транзакцыямі. На практыцы гэты ўзровень можа спатрэбіцца для прыблізных адзнак, напрыклад, для запытаў. COUNT(*) на табліцы.

Узровень Сериализуемы мінімізуе рызыку ўзнікнення гонак дадзеных, пры гэтым найбольш затратны ў рэалізацыі і прыводзіць да самай высокай канкурэнтнай нагрузкі на сістэму. Іншыя ўзроўні ізаляцыі рэалізаваць лягчэй, аднак узрастае верагоднасць гонак дадзеных. Некаторыя СКБД дазваляюць задаць карыстацкі ўзровень ізаляванасці, у іншых маюцца выяўленыя перавагі і падтрымліваюцца не ўсе ўзроўні.

Падтрымка ўзроўняў ізаляванасці часта рэкламуецца ў той ці іншай СКБД, аднак толькі стараннае вывучэнне яе паводзін дазваляе растлумачыць, што насамрэч адбываецца.

Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных
Агляд анамалій паралелізму на розных узроўнях ізаляцыі для розных СКБД

Martin Kleppmann у сваім праекце скіт праводзіць параўнанне розных узроўняў ізаляцыі, расказвае пра анамаліі паралелізму і пра тое, ці здольная база даных прытрымлівацца пэўнага ўзроўню ізаляцыі. Даследаванні Kleppmann'а паказваюць, наколькі па-рознаму распрацоўнікі баз дадзеных уяўляюць сабе ўзроўні ізаляцыі.

Аптымістычнае блакаванне прыходзіць на выручку, калі складана ўтрымліваць звычайную

Блакаванне можа быць вельмі накладной не толькі таму, што ўзмацняе канкурэнцыю ў базе, але і таму, што патрабуе сталага падлучэння ад сервераў прыкладання да базы дадзеных. Сегментацыя сеткі здольная пагоршыць сітуацыю з манапольнымі блакіроўкамі і прывесці да ўзнікнення тупіковых блакіровак, якія цяжка ідэнтыфікаваць і ўстараняць. У выпадках, калі манапольнае блакаванне не падыходзіць, дапамагае аптымістычнае.

Аптымістычнае блакаванне - Гэта метад, пры якім пры счытванні радка ўлічваецца яе версія, кантрольная сума або час апошняй змены. Дзякуючы гэтаму перад тым, як змяняць запіс, можна пераканацца ў адсутнасці атамарнай змены версіі:

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

У дадзеным выпадку абнаўленне табліцы products не будзе праведзена, калі раней іншая аперацыя ўнесла змены ў гэты радок. Калі ж іншых аперацый з дадзеным радком не праводзілася, змена для аднаго радка адбудзецца і мы зможам сказаць, што абнаўленне было паспяховым.

Існуюць і іншыя анамаліі акрамя "брудных" чытанняў і страты дадзеных

Калі гаворка заходзіць аб узгодненасці дадзеных, асноўная ўвага надаецца магчымаму ўзнікненню ўмоў гонкі, якія могуць прывесці да «брудных» чытанняў і страты дадзеных. Аднак анамаліі з дадзенымі гэтым не абмяжоўваюцца.

Адным з прыкладаў такіх анамалій з'яўляюцца скажэнні запісу (write skews). Скажэнні складана выявіць, паколькі звычайна іх актыўна не шукаюць. Яны звязаны не з "бруднымі" чытаннямі або стратай дадзеных, а з парушэннямі лагічных абмежаванняў, якія накладаюцца на дадзеныя.

Напрыклад, давайце разгледзім прыкладанні для маніторынгу, якое патрабуе, каб адзін аператар увесь час быў on-call:

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;

У сітуацыі вышэй узнікне скажэнне запісу, калі абедзве транзакцыі будуць паспяхова зафіксаваныя. Хоць "брудных" чытанняў або страты дадзеных не адбылося, цэласнасць дадзеных аказалася парушана: цяпер два чалавекі адначасова лічацца on-call.

Серыялізаваная ізаляцыя, будынак схемы ці абмежаванні БД могуць спрыяць ва ўхіленні скажэнняў запісу. Распрацоўнікі павінны ўмець выяўляць такія анамаліі падчас распрацоўкі, каб пазбегнуць іх у production. Пры гэтым скажэнні запісу вельмі цяжка шукаць у кодавай базе. Асабліва ў буйных сістэмах, калі розныя каманды распрацоўшчыкаў адказваюць за рэалізацыю функцый на аснове адных і тых жа табліц і не ўзгадняюць паміж сабой асаблівасці доступу да дадзеных.

База дадзеных і карыстач не заўсёды прыходзяць да адзінага меркавання з нагоды парадку дзеянняў

Адна з ключавых асаблівасцяў баз дадзеных - гэта гарантаванне парадку выканання, аднак сам гэты парадак можа быць непразрысты для распрацоўніка ПЗ. Базы дадзеных выконваюць транзакцыі ў парадку атрыманьня, а не ў тым парадку, які маюць на ўвазе праграмісты. Парадак выканання транзакцый складана прадказаць, асабліва ў высоканагружаных паралельных сістэмах.

Падчас распрацоўкі, асабліва пры працы з неблакавальнымі бібліятэкамі, дрэнны стыль і нізкая чытальнасць могуць прывесці да таго, што карыстачы будуць упэўненыя, што транзакцыі выконваюцца паслядоўна, хоць насамрэч яны могуць паступаць у базу ў любым парадку.

На першы погляд, у прыведзенай ніжэй праграме Т1 і Т2 выклікаюцца паслядоўна, але калі гэтыя функцыі неблакіруючыя і адразу вяртаюць вынік у выглядзе абяцанне, то парадак выкліку будзе вызначацца момантамі, калі яны паступілі ў базу даных:

result1 = T1() // рэальныя вынікі - гэта promises
result2 = T2()

Калі неабходная атамарнасць (гэта значыць або ўсе аперацыі павінны быць выкананы, або перапынены) і паслядоўнасць мае значэнне, то аперацыі Т1 і Т2 павінны выконвацца ў рамках адзінай транзакцыі.

Шардынг на ўзроўні прыкладання можна вынесці за межы прыкладання

Шардынг - гэта спосаб гарызантальнага падзелу базы дадзеных. Некаторыя базы ўмеюць аўтаматычна разбіваць дадзеныя гарызантальна, у той час як іншыя гэтага не ўмеюць, ці ім гэта ўдаецца не занадта добра. Калі архітэктары дадзеных/распрацоўнікі маюць магчымасць спрагназаваць, як менавіта будзе ажыццяўляцца доступ да дадзеных, яны могуць стварыць гарызантальныя партіціі ў карыстацкай прасторы замест таго, каб дэлегаваць гэтую працу БД. Гэты працэс называецца "шардынг на ўзроўні прыкладання" (application-level sharding).

Нажаль, такая назва часта стварае няслушнае ўяўленне, што шардынг насяляе ў сэрвісах прыкладання. Насамрэч яго можна рэалізаваць у выглядзе асобнага пласта перад базай дадзеных. У залежнасці ад росту дадзеных і ітэрацый схемы, патрабаванні да шардынгу могуць станавіцца вельмі складанымі. Пры некаторых стратэгіях можа спатрэбіцца магчымасць праводзіць ітэрацыі без неабходнасці паўторна разгортваць серверы прыкладання.

Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных
Прыклад архітэктуры, у якой серверы прыкладання аддзеленыя ад сэрвісу шардынгу

Вынас шардынгу ў асобны сэрвіс пашырае магчымасці па выкарыстанні розных стратэгій шардынгу без неабходнасці зноўку дэплоіць прыкладанні. Vitess - прыклад такой сістэмы шардынгу на ўзроўні прыкладання. Vitess забяспечвае гарызантальны шардынг для MySQL і дазваляе кліентам падлучацца ў ім праз пратакол MySQL. Сістэма сегментуе дадзеныя па розных вузлах MySQL, нічога не дасведчаных сябар пра сябра.

Аўтаінкрыментаванне можа быць небяспечным

AUTOINCREMENT - распаўсюджаны спосаб генерацыі першасных ключоў. Нярэдка сустракаюцца выпадкі, калі БД выкарыстоўваюцца ў якасці генератараў ID, а ў базе маюцца табліцы, прызначаныя для генерацыі ідэнтыфікатараў. Існуе некалькі чыннікаў, па якіх генерацыя першасных ключоў з дапамогай auto-incrementing'а далёкая ад ідэалу:

  • У размеркаванай базе даных auto-incrementing уяўляе сабой сур'ёзную праблему. Для генерацыі ID неабходна глабальная блакіроўка. Замест гэтага можна згенераваць UUID: для гэтага не запатрабуецца ўзаемадзеянне розных вузлоў базы. Auto-incrementing з блакіроўкамі можа прывесці да канкурэнцыі і значна знізіць прадукцыйнасць пры ўстаўках у размеркаваных сітуацыях. Некаторым СКБД (напрыклад, MySQL) можа запатрабавацца адмысловая налада і больш пільная ўвага, каб правільнай выявай арганізаваць рэплікацыю майстар-майстар. А пры канфігураванні лёгка памыліцца, што прывядзе да збояў запісу.
  • У некаторых баз алгарытмы партыцыравання заснаваныя на першасных ключах. Паслядоўныя ID могуць прывесці да непрадказальнага ўзнікнення "гарачых кропак" і падвышанай нагрузкі на некаторыя партіціі, у той час як іншыя будуць прастойваць.
  • Першасны ключ - гэта самы хуткі спосаб доступу да радка ў базе дадзеных. Пры наяўнасці лепшых спосабаў ідэнтыфікацыі запісаў, паслядоўныя ID могуць ператварыць найважнейшы слупок у табліцах у бескарысны, напоўнены бессэнсоўнымі значэннямі. Таму пры магчымасці, калі ласка, выбірайце глабальна ўнікальны і натуральны першасны ключ (напрыклад, імя карыстальніка).

Перш чым вызначыцца з падыходам, разгледзьце ўплыў auto-increment'аваных ID і UUID на індэксаванне, партыцыраванне і шардынг.

Састарэлыя (stale) дадзеныя могуць быць карысныя і не патрабаваць блакавання

Упраўленне паралельным доступам з дапамогай шматверсійнасці (MVCC) рэалізуе многія патрабаванні да ўзгодненасці, якія коратка абмяркоўваліся вышэй. Некаторыя базы дадзеных (напрыклад, Postgres, Spanner) з дапамогай MVCC "скормліваюць" транзакцыям здымкі (snapshot'ы) - больш старыя версіі базы дадзеных. Транзакцыі са snapshot'амі таксама можна серыялізаваць для забеспячэння ўзгодненасці. Пры чытанні са старога snapshot'a счытваюцца састарэлыя дадзеныя.

Чытанне злёгку састарэлых дадзеных можа спатрэбіцца, напрыклад, пры генераванні аналітыкі на аснове дадзеных або вылічэнні прыкладных агрэгаваных значэнняў.

Першая перавага працы з састарэлымі дадзенымі - нізкая затрымка (асабліва калі база дадзеных размеркавана па розных геаграфічных рэгіёнах). Другое - у тым, што транзакцыі «толькі для чытання» вольныя ад блакавання. Гэта істотная перавага для прыкладанняў, якія шмат чытаюць, калі, вядома, дапушчальная праца са састарэлымі дадзенымі.

Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных
Сервер прыкладання счытвае з лакальнай рэплікі дадзеныя, састарэлыя на 5 секунд, нават калі іх свежая версія даступная па іншым боку Ціхага акіяна

СКБД аўтаматычна зачышчаюць старыя версіі і ў некаторых выпадках дазваляюць рабіць гэта па запыце. Напрыклад, Postgres дазваляе карыстальнікам рабіць VACUUM па запыце, а таксама перыядычна праводзіць гэтую аперацыю ў аўтаматычным рэжыме. Spanner запускае зборшчык смецця, каб пазбавіцца ад здымкаў старэйшых за адну гадзіну.

Скажэнню схільныя любыя крыніцы часу

Самы старанна ахоўны сакрэт у інфарматыцы складаецца ў тым, што ўсе часовыя API хлусяць. Насамрэч нашы машыны не ведаюць дакладны бягучы час. Кампутары ўтрымоўваюць кварцавыя крышталі, якія генеруюць ваганні, якія выкарыстоўваюцца для адліку часу. Аднак яны недастаткова дакладныя і могуць апярэджваць/адставаць ад дакладнага часу. Зрух можа дасягаць 20 секунд у суткі. Таму час на нашых кампутарах неабходна перыядычна сінхранізаваць з сеткавым.

Для сінхранізацыі выкарыстоўваюцца серверы NTP, аднак сам працэс сінхранізацыі схільны затрымкам з-за сеткі. Нават сінхранізацыя з NTP-серверам у тым жа дата-цэнтры займае некаторы час. Зразумела, што праца з публічным NTP-серверам можа прывесці да яшчэ большага скажэння.

Атамныя гадзіны і іх GPS-аналагі лепш падыходзяць для вызначэння бягучага часу, аднак яны дарогі і патрабуюць складанай наладкі, таму не могуць быць устаноўлены на кожнай машыне. З-за гэтага ў цэнтрах апрацоўкі даных выкарыстоўваецца шматузроўневы падыход. Атамныя і/ці GPS-гадзіны паказваюць дакладны час, пасля чаго яно транслюецца астатнім машынам праз другасныя серверы. Гэта азначае, што на кожнай машыне будзе назірацца пэўны зрух ад дакладнага часу.

Сітуацыя пагаршаецца тым, што прыкладанні і БД часта знаходзяцца на розных машынах (калі не ў розных ЦАД). Такім чынам, час будзе адрознівацца не толькі на вузлах ДБ, размеркаваных па розных машынах. Яно таксама будзе іншым і на серверы дадатку.

У Google TrueTime рэалізаваны зусім іншы падыход. Большасць людзей лічаць, што прагрэс Google у гэтым кірунку тлумачыцца банальным пераходам на атамныя і GPS-гадзіны, але гэта толькі частка агульнай карціны. Вось як уладкованы TrueTime:

  • TrueTime выкарыстоўвае дзве розныя крыніцы: GPS і атамныя гадзіны. У гэтых гадзін некарэлюючыя кропкі адмовы (failure modes) [падрабязнасці гл. на 5 старонцы тут - заўв. перав.), таму іх сумеснае выкарыстанне падвышае надзейнасць.
  • У TrueTime незвычайны API. Ён вяртае час у выглядзе інтэрвалу з закладзенай хібнасцю вымярэння і нявызначанасцю. Рэальны момант часу знаходзіцца недзе паміж верхняй і ніжняй межамі інтэрвалу. Spanner, размеркаваная база дадзеных ад Google, проста чакае моманту, калі можна з упэўненасцю сказаць, што цяперашні час выйшла за межы інтэрвалу. Гэты метад прыўносіць пэўную затрымку ў сістэму, асабліва калі нявызначанасць на майстрах высокая, але забяспечвае карэктнасць нават у глабальна размеркаванай сітуацыі.

Больш распрацоўшчыкаў павінны ведаць гэта аб базах дадзеных
Кампаненты Spanner выкарыстоўваюць TrueTime, дзе TT.now() вяртае інтэрвал, так што Spanner проста спіць да моманту, калі можна напэўна сказаць, што бягучы час пераваліла праз пэўную адзнаку

Зніжэнне дакладнасці вызначэння бягучага часу азначае рост працягласці аперацый Spanner і зніжэнне прадукцыйнасці. Вось чаму важна падтрымліваць максімальна высокую дакладнасць нават нягледзячы на ​​тое, што немагчыма атрымаць абсалютна дакладны гадзіннік.

У затрымкі мноства значэнняў

Спытаўшы дзясятак адмыслоўцаў аб тым, што такое затрымка, вы напэўна атрымаеце розныя адказы. У СКБД latency часта называецца "затрымкай базы дадзеных", і яна адрозніваецца аб той, якую ўспрымае кліент. Справа ў тым, што кліент назірае суму сеткавай затрымкі і затрымкі БД. Здольнасць вылучаць тып затрымкі крытычна важная пры адладцы нарастаючых праблем. Пры зборы і адлюстраванні метрык заўсёды імкніцеся сачыць за абодвума тыпамі.

Патрабаванні да прадукцыйнасці варта ацэньваць для канкрэтнай транзакцыі

Часам характарыстыкі прадукцыйнасці СКБД і яе абмежаванні паказваюцца ў выглядзе прапускной здольнасці write/read і затрымкі. Гэта дазваляе атрымаць агульнае ўяўленне аб ключавых параметрах сістэмы, аднак пры ацэнцы прадукцыйнасці новай СКБД значна больш усёабдымны падыход заключаецца ў раздзельнай ацэнцы крытычных аперацый (для кожнага запыту і / або транзакцыі). Прыклады:

  • Прапускная здольнасць запісу і затрымка пры ўстаўцы новага радка ў табліцу Х (з 50 млн. радкоў) з зададзенымі абмежаваннямі і запаўненнем радкоў у звязаных табліцах.
  • Затрымка пры вывадзе сяброў сяброў нейкага карыстальніка, калі сярэдні лік сяброў роўна 500.
  • Затрымка пры выманні топ-100 запісаў з гісторыі карыстальніка, калі ён падпісаны на 500 іншых карыстальнікаў з Х запісаў у гадзіну.

Ацэнка і эксперыментаванне могуць уключаць падобныя крытычныя выпадкі датуль, пакуль вы не будзеце ўпэўненыя, што БД задавальняе патрабаванням да прадукцыйнасці. Аналагічнае эмпірычнае правіла таксама ўлічвае гэтую разбіўку пры зборы latency-метрык і вызначэнні SLO.

Памятайце аб высокай магутнасці (cardinality) пры зборы метрык для кожнай аперацыі. Выкарыстоўвайце логі, збор падзей ці размеркаваную трасіроўку для атрымання адладкавых дадзеных з высокай магутнасцю. У артыкуле «Want to Debug Latency?» вы можаце азнаёміцца ​​з метадалогіямі адладкі затрымак.

Укладзеныя транзакцыі могуць быць небяспечныя

Не кожная СКБД падтрымлівае ўкладзеныя транзакцыі, але калі гэта адбываецца, такія транзакцыі могуць прыводзіць да нечаканых памылак, якія не заўсёды лёгка выявіць (гэта значыць павінна стаць відавочна, што ёсць нейкая анамалія).

Пазбегнуць выкарыстання ўкладзеных транзакцый можна з дапамогай кліенцкіх бібліятэк, якія ўмеюць іх выяўляць і абыходзіць. Калі ад укладзеных транзакцый не атрымліваецца адмовіцца, пастаўцеся да іх рэалізацыі з асаблівай увагай, каб пазбегнуць нечаканых сітуацый, калі здзейсненыя транзакцыі выпадкова перарываюцца з-за ўкладзеных.

Інкапсуляцыя транзакцый у розныя пласты можа прыводзіць да нечаканага ўзнікнення ўкладзеных транзакцый, а з пункту гледжання чытальнасці кода здольная ўскладніць разуменне намераў аўтара. Зірніце на наступную праграму:

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

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар