Транскрипция на лекцията на Брус Момджиан от 2020 г. „Отключване на мениджъра за заключване на Postgres“.
(Забележка: Можете да получите всички SQL заявки от слайдовете на тази връзка:
Здравейте! Страхотно е да съм отново тук в Русия. Съжалявам, че миналата година не можах да дойда, но тази година с Иван имаме големи планове. Надявам се да съм тук много по-често. Обичам да идвам в Русия. Ще посетя Тюмен, Твер. Много се радвам, че ще мога да посетя тези градове.
Казвам се Брус Момджиян. Работя в EnterpriseDB и работя с Postgres повече от 23 години. Живея във Филаделфия, САЩ. Пътувам около 90 дни в годината. И присъствам на около 40 конференции. моя
Преди да започна да работя с Postgres, бях учител, професор. И много се радвам, че сега мога да ви кажа това, което ще ви кажа. Това е една от най-интересните ми презентации. И тази презентация съдържа 110 слайда. Ще започнем да говорим с прости неща, а към края на доклада ще става все по-сложно и ще стане доста сложно.
Това е доста неприятен разговор. Блокирането не е най-популярната тема. Искаме да изчезне някъде. Все едно отиваш на зъболекар.
- Заключването е проблем за много хора, които работят с бази данни и имат множество процеси, работещи едновременно. Имат нужда от блокиране. Тоест днес ще ви дам основни познания за блокирането.
- Идентификационни номера на транзакции. Това е доста скучна част от презентацията, но те трябва да бъдат разбрани.
- След това ще говорим за видовете блокиране. Това е доста механична част.
- И тогава ще дадем няколко примера за блокиране. И ще бъде доста трудно за разбиране.
Да поговорим за блокирането.
Нашата терминология е доста сложна. Колко от вас знаят откъде идва този пасаж? Двама души. Това е от игра, наречена Colossal Cave Adventure. Мисля, че беше текстова компютърна игра през 80-те години. Там беше необходимо да се влезе в пещерата, в лабиринта и текстът се промени, но съдържанието беше приблизително същото всеки път. Така помня този мач.
И тук виждаме името на ключалките, дошли при нас от Oracle. Ние ги използваме.
Тук виждаме термини, които ме объркват. Например, SHARE UPDATE ECXLUSIVE. Следващ СПОДЕЛЕТЕ RAW EXXLUSIVE. Честно казано, тези имена не са много ясни. Ще се опитаме да ги разгледаме по-подробно. Някои съдържат думата „споделяне“, което означава разделяне. Някои съдържат думата "exclusive" - изключителен. Някои съдържат и двете думи. Бих искал да започна с това как работят тези брави.
И думата „достъп“ също е много важна. И думите "ред" - линия. Тоест разпределение на достъпа, разпределение на редове.
Друг проблем, който трябва да бъде разбран в Postgres, който за съжаление няма да мога да покрия в моя доклад, е MVCC. Имам отделна презентация по тази тема на моя уебсайт. И ако смятате, че тази презентация е трудна, тогава MVCC е може би най-трудната за мен. А при интерес може да го видите в сайта. Можете да гледате видеото.
Друго нещо, което трябва да разберем, са идентификаторите на транзакциите. Много транзакции не могат да работят без уникални идентификатори. И тук имаме обяснение какво е транзакция. Postgres има две системи за номериране на транзакции. Знам, че не е много красиво решение.
Също така имайте предвид, че слайдовете ще бъдат доста трудни за четене, така че това, което е маркирано в червено, е това, на което трябва да обърнете внимание.
Ние гледаме. Номерът на транзакцията е маркиран в червено. Тук е показана функцията SELECT pg_back. Връща моята транзакция и идентификационния номер на тази транзакция.
Още нещо, ако харесвате тази презентация и искате да я стартирате във вашата база данни, тогава можете да следвате тази връзка, маркирана в розово, и да изтеглите SQL за тази презентация. И можете просто да го стартирате във вашия PSQL и цялата презентация ще бъде на екрана ви за нула време. Няма да съдържа цветя, но поне можем да го видим.
В този случай виждаме идентификатора на транзакцията. Това е номерът, който й дадохме. Има и друг тип идентификатор на транзакция в Postgres, наречен идентификатор на виртуална транзакция
И ние трябва да разберем това. Това е много важно, в противен случай няма да можем да разберем заключването в Postgres.
Виртуален идентификатор на транзакция е идентификатор на транзакция, който не съдържа постоянни стойности. Например, ако изпълня команда SELECT, тогава най-вероятно няма да променя базата данни, няма да заключвам нищо. Така че, когато изпълняваме прост SELECT, ние не даваме на транзакцията постоянен идентификатор. Там й даваме само виртуална лична карта.
И това подобрява производителността на Postgres, подобрява възможността за почистване, така че ID на виртуалната транзакция се състои от две числа. Първото число преди наклонената черта е ID на бекенда. А отдясно виждаме само брояч.
Следователно, ако изпълня заявка, тя казва, че бекенд ID е 2.
И ако изпълня поредица от такива транзакции, тогава виждаме, че броячът се увеличава всеки път, когато изпълня заявката. Например, когато стартирам заявка 2/10, 2/11, 2/12 и т.н.
Имайте предвид, че тук има две колони. Вляво виждаме ID на виртуалната транзакция - 2/12. А отдясно имаме постоянен идентификатор на транзакция. И това поле е празно. И тази транзакция не променя базата данни. Следователно не му присвоявам постоянен идентификатор на транзакция.
Веднага щом изпълня командата за анализ ((ANALYZE)), същата заявка ми дава постоянен идентификатор на транзакция. Вижте как сме се променили. Преди нямах тази лична карта, сега имам.
Ето още една заявка, друга транзакция. Номерът на виртуалната транзакция е 2/13. И ако поискам постоянен идентификатор на транзакция, тогава, когато изпълня заявката, ще го получа.
И така, още веднъж. Имаме идентификатор на виртуална транзакция и постоянен идентификатор на транзакция. Просто вземете тази точка, за да разберете поведението на Postgres.
Преминаваме към трети раздел. Тук просто ще разгледаме различните видове ключалки в Postgres. Не е много интересно. Последният раздел ще бъде много по-интересен. Но трябва да се съобразяваме с основните неща, защото иначе няма да разберем какво ще последва.
Ще преминем през този раздел, ще разгледаме всеки тип блокиране. И ще ви покажа примери за това как се инсталират, как работят, ще ви покажа някои заявки, които можете да използвате, за да видите как работи блокирането в Postgres.
За да създадем заявка и да видим какво се случва в Postgres, трябва да изпратим заявката към системния изглед. В този случай pg_lock е маркиран в червено. pg_lock е системна таблица, която ни казва кои ключалки се използват в момента в Postgres.
Въпреки това ми е много трудно да ви покажа pg_lock сам по себе си, защото е доста сложен. Така че създадох изглед, който показва pg_locks. Освен това върши някаква работа за мен, която ми позволява да разбирам по-добре. Това означава, че изключва моите заключвания, моята собствена сесия и т.н. Това е просто стандартен SQL и ви позволява да покажете по-добре какво се случва.
Друг проблем е, че този изглед е много широк, така че трябва да създам втори - lockview2.
И ми показва още колони от таблицата. И още един, който ми показва останалите колони. Това е доста сложно, затова се опитах да го представя възможно най-просто.
И така, създадохме таблица, наречена Lockdemo. И създадохме една линия там. Това е нашата примерна таблица. И ние ще създадем секции само за да ви покажем примери за блокиране.
И така, един ред, една колона. Първият тип заключване се нарича ACCESS SHARE. Това е най-малко ограничаващото блокиране. Това означава, че практически не е в конфликт с други ключалки.
И ако искаме изрично да дефинираме заключване, изпълняваме командата "lock table". И изрично ще блокира, т.е. в режим ACCESS SHARE, стартираме таблицата за заключване. И ако стартирам PSQL във фонов режим, тогава стартирам втората сесия от първата си сесия по този начин. Тоест какво ще правя тук? Отивам на друга сесия и му казвам "покажи ми изгледа за заключване за тази заявка". И тук имам AccessShareLock на тази маса. Точно това поисках. И той казва, че ключалката е зададена. Много просто.
Освен това, ако погледнем втората колона, там няма нищо. Празни са.
И ако изпълня командата "SELECT", тогава това е имплицитният (изричен) начин за заявка на AccessShareLock. Така че освобождавам таблицата си и изпълнявам заявка и заявката връща множество редове. И в един от редовете виждаме AccessShareLock. Така че SELECT извиква AccessShareLock на масата. И не влиза в конфликт с почти нищо, защото е заключване на ниско ниво.
Какво ще стане, ако стартирам SELECT и имам три различни таблици? Преди работех само с една таблица, сега изпълнявам три: pg_class, pg_namespace и pg_attribute.
И сега, когато гледам заявката, виждам 9 AccessShareLocks в XNUMX таблици. Защо? Три таблици са маркирани в синьо: pg_attribute, pg_class, pg_namespace. Но можете също да видите, че всички индекси, които са дефинирани чрез тези таблици, също имат AccessShareLock.
И това е блокиране, което практически не противоречи на другите. И всичко, което прави, е просто да ни предпази от изпускане на таблицата, докато я избираме. Има смисъл. Тоест, ако изберем таблица, тя изчезва в този момент, тогава това е грешно, следователно AccessShare е заключване на ниско ниво, което ни казва „не изтривайте тази таблица, докато работя“. По принцип това е всичко, което тя прави.
ROW SHARE - Тази ключалка е малко по-различна.
Да вземем пример. SELECT ROW SHARE начин за заключване на всеки ред поотделно. По този начин никой не може да ги изтрие или промени, докато ги гледаме.
И така, какво прави SHARE LOCK? Виждаме, че ID на транзакцията е 681 за SELECT. И е интересно. Какво е станало тук? За първи път виждаме номера в полето "Заключване". Взимаме идентификатора на транзакцията и той казва, че го блокира в изключителен режим. Всичко, което прави, е, че казва, че имам ред, който технически е заключен някъде в таблицата. Но не казва къде точно. Ще разгледаме това по-подробно малко по-късно.
Тук казваме, че ключалката се използва от нас.
И така, изключителна ключалка изрично (изрично) казва, че е изключителна. И също така, ако изтриете ред в тази таблица, това се случва, както можете да видите.
SHARE EXCLUSIVE е по-дълъг кичур.
Това е (ANALYZE) командата на анализатора, която ще се използва.
ЗАКЛЮЧВАНЕ НА СПОДЕЛЯНЕ - Можете изрично да заключите в режим на споделяне.
Можете също да създадете уникален индекс. И там можете да видите SHARE LOCK, който е част от тях. И той заключва масата и задава заключване SHARE LOCK върху нея.
Стандартното ЗАКЛЮЧВАНЕ НА СПОДЕЛЯНЕ на таблица означава, че други хора могат да четат таблицата, но никой не може да я променя. И точно това се случва, когато създадете уникален индекс.
Ако създам уникален паралелен индекс, тогава ще имам различен тип заключване, защото не забравяйте, че използването на едновременни индекси намалява изискването за заключване. И ако използвам нормално заключване, нормален индекс, тогава предотвратявам писането в индекса на таблицата по време на нейното създаване. Ако използвам паралелен индекс, тогава трябва да използвам различен тип заключване.
SHARE ROW EXCLUSIVE - отново може да се зададе изрично (изрично).
Или можем да създадем правило, тоест да вземем някакъв конкретен случай, в който ще се използва.
ЕКСКЛУЗИВНО заключване означава, че никой друг не може да промени масата.
Тук виждаме различни видове брави.
ACCESS EXCLUSIVE, например, е команда за заключване. Например, ако го направите CLUSTER table
, тогава това ще означава, че никой няма да може да пише там. И заключва не само самата таблица, но и индексите.
Това е втората страница на ключалката ACCESS EXCLUSIVE, където виждаме какво точно заключва в таблицата. Той заключва отделни редове на таблицата, което е достатъчно интересно.
Това е цялата основна информация, която исках да дам. Говорихме за ключалки, за идентификатори на транзакции, говорихме за идентификатори на виртуални транзакции, за постоянни идентификатори на транзакции.
А сега ще преминем през примерите за блокиране. Това е най-интересната част. Ще видим много интересни случаи. И моята цел в тази презентация е да ви дам по-добра представа за това какво всъщност прави Postgres, когато се опитва да блокира нещата. Струва ми се, че е много добър в блокирането на отделни части.
Нека да разгледаме някои конкретни примери.
Ще започнем с таблици и по един ред на таблица. Когато вмъкна нещо, получавам ExclusiveLock, ID на транзакцията и ExclusiveLock на масата.
Какво се случва, ако вмъкна още два реда? И сега нашата таблица има три реда. И аз вмъкнах един ред и получих това като изход. И ако вмъкна още два реда, какво странно има тук? Тук има странност, защото добавих три реда към тази таблица, но все още имам два реда в таблицата за заключване. И това всъщност е фундаменталното поведение на Postgres.
Много хора смятат, че ако в база данни заключите 100 реда, тогава ще трябва да създадете 100 записа за заключване. Ако блокирам 1 реда наведнъж, тогава ще ми трябват 000 такива заявки. И ако ми трябва милион или милиард за блокиране. Но ако направим това, няма да работи много добре. Ако сте използвали система, която създава блокиращи записи за всеки отделен ред, можете да видите, че това е сложно. Защото трябва да дефинирате таблицата за заключване веднага, което може да препълни, но Postgres не прави това.
И е много важно на този слайд ясно да демонстрира, че има друга система, която работи вътре в MVCC, която блокира отделни линии. Така че, когато заключите милиарди редове, Postgres не създава милиард отделни инструкции за заключване. И това е много добре за производителността.
Какво ще кажете за актуализация? Сега актуализирам серията и можете да видите, че е извършила две различни операции наведнъж. В същото време заключи таблицата, но заключи и индекса. И той трябваше да заключи индекса, защото има уникални ограничения на тази таблица. И искаме да сме сигурни, че никой не го променя, затова го блокираме.
Какво се случва, ако искам да актуализирам два реда? И виждаме, че той се държи по същия начин. Ние правим два пъти повече актуализации, но точно същия брой блокиращи линии.
Ако се чудите как Postgres прави това, трябва да слушате моите разговори за MVCC, за да разберете как Postgres вътрешно маркира тези редове, които променя. И Postgres има начин да го направи, но не го прави на нивото на заключване на таблицата, а на по-ниско и по-ефективно ниво.
Ами ако искам да изтрия нещо? Ако изтрия например един ред и все още имам двата си входа при заключване и дори да искам да ги изтрия всичките, те пак са там.
И, например, искам да вмъкна 1 реда и след това или да изтрия, или да добавя 000 реда, тогава отделните редове, които добавям или променям, те не се записват тук. Те са написани на по-ниско ниво в самия ред. И по време на разговора на MVCC говорих за това подробно. Но е много важно, когато анализирате ключалки, да сте сигурни, че имате заключване на ниво таблица и че не можете да видите как отделните редове се записват тук.
Какво ще кажете за изричното блокиране?
Ако щракна върху „опресняване“, тогава имам заключени два реда. И ако ги избера всички и щракна върху „актуализиране навсякъде“, тогава все още имам два записа за заключване.
Ние не създаваме отделни записи за всеки отделен ред. Тъй като тогава производителността пада, може да има твърде много. И може да се окажем в неприятна ситуация.
И същото нещо, ако го направим споделено, можем да направим всичко 30 пъти.
Възстановяваме нашата таблица, изтриваме всичко, след което вмъкваме отново един ред.
Друг вид поведение, което виждате в Postgres, което е много добре известно и желано поведение, е, че можете да направите актуализация или избор. И можете да го направите едновременно. И изберете не блокира актуализацията и същото нещо в обратната посока. Ние казваме на читателя да не блокира писателя, а писателят не е блокирал читателя.
Ще ви покажа пример за това. Сега ще направя избор. След това ще направим INSERT. И тогава можете да видите - 694. Можете да видите идентификатора на транзакцията, която е направила това вмъкване. И това е как работи.
И ако сега погледна моя бекенд ID, той стана - 695.
И виждам, че 695 се появява в моята таблица.
И ако актуализирам тук по този начин, тогава получавам различен случай. В този случай 695 е изключително заключване и актуализацията има същото поведение, но няма конфликт между тях, което е доста необичайно.
И можете да забележите, че отгоре е ShareLock, а отдолу е ExclusiveLock. И двете транзакции бяха успешни.
И трябва да чуете моята реч в MVCC, за да разберете как се случва това. Но това е илюстрация на факта, че можете да го направите по едно и също време, т.е. да направите SELECT и UPDATE едновременно.
Нека нулираме и повторим една операция.
Ако се опитате да стартирате две актуализации едновременно на един и същи ред, това ще блокира. И не забравяйте, че казах, че читателят не блокира писателя, а писателят на читателя, но един писател блокира друг писател. Тоест не можем да накараме двама души да актуализират един и същ ред едновременно. Трябва да изчакате, докато един от тях свърши.
И за да илюстрирам това, ще разгледам таблицата на Lockdemo. И ще разгледаме един ред. За транзакция 698.
Ние го надстроихме до 2. 699 е първата актуализация. И беше успешен или е в чакаща транзакция, чакаща да се ангажираме или анулираме.
Но вижте нещо друго - 2/51 е първата ни транзакция, първата ни сесия. 3/112 е втората заявка, която дойде отгоре и промени тази стойност на 3. И ако забележите, горната се заключи, което е 699. Но 3/112 не предостави заключване. Колоната Lock_mode казва, че чака. Той очаква 699. И ако погледнете къде е 699, той е по-висок. И какво направи първата сесия? Тя създаде ексклузивно заключване на собствения си идентификатор на транзакция. Ето как го прави Postgres. Той блокира своя собствен идентификатор на транзакция. И ако искате да изчакате някой да се ангажира или отмени, тогава трябва да изчакате, докато има чакаща транзакция. И така можем да видим странна линия.
Нека погледнем отново. Отляво виждаме нашия ID за обработка. Във втората колона виждаме нашия идентификатор на виртуална транзакция, а в третата виждаме lock_type. Какво означава това? Всъщност тя казва, че блокира идентификатора на транзакцията. Но забележете, че във всички редове най-отдолу е написана връзка. И така имате два вида ключалки на масата. Има заключване на връзката. И също така има заключване на транзакция, където ви заключваме сами, точно това се случва на първия ред или в най-долния край, където е транзакция, където очакваме 699 да завърши своята операция.
Виждам какво става тук. И тук се случват две неща едновременно. Гледате заключването на ID на транзакцията в първия ред, което се заключва само. И тя се блокира, за да накара хората да чакат.
Ако погледнете 6-ия ред, това е същият запис като първия. И така транзакция 699 е блокирана. 700 също е самозаключваща се. И тогава в долния ред ще видите, че чакаме 699 да завърши своята операция.
И в lock_type, tuple виждате числа.
Можете да видите, че е 0/10. И това е номерът на страницата, а също и отместването на този конкретен ред.
И виждате какво става 0/11, когато актуализираме.
Но всъщност е 0/10, защото има очакване за тази операция. Имаме възможност да видим, че това е редът, който чакам да потвърдя.
След като го потвърдихме и натиснахме commit и когато актуализацията приключи, това е, което получаваме отново. Транзакция 700 е единствената ключалка, тя не чака никой друг, защото е ангажирана. Просто изчаква транзакцията да приключи. След като 699 приключи, ние не чакаме нищо друго. И сега транзакция 700 казва, че всичко е наред, че има всички ключалки, от които се нуждае във всички разрешени таблици.
И за да усложним допълнително цялата работа, създаваме друг изглед, който този път ще ни предостави йерархия. Не очаквам да разберете тази молба. Но ще ни даде по-ясна представа какво се случва.
Това е рекурсивен изглед, който също има още един раздел. И след това връща всичко отново заедно. Нека използваме това.
Ами ако направим три едновременни актуализации и кажем, че редът вече е три. И ще променим 3 на 4.
И тук виждаме 4. И идентификатор на транзакция 702.
И тогава ще разменя 4 за 5. И 5 за 6, и 6 за 7. И подреждам няколко души, които да чакат тази транзакция да завърши.
И всичко става ясно. Какъв е първият ред? Това е 702. Това е идентификаторът на транзакцията, който първоначално е задал тази стойност. Какво имам в колоната „Разрешено“? Имам белези f
. Това са моите актуализации, които (5, 6, 7) не могат да бъдат одобрени, защото чакаме изтичането на ID на транзакцията 702. Там имаме заключване на ID на транзакция. И се оказва 5 идентификатора за заключване на транзакция.
И ако погледнете 704, 705, там още нищо не пише, защото те още не знаят какво става. Те просто пишат, че нямат представа какво се случва. И те просто ще заспят, защото чакат някой да свърши и ще бъдат събудени, когато е възможно да сменят реда.
Ето как изглежда. Ясно е, че всички те чакат 12-та линия.
Това е, което видяхме тук. Ето 0/12.
И така, след като първата транзакция бъде одобрена, можете да видите как работи йерархията тук. И сега всичко е ясно. Всички стават чисти. И те всъщност все още чакат.
Ето какво се случва. 702 е ангажиран. И сега 703 получава това заключване на ред и след това 704 започва да чака 703 да се ангажира. И 705 също чака това. И когато всичко това е завършено, те се почистват. И държа да отбележа, че всички се нареждат. И много прилича на ситуацията със задръстване, когато всеки чака първата кола. Първата кола е спряла и всички се нареждат на дълга опашка. След това се движи, след това следващата кола може да излезе напред и да получи своя блок и т.н.
И ако ви се стори недостатъчно трудно, сега ще поговорим с вас за безизходици. Не знам кой от вас ги е преживял. Това е доста често срещан проблем в системите с бази данни. Но блокиранията са, когато една сесия чака друга сесия да направи нещо. И в този момент друга сесия чака първата сесия, за да направи нещо.
И например, ако Иван каже: „Дай ми нещо“, а аз казвам: „Не, ще ти го дам само ако ми дадеш нещо друго“. И той казва: „Не, няма да ти го дам, ако ти не ми го дадеш“. И се озоваваме в безизходна ситуация. Сигурен съм, че Иван няма да го направи, но разбирате, че имаме двама души, които искат нещо и не са готови да го дадат, докато другият не им даде това, което искат. И няма решение.
И всъщност вашата база данни трябва да открие това. И тогава трябва да изтриете или затворите една от сесиите, защото в противен случай те ще останат там завинаги. И го виждаме в базите данни, виждаме го в операционните системи. И на всички места, където имаме паралелни процеси, това може да се случи.
И сега ще поставим две задънени точки. Ще поставим 50 и 80. В първия ред ще актуализирам от 50 на 50. Ще получа номер на транзакция 710.
И тогава ще променя 80 на 81 и 50 на 51.
И ето как ще изглежда. И така 710 има заключване на ред, а 711 чака потвърждение. Видяхме го, когато актуализирахме. 710 - е собственик на нашата серия. И 711 чака 710 да завърши транзакцията.
И дори пише на кой ред имаме задънени блокировки. И тук започва да става странно.
Сега актуализираме 80 на 80.
И тук започват безизходиците. 710 чака отговор от 711, а 711 чака 710. И това няма да свърши добре. И няма изход от това. И те ще очакват отговор един от друг.
И просто започва да забавя всичко. А ние не искаме това.
И Postgres има начини да забележи, когато това се случи. И когато това се случи, получавате тази грешка. И от това става ясно, че такъв и такъв процес чака SHARE LOCK от друг процес, т.е. който е блокиран от процеса 711. И този процес чакаше SHARE LOCK да бъде даден на такъв и такъв идентификатор на транзакция и да бъде блокиран от такъв и такъв процес. Следователно има ситуация на мъртво блокиране.
Има ли тристранни безизходици? Възможно ли е? да
Вкарваме тези числа в таблицата. Сменяме 40 на 40, правим ключалка.
Променете 60 на 61, 80 на 81.
И тогава сменяме 80 и тогава бум!
И 714 сега чака 715. 716 чака 715. И няма какво да се направи по въпроса.
Вече няма двама души, вече са трима. Аз искам нещо от теб, този иска нещо от трети човек, а третият иска нещо от мен. И в крайна сметка се озоваваме в тройно чакане, защото всички чакаме другият човек да завърши това, което трябва да направи.
И Postgres знае на кой ред се случва. И така ще ви даде следното съобщение, което показва, че имате проблем, при който трите входа се блокират един друг. И няма никакви ограничения. Това може да е случаят, когато 20 записа се блокират един друг.
Следващият брой може да бъде сериализиран.
Ако специална сериализуема ключалка.
И се връщаме на 719. Той има напълно нормален проблем.
И можете да натиснете, за да направите транзакция от сериализуема.
И вие разбирате, че сега имате различен вид SA блокиране - това означава сериализуемо.
И така имаме нов вид заключване, наречено SARieadLock, което е серийно заключване и ви позволява да въвеждате серийни номера.
Освен това можете да вмъкнете уникални индекси.
В тази таблица имаме уникални индекси.
Така че, ако сложа числото 2 тук, ето защо имам 2. Но най-отгоре сложих още 2. И можете да видите, че 721 има изключителна ключалка. Но сега 722 чака 721 да завърши своята операция, защото не може да вмъкне 2, докато не разбере какво ще се случи с 721.
И ако направим подтранзакция.
Тук имаме 723.
И ако запазим точката и след това я актуализираме, тогава получаваме нов идентификатор на транзакция. Това е друго поведение, с което трябва да сте наясно. Ако върнем това, идентификаторът на транзакцията го няма. 724 си тръгва. Но сега имаме 725.
И какво се опитвам да направя тук? Опитвам се да ви покажа примери за необичайни ключалки, които можете да намерите: било то ключалки с възможност за сериализиране или ключалки SAVEPOINT, това са различни видове ключалки, които ще се появят в таблицата за заключване.
Това е създаването на явни (изрични) заключвания, които имат pg_advisory_lock.
И можете да видите, че типът заключване е посочен тук като препоръчителен. И тук пише „препоръчително“ в червено. И можете едновременно да блокирате с pg_advisory_unlock.
И в заключение бих искал да ви покажа още нещо умопомрачително. Ще създам друг изглед. Но аз ще съединя таблицата pg_locks с таблицата pg_stat_activity. И защо искам да направя това? Защото това ще ми позволи да погледна и да видя всички текущи сесии и да видя какви заключвания чакат. И е достатъчно интересно, когато съставим таблица за заключване и таблица за заявки.
И тук създаваме pg_stat_view.
И актуализираме реда с един. И тук виждаме 724. И след това актуализираме нашия ред до три. И какво виждате тук сега? Това са заявки, т.е. виждате целия списък от заявки, които са изброени в лявата колона. И след това от дясната страна можете да видите ключалки и какво създават. И може да бъде по-разбираемо за вас, така че да не се налага да се връщате към всяка сесия всеки път и да виждате дали трябва да се присъедините към нея или не. Те го правят за нас.
Друга много полезна функция е pg_blocking_pids
. Вероятно никога не сте чували за нея. Какво прави тя? Това ни позволява да кажем, че за тази сесия 11740 какви идентификатори на процеси чака. И можете да видите, че 11740 очаква 724. А 724 е най-отгоре. А 11306 е идентификаторът на вашия процес. По същество тази функция надхвърля вашата заключваща таблица. И знам, че е малко сложно, но схващате идеята. По същество тази функция преминава през тази заключваща таблица и се опитва да намери къде е този ID на процеса, като се имат предвид заключванията, които чака. И също така се опитва да разбере кой идентификатор на процес има процесът, който чака за заключване. Така че можете да стартирате тази функция pg_blocking_pids
.
И това е много полезно. Добавихме това едва след версия 9.6, така че тази функция е само на 5 години, но е много, много полезна. Същото важи и за второто искане. Показва точно това, което трябва да видим.
Ето за това исках да говоря с теб. И както очаквах, използвахме цялото си време, защото имаше толкова много слайдове. И слайдовете са достъпни за изтегляне. Бих искал да ви благодаря, че сте тук. Сигурен съм, че ще се насладите на останалата част от конференцията, благодаря ви много!
въпроси:
Например, ако се опитам да актуализирам редовете и втората сесия се опита да изтрие цялата таблица. Доколкото разбирам, трябва да има нещо като intent lock. Има ли такова нещо в Postgres?
Връщаме се в самото начало. Може би си спомняте, че когато правите нещо, например когато правите SELECT, ние издаваме AccessShareLock. И предотвратява изпускането на масата. Така че, ако например искате да актуализирате ред в таблица или да изтриете ред, тогава някой не може да изтрие цялата таблица едновременно, защото държите този AccessShareLock върху цялата таблица и над реда. И след като сте готови, те могат да го премахнат. Но докато директно промените нещо там, те няма да могат да го направят.
Нека го направим отново. Да преминем към примера за изтриване. И виждате как редът има изключително заключване върху цялата маса.
Ще изглежда като изключителна ключалка, нали?
Да, така изглежда. Разбирам за какво говориш. Искате да кажете, че ако направя SELECT, тогава имам ShareExclusive и след това го поставя в състояние Row Exclusive, това става ли проблем? Но изненадващо това не представлява проблем. Това е като увеличаване на степента на заключване, но по същество имам заключване, което предотвратява изтриването му. И сега, когато направя тази ключалка по-мощна, тя все още предотвратява изтриването. Така че не е като да се качвам. Т.е. предотврати го и когато беше на по-ниско ниво, така че когато го издигна, пак предотвратява изпускането на масата.
Разбирам за какво говориш. Няма случай на увеличаване на степента на блокиране, при който се опитвате да се откажете от един блок, за да въведете по-мощен. Тук просто увеличава това избягване навсякъде, така че не предизвиква никакъв конфликт. Но това е добър въпрос. Благодаря ви много, че попитахте!
Какво трябва да направим, за да избегнем безизходна ситуация, когато имаме много сесии, голям брой потребители?
Postgres автоматично забелязва ситуации на блокиране. И автоматично ще изтрие една от сесиите. Единственият начин да избегнете безизходна ситуация е да блокирате хората в същия ред. Така че, когато погледнете приложението си, това често е причината за блокиране... Да кажем, че искам да блокирам две различни неща. Едно приложение заключва таблица 1, а друго приложение заключва таблица 2 и след това таблица 1. И най-лесният начин да избегнете блокировки е да погледнете вашето приложение и да се опитате да се уверите, че заключването се случва в същия ред във всички приложения. И това обикновено премахва 80% от проблемите, защото всякакви хора пишат тези приложения. И ако ги блокирате в същия ред, тогава няма да попаднете в ситуация на задънена улица.
Благодаря ви много за представянето ви! Говорихте за пълен вакуум и, ако разбирам правилно, пълен вакуум изкривява реда на записите в отделно хранилище, така че запазва текущите записи непроменени. Защо vacuum full взема изключителен достъп за заключване и защо е в конфликт с операциите за запис?
Това е добър въпрос. Причината е, че вакуумът пълен заема маса. И по същество създаваме нова версия на таблицата. И масата ще е нова. Оказва се, че това ще бъде напълно нова версия на масата. И проблемът е, че когато правим това, не искаме хората да го четат, защото искаме те да видят новата таблица. И това е свързано с предишния въпрос. Ако можехме да четем едновременно, тогава нямаше да можем да го преместим и да насочим хората към нова маса. Ще трябва да изчакаме всички да приключат с четенето на тази таблица и така, по същество, това е ситуация с изключителна ключалка.
Просто казваме, че заключваме от самото начало, защото знаем, че ще се нуждаем от изключително заключване в самия край, за да преместим всички към новото копие. Така че потенциално можем да го разрешим. И ето как го правим с едновременно индексиране. Но това е много по-трудно да се направи. И това се отнася много силно за предишния ви въпрос относно ексклузивното заключване.
Възможно ли е да се добави таймаут за заключване в Postgres? В Oracle мога например да напиша „изберете за актуализиране“ и да изчакам 50 секунди, преди да актуализирам. Добре беше за приложението. Но в Postgres или трябва да направя това веднага и да не чакам изобщо, или да изчакам известно време.
Да, можете да изберете да изчакате вашите ключалки, вашите ключалки. Можете също да издадете командата no way, която ще бъде ... ако не можете да получите ключалката веднага. Следователно или изчакване за заключване, или нещо друго, което ще ви позволи да направите това. Това не се прави на синтактично ниво. Това се прави като променлива на сървъра. Понякога не може да се използва.
Можете ли да отворите слайд 75?
Да.
И моят въпрос е следващият. Защо и двата процеса на актуализиране чакат 703?
И това е страхотен въпрос. Между другото, не разбирам защо Postgres прави това. Но когато 703 беше създаден, той чакаше 702. И когато 704 и 705 се появиха, те изглежда не знаят какво чакат, защото там все още няма нищо. А Postgres го прави така: когато не можете да получите ключалка, той казва "Какъв е смисълът да ви обработвам?", Защото вече чакате някого. Така че просто го оставете да виси във въздуха, той изобщо не го актуализира. Но какво се случи тук? Веднага след като 702 завърши процеса и 703 получи своето заключване, системата се върна обратно. И тя каза, че сега имаме двама души, които чакат. И тогава нека ги актуализираме заедно. И посочете, че се очакват и двете.
Не знам защо Postgres прави това. Но има проблем, наречен f…. Струва ми се, че това не е термин на руски. Това е, когато всеки чака един замък, дори ако има 20 инстанции, които чакат замъка. И изведнъж всички се събуждат едновременно. И всеки започва да се опитва да реагира. Но системата прави така, че всички чакат 703. Защото всички чакат и веднага ще ги наредим всички. И ако се появи друга нова заявка, която е била формирана след това, например 707, тогава отново ще има празнота.
И струва ми се, че това се прави, за да може да се каже, че на този етап 702 чака 703 и всички, които идват след това, те няма да имат никакво влизане в това поле. Но веднага щом първият сервитьор си тръгне и всички, които са чакали в този момент преди актуализацията, получават същия знак. И така ми се струва, че това се прави, за да можем да обработваме по ред, така че да са правилно подредени.
Винаги съм гледал на това като на доста странен феномен. Защото тук, например, те изобщо не са посочени. Но, струва ми се, всеки път, когато даваме нова ключалка, гледаме всички, които са в процес на чакане. След това ги редим всичките. И след това всеки нов, който идва, се поставя на опашка само когато следващият човек приключи обработката. Много добър въпрос. Благодаря ви много за вашия въпрос!
Струва ми се много по-логично, когато 705 очаква 704.
Но проблемът тук е следният. Технически можете да събудите или един, или онзи. И така събуждаме едното или другото. Но какво се случва при работата на системата? Можете да видите как 703 най-отгоре е блокирал собствения си идентификатор на транзакция. Ето как работи Postgres. И 703 е блокиран от собствения си идентификатор на транзакция, така че ако някой иска да изчака, той ще изчака 703. И всъщност 703 завършва. И едва след завършването му един от процесите се събужда. И не знаем какъв процес ще бъде. След това постепенно обработваме всичко. Но не е ясно кой процес се събужда първи, защото може да е всеки от тези процеси. По принцип имахме планировчик, който каза, че сега можем да събудим всеки от тези процеси. Просто избираме един на случаен принцип. Следователно и двете трябва да бъдат отбелязани, защото можем да събудим всяка от тях.
И проблемът е, че имаме CP-безкрайност. И следователно е много вероятно да събудим по-късния. И ако например се събудим по-късно, тогава ще изчакаме този, който току-що е получил ключалката, така че не определяме кой точно ще бъде събуден пръв. Ние създаваме точно такава ситуация и системата ще ги събуди на случаен принцип.
Има
Източник: www.habr.com