Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Разшифроване на доклада от 2020 г. на Брус Момжиан "Отваряне на мениджъра на заключвания в Postgres".

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

(Забележка: Всички SQL запитвания от слайдов можете да получите на този линк: http://momjian.us/main/writings/pgsql/locking.sql)

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

Казвам се Брус Момжиан. Работя в EnterpriseDB и се занимавам с Postgres повече от 23 години. Живея в Филаделфия, САЩ. Пътувам около 90 дни в годината и посещавам около 40 конференции. Моят веб сайт, който съдържа слайдовете, които ще ви покажа сега. След конференцията можете да ги изтеглите от личния ми сайт. Там също така има около 30 презентации. Освен това има видео и много записи в блога, над 500. Това е доста съдържателен ресурс. И ако ви интересува този материал, ви приканвам да се възползвате от него.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Нека говорим за заключванията.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Терминология у нас е доста сложна. Колко от вас знаят откъде е този откъс? Двама души. Това е от игра, която се нарича „Колосално приключение в пещерата“. Беше текстова компютърна игра от 80-те години, мисля. Трябваше да влезеш в пещера, в лабиринт и текстът се променяше, но съдържанието беше приблизително същото всеки път. Така помня тази игра.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И тук виждаме наименования на блокировки, които дойдоха при нас от Oracle. Използваме ги.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Тук виждаме термини, които ми създават съмнения. Например, SHARE UPDATE EXCLUSIVE. После SHARE RAW EXCLUSIVE. Честно казано, тези наименования не са много ясни. Ще се опитаме да ги разгледаме по-подробно. Някои съдържат думата „share“, което означава – отделям. Някои съдържат думата „exclusive“ — ексклузивен. В някои са дуите две думи. Искам да започна с това как работят тези блокировки.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И също е много важно словото „достъп“ — access. И думата „row“ — ред. Т. е. разпределение на достъпа, разпределение на редовете.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Още един момент, който трябва да разберем – това са идентификаторите на транзакции. Много транзакции не могат да работят без уникални идентификатори. И тук имаме обяснение какво е транзакция. В Postgres има две системи за нумерация на транзакции. Знам, че това не е много красиво решение.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

http://momjian.us/main/writings/pgsql/locking.sql

Гледаме. С червен цвят е подчертат номерът на транзакцията. Тук е показана функцията SELECT pg_back. Тя връща моята транзакция и ID на тази транзакция.

Още един момент, ако ви харесва тази презентация и искате да я стартирате в вашата база данни, можете да преминете по тази розова връзка и да изтеглите SQL за тази презентация. Можете просто да я стартирате в вашия PSQL и цялата презентация веднага ще се появи на екрана ви. Тя няма да съдържа цветове, но поне ще можем да я видим.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

В този случай виждаме ID на транзакцията. Това е номерът, който сме му присвоили. И има още един тип ID на транзакция в Postgres, наречен виртуален ID на транзакция.

И трябва да разберем това. Много е важно, иначе няма да можем да разберем блокировката в Postgres.

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

И това увеличава производителността на Postgres, подобрява възможностите за почистване, затова виртуалният ID на транзакцията се състои от две числа. Първото число пред слеша е ID на бекенда. А вдясно просто виждаме брояч.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Затова, ако стартирам заявка, той казва, че ID на бекенда е 2.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Ако стартирам серия от такива транзакции, виждаме, че броячът се увеличава всеки път, когато стартирам заявка. Например, когато стартирам заявка 2/10, 2/11, 2/12 и т.н.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Имайте предвид, че тук има две колони. Вляво виждаме виртуалния ID на транзакцията – 2/12. А вдясно имаме постоянен ID на транзакцията. И това поле е празно. И тази транзакция не модифицира базата данни. Затова не й присвоявам постоянен ID на транзакцията.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Веднага щом стартирам командата за анализ (ANALYZE), същата заявка ми издава постоянен ID на транзакцията. Вижте как се е променило. Преди нямах този ID, сега се появи.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И така, тук имаме друга заявка, друга транзакция. Виртуалният номер на транзакцията е 2/13. И ако поискам постоянен ID на транзакцията, при стартиране на заявката, ще го получа.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Така че, отново. Имаме виртуален ID на транзакцията и постоянен ID на транзакцията. Просто разберете този момент, за да разберете поведението на Postgres.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

За да създадем заявка и да видим какво се случва в Postgres, трябва да издадем заявка в системния изглед. В този случай в червено е подчертан pg_lock. Pg_lock е системна таблица, която ни показва какви блокировки в момента се използват в Postgres.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Още един проблем е, че този изглед е много широк, затова ми се налага да създам втори – lockview2.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Така че, един ред, една колона. Първият тип блокировка се нарича ACCESS SHARE. Това е най-малко рестриктивната блокировка. Това означава, че почти не влиза в конфликти с другите блокировки.

Ако искаме експлицитно да определим блокировката, изпълняваме командата «lock table». Това явно ще блокира, т.е. в режим ACCESS SHARE изпълняваме lock table. И ако стартирам PSQL във фонов режим, така стартирам втора сесия от моята първа сесия. Какво ще направя тук? Преминавам към друга сесия и й казвам «покажи ми lockview за тази заявка». И тук имам AccessShareLock в тази таблица. Това е точно това, което поисках. И той казва, че блокировката е присвоена. Много просто.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Следващото, ако погледнем втората колона, там няма нищо. Те са празни.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Ако изпълня командата «SELECT», това е имплицитен (явен) начин за заявяване на AccessShareLock. Затова освобождавам таблицата и изпълнявам запитването, и запитването връща няколко реда. В един от редовете виждаме AccessShareLock. Така SELECT предизвиква AccessShareLock в таблицата. И той не конфликтира практически с нищо, защото е блокировка с ниско ниво.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Какво ще стане, ако изпълня SELECT и имам три различни таблици? Преди изпълнявах само една таблица, сега изпълнявам три: pg_class, pg_namespace и pg_attribute.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И сега, когато погледна запитването, виждам 9 AccessShareLocks в трите таблици. Защо? Синьо е оцветено трите таблици: pg_attribute, pg_class, pg_namespace. Но можете също да видите, че всички индекси, които са определени през тези таблици, също имат AccessShareLock.

И това е блокировка, която практически не конфликтува с другите. Всичко, което прави, е просто да не ни позволи да изтриваме таблицата, докато я избираме. Има смисъл. Т.е. ако избираме таблицата, тя в този момент изчезва, което е неправилно, затова AccessShare – това е блокировка с ниско ниво, която ни казва «не изтривайте тази таблица, докато работя». Всъщност, това е всичко, което прави.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

ROW SHARE – това е блокировка, която е малко различна.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Да вземем пример. SELECT ROW SHARE е начин на блокиране на всеки ред поотделно. Така че никой не може да ги изтрива или променя, докато ги гледаме.

Откючване на мениджъра за заключвания на Postgres. Брус МомжианТака, какво прави SHARE LOCK? Виждаме, че ID на транзакцията е 681 за SELECT. И това е интересно. Какво се е случило тук? Първият път виждаме номер в полето "Lock". Вземаме ID на транзакцията и той казва, че я блокира в ексклузивен режим. Всичко, което прави, е, че казва, че имам ред, който технически е блокиран някъде в таблицата. Но не казва къде точно. По-късно ще разгледаме това по-подробно.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Тук казваме, че блокировката се използва от нас.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

SHARE EXCLUSIVE – това е по-дълга блокировка.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Това (ANALYZE) е команда на анализатора, която ще се използва.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

SHARE LOCK – можете изрично да блокирате в режим share.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

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

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

SHARE ROW EXCLUSIVE – отново може да бъде зададена изрично (ясно).

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

EXCLUSIVE блокировка означава, че никой друг не може да променя таблицата.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Тук виждаме различни типове блокировки.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

ACCESS EXCLUSIVE, например, това е команда за блокировка. Например, ако правите CLUSTER table, тогава това ще означава, че никой не може да записва там. И тя блокира не само самата таблица, но и индекси също.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Това е втората страница на блокировката ACCESS EXCLUSIVE, където виждаме конкретно какво блокира в таблицата. Тя блокира отделни редове на таблицата, което е доста интересно.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Нека да разгледаме конкретни примери.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

А какво ще се случи, ако вмъкна още два реда? И сега в нашата таблица има три реда. Вмъкнах един ред и получих това на изхода. И ако вмъкна още два реда, какво е странното тук? Има странност, защото добавих три реда в тази таблица, но все още имам два реда в таблицата за блокировки. И това по същество е основополагающо поведение на Postgres.

Много хора мислят, че ако в блокирането на базата данни блокирате 100 реда, то ще ви е необходимо създаването на 100 записи за блокировка. Ако блокирам веднага 1 000 реда, то тогава ще ми трябват 1 000 такива заявки. И ако трябва да блокира милион или милиард. Но ако правим така, няма да работи много добре. Ако сте използвали система, която създава записи за блокировка за всеки отделен ред, виждате, че това е сложно. Защото трябва да определите веднага табличка за блокировка, която може да се пренасити, но Postgres не прави така.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

А какво ще се случи, ако искам да обновя два реда? И виждаме, че той се държи по същия начин. Правим два пъти повече обновления, но точно същото количество блокировки на редове.

Ако ви интересува как Postgres го прави, трябва да слушате моите лекции за MVCC, за да разберете как Postgres вътрешно маркира редовете, които променя. И Postgres има начин, по който го прави, но не го прави на ниво блокиране на таблици, а го прави на по-ниско и по-ефикасно ниво.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

А какво ще стане, ако искам да изтрия нещо? Ако изтрия, например, един ред и все още имам моите две входни блокировки, дори и да искам да изтрия всичките им, те все още ще присъстват.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

А какво ще кажете за експлицитната блокировка?

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Ако натисна "обнови", тогава имам две блокирани реда. И ако ги маркирам всички и натисна "обнови навсякъде", все пак оставам с две записи за блокировка.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И същото важи, ако правим shared, можем да правим за всички 30 пъти.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Още един вид поведение, което виждате в Postgres, е много добре известно и желано поведение – това е, че можете да извършвате update или select. И можете да го правите едновременно. И select не блокира update и също така обратното. Казваме на четящия да не блокира пишещия, а пишещият да не блокира четящия.

Ще ви покажа пример за това. Сега ще направя избор. После ще направим INSERT. И след това ще можете да видите – 694. Ще можете да видите ID на транзакцията, която е извършила това вмъкване. И това е как работи.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И ако сега погледна моя бекенд ID, то стана – 695.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И мога да видя, че 695 се появява в моята таблица.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

И можете да забележите, че отгоре – това е ShareLock, а отдолу – ExclusiveLock. И двете транзакции са се получили.

И трябва да чуете моето представяне в MVCC, за да разберете как това става. Но това е илюстрация на това, че можете да правите това едновременно, т.е. едновременно да извършвате SELECT и UPDATE.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Нека нулираме и отново да направим една операция.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И за да илюстрирам това, ще разгледам таблицата Lockdemo. И ще погледнем един ред. На транзакция 698.

Обновихме го до 2. 699 – това е първото обновление. И то премина успешно или е в чакаща транзакция и очаква, когато ние потвърдим или отменим.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Но погледнете нещо друго – 2/51 – това е нашата първа транзакция, нашата първа сесия. 3/112 – това е втората заявка, която се появи отгоре и която промени тази стойност на 3. И ако забележите, горният блокира сам себе си, който е 699. Но 3/112 не предостави блокировка. В колоната Lock_mode пише, че очаква. Той очаква 699. И ако погледнете, къде е 699, той е по-горе. И какво направи първата сесия? Тя създаде ексклузивна блокировка на собствения си транзакционен ID. Това е начина, по който Postgres го прави. Той блокира собствения транзакционен ID. И ако искате да чакате, докато някой потвърди или отмени, трябва да чакате, докато има изчакваща транзакция. И затова можем да видим странен ред.

Да погледнем отново. Отляво виждаме нашето процесингово ID. Във втората колона виждаме нашето виртуално ID на транзакцията, а в третата виждаме lock_type. Какво означава това? По същество, тя казва, че блокира транзакционния ID. Но забележете, че във всички редове по-долу пише relation. И затова имате два вида блокировка в таблицата. Има блокировка relation. А също така има блокировка transactionid, където блокирате сами, това е точно това, което се случва в първия ред или в самия низ, където transationid, където чакаме 699 да завърши операцията си.

Гледам какво се получава тук. И тук едновременно се случват две неща. Вие гледате на блокировката по транзакционен ID в първия ред, която блокира сама себе си. И тя блокира сама себе си, за да накара хората да чакат.

Ако погледнете 6-ия ред, то това е същият запис, като първия. И затова транзакция 699 се блокира. 700 също се самоблокира. И после в долния ред ще видите, че чакаме 699 да завърши операцията си.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И в lock_type, tuple виждате числа.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Можете да видите, че това е 0/10. И това е номерът на страницата, и също offset на този конкретен ред.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И виждате, че става 0/11, когато обновяваме.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Но всъщност – това е 0/10, защото се случва чакане на тази операция. Имаме възможност да видим, че това е редът, който чакам, за да бъде потвърден.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

След като потвърдим и натиснем commit, и когато актуализацията приключи, получаваме следното. Транзакция 700 е единствената блокировка, която не чака никого, защото е закомитвана. Тя просто чака да приключи транзакцията. След като 699 завърши, вече не чакаме нищо. И сега транзакцията 700 казва, че всичко е наред, че има всички необходими блокировки във всички разрешени таблици.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Какво ще стане, ако направим три едновременни актуализации и кажем, че редът в момента е 3. И ние променим 3 на 4.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И ето ни виждаме 4. И транзакционният ID 702.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

След това аз променям 4 на 5. А 5 на 6, а 6 на 7. И опашвам редица хора, които ще чакат да приключи тази една транзакция.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И всичко става ясно. Какъв е първият ред? Това е 702. Това е транзакционният ID, който първоначално зададе това значение. А какво имам написано в колоната Granted? Имам отметки f. Това са моите актуализации, които (5, 6, 7) не могат да бъдат одобрени, защото чакаме да завърши транзакционният ID 702. Там имаме блокировка на транзакционния ID. И в момента имаме 5 транзакционни блокировки ID.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Така изглежда. Ясно е, че всички чакат 12-ия ред.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Това е, което видяхме тук. Ето 0/12.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Това, което се случва. 702 записва. И сега 703 получава блокировката на реда, а после 704 започва да чака, докато 703 запише. И 705 също го очаква. И когато всичко приключи, те самите се освобождават. Искам да подчертая, че всички се подреждат в опашка. Много е подобно на ситуацията с задръстването, когато всички чакат първата кола. Първата кола спира и всички се подреждат в дълга линия. След това тя се движи, следващата кола може да премине напред и да получи своята блокировка и т.н.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Например, ако Иван каже: „Дай ми нещо“, а аз отговоря: „Не, ще ти дам това само ако ми дадеш нещо друго“. А той казва: „Не, няма да ти дам, ако не ми дадеш“. И попадаме в ситуация на мъртва блокировка. Сигурен съм, че Иван няма да направи така, но разбирате смисъла, двама души искат нещо, но не са готови да го дадат, докато другият не им предостави това, което желаят. И тук няма решение.

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Сега ще предизвикаме две deadlocks. Ще използваме 50 и 80. В първия ред ще направя актуализация от 50 на 50. Ще получа номер на транзакцията 710.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

След това ще сменя 80 на 81, а 50 на 51.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Там дори е написано на кой точно ред имаме deadlocks. И тук започва да става странно.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Сега актуализираме 80 на 80.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И това просто ще започне да забавя всичко. А ние не го искаме.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И в Postgres има начини да се забележи, когато това се случва. И когато това стане, ще получите следната грешка. И от това е ясно, че такъв процес чака SHARE LOCK от друг процес, т.е. който е блокиран от процес 711. А този процес е чакал да бъде предоставен SHARE LOCK на определен идентификатор на транзакцията и е бил блокиран от конкретен процес. Следователно, тук имаме ситуация на мъртва блокировка.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

А има ли тройни мъртви блокировки? Възможно ли е това? Да.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Променяме 60 на 61, 80 на 81.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

А след това променяме 80, а след това – бум!

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И 714 сега чака 715. 716-ти чака 715-ти. И с това вече нищо не може да се направи.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Следващият проблем – това е сериализуемост.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Ако има специална сериализуема блокировка.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И се връщаме към 719. Той има напълно нормален изход.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И можете да натиснете, за да направите транзакция от сериализуема.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И разбирате, че сега имате друг вид блокировка SA – това означава сериализуемо.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И също така можете да въвеждате уникални индекси.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

В тази таблица имаме уникални индекси.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Ето ни 723.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Ако запазим точка и след това я актуализираме, получаваме нов транзакционен ID. Това е още един аспект на поведението, който е важно да знаете. Ако го върнем, транзакционният ID ще изчезне. 724 ще изчезне. Но сега получаваме 725.

Какво се опитвам да направя тук? Опитвам се да ви покажа примери за необичайни блокировки, които можете да срещнете: независимо дали става въпрос за блокировки с serializable или SAVEPOINT - това са различни видове блокировки, които ще се появят в таблицата с блокировки.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Това е създаването на експлицитни (явни) блокировки, при които имаме pg_advisory_lock.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Виждате, че типът на блокировката тук е посочен като advisory. И тук червено е написано «advisory». Можете едновременно да блокирате с pg_advisory_unlock.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Искам да завърша, като ви покажа още нещо удивително. Ще създам още един вид. Но ще свържа таблицата pg_locks с таблицата pg_stat_activity. Защо искам да направя това? Защото това ми позволява да видя всички текущи сесии и да видя какви точно блокировки очакват. Това е доста интересно, когато комбинираме таблицата с блокировки и таблицата с заявки.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Тук създаваме pg_stat_view.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

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

Още една функция, която е много полезна – е pg_blocking_pidsВероятно, вы никогда о ней не слышали. Что она делает? Она позволяет нам указать, какие именно ID процессов ожидаются для сессии 11740. Вы можете видеть, что 11740 ожидает 724, и 724 находится на самом верху. А 11306 — это ваш ID процесса. По сути, эта функция просматривает вашу таблицу блокировок. Я понимаю, что это немного сложно, но вам удается это понять. Эта функция проходит через таблицу блокировок и пытается найти ваш процесс ID, учитывая блокировки, которые она ожидает. Она также пытается вычислить, какой именно процесс ID у процесса, ожидающего блокировку. Поэтому вы можете запустить эту функцию. pg_blocking_pids.

Это действительно полезно. Мы добавили эту функцию только с версии 9.6, поэтому ей всего 5 лет, но она очень и очень полезна. То же самое касается второго запроса. Он показывает именно то, что нам нужно увидеть.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Это то, о чем я хотел поговорить с вами. Как я и ожидал, мы исчерпали все наше время, поскольку было так много слайдов. Слайды доступны для скачивания. Я хотел бы поблагодарить вас за то, что вы были здесь. Я уверен, вам понравится оставшаяся часть конференции, большое спасибо!

Вопроси:

Например, если я пытаюсь обновить строки, а другая сессия пытается удалить всю таблицу. Насколько я понимаю, в этом случае должен быть какой-то intent lock. Есть ли он в Postgres?

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

Вернемся к началу. Возможно, вы помните, что когда вы выполняете что-либо, например, делаете SELECT, мы выдаем AccessShareLock. Это предотвращает удаление таблицы. Поэтому если вы хотите обновить строку таблицы или удалить строку, кто-то не сможет удалить всю таблицу одновременно с этим, потому что вы удерживаете AccessShareLock над всей таблицей и строкой. Как только вы закончили, они могут удалить. Но пока вы что-то меняете, они не смогут это сделать.

Давайте повторим. Рассмотрим пример с удалением. Вы видите, что на строке есть эксклюзивный lock над всей таблицей.

Это будет выглядеть как exclusive lock, правильно?

Да, това изглежда така. Разбирам какво казвате. Казвате, че ако направя SELECT, ще имам ShareExclusive, а след това го превеждам в Row Exclusive, дали това ще създаде проблем? Но учудващо, не създава проблем. Изглежда като увеличаване на степента на блокиране, но по същество имам lock, който предотвратява изтриването. И сега, когато правя този lock по-силен, той все още предотвратява изтриването. Така че не е така, че аз се повишавам. Т.е. той предотвратява това и когато е на по-ниско ниво, така че когато увеличавам нивото му, той все още предотвратява изтриването на таблицата.

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

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

Postgres автоматично забелязва ситуации на deadlock. И автоматично ще изтрива една от сесиите. Единственото решение, което може да помогне да избегнете ситуация с мъртви блокировки, е да блокирате хората в една и съща последователност. Така че, когато погледнете приложението си, често причината за deadlocks... Нека си представим, че искам да блокирам две различни неща. Едно приложение блокира таблица 1, а друго приложение блокира таблица 2, а след това таблица 1. Най-простият начин да избегнете deadlocks е да прегледате приложението си и да се опитате да се уверите, че блокировката става в една и съща последователност във всички приложения. И това обикновено отстранява 80% от проблемите, защото много различни хора пишат тези приложения. И ако ги блокирате в една и съща последователност, не срещате ситуация на deadlock.

Благодаря ви много за вашето представяне! Говорехте за vacuum full и, ако разбирам правилно, vacuum full деформира реда на записите в отделното хранилище, така че текущите записи остават непроменени. А защо vacuum full взема достъп до ексклузивна блокировка и защо конфликтира с операциите по запис?

Това е добър въпрос. Причината е, че vacuum full взима таблицата. Всъщност създаваме нова версия на таблицата. И таблицата ще бъде нова. Така се получава, че това ще бъде напълно нова версия на таблицата. Проблемът е, че когато го правим, не искаме хората да я четат, защото трябва да видят новата таблица. И затова това се съчетава с предишния въпрос. Ако можехме да четем едновременно, не бихме могли да я преместим и да насочим хората към новата таблица. Щеше да се наложи да изчакаме, за да завършат всички с четенето на тази таблица, и затова всъщност това е ситуация на lock exclusive.
Просто казваме, че блокираме от самото начало, защото знаем, че в края ще ни трябва эксклузивна блокировка, за да преместим всички на новата копия. Така че потенциално можем да разрешим това. И правим това с едновременно индексиране. Но това е много по-сложно за изпълнение. И много от това се отнася до предишния ви въпрос за lock exclusive.

Възможно ли е да добавим locking timeout в Postgres? В Oracle мога, например, да напиша „избери за обновление“ и да изчакам 50 секунди преди обновлението. Това беше добро за приложението. Но в Postgres или трябва да го направя веднага и изобщо да нечакам, или да изчакам до някакво време.

Да, можете да изберете таймаут за вашите блокировки. Можете също така да издавате команда no way, която ще бъде ..., ако не можете веднага да получите блокировка. Така че или lock timeout, или нещо друго, което да ви позволи да го направите. Това не се прави на синтактично ниво. Прави се като променлива на сървъра. Понякога не може да се използва.

Можете ли да отворите 75-ти слайд?

Да.

Откючване на мениджъра за заключвания на Postgres. Брус Момжиан

И моят въпрос е следният. Защо двата обновителни процеса чакат 703?

И това е чудесен въпрос. Всъщност не разбирам защо Postgres го прави. Но когато 703 е създаден, той е очаквал 702. И когато се появят 704 и 705, изглежда, че не знаят какво чакат, защото там все още няма нищо. И Postgres го прави така: когато не можете да получите заключване, той пише "Какъв е смисълът да ви обработвам?", защото вие и така чакате някого. Затова просто го оставяме да виси във въздуха, той изобщо не актуализира това. Но какво се случи тук? Веднага щом 702 завърши процеса и 703 получи заключването си, системата се върна обратно. И каза, че сега имаме двама, които чакат. А след това нека да ги актуализираме заедно. И да посочим, че и двамата чакат.

Не знам защо Postgres го прави. Но има проблем, който се нарича f…. Мисля, че това не е термин на български. Това е, когато всички чакат едно заключване, дори когато има 20 инстанции, които чакат за заключване. И изведнъж всички те се събуждат едновременно. И всички започват да се опитват да реагират. Но системата прави така, че всички чакат 703. Защото те всички чакат и ние веднага ги подреждаме на опашка. И ако се появи всяко ново искане, което е било генерирано след това, например 707, то отново ще има празнота.

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

Винаги съм гледал на това като на доста странен феномен. Защото, например, тук изобщо не ги изброяваме. Но ми се струва, че всеки път, когато даваме ново заключване, гледаме на всички, които са в чакателния процес. Тогава всички те се подреждат на опашка. А след това, всеки нов, който идва, попада на опашка само когато следващият човек завърши обработката. Много добър въпрос. Благодаря ви много за въпроса!

Мисля, че е много по-логично, когато 705 очаква 704.

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

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

Има статии за locks на Егора Рогова. Вижте, те също са интересни и полезни. Темата, разбира се, е ужасно сложна. Благодаря много, Брюс!

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

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster