Unlocking Postgres Lock Manager. Брюс Момжіан

Розшифровка доповіді 2020 Брюса Момжіана "Unlocking the Postgres Lock Manager".

Unlocking Postgres Lock Manager. Брюс Момжіан

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

Вітання! Чудово знову бути тут у Росії. Я перепрошую, що я не зміг приїхати торік, але цього року в Івана і в мене великі плани. Я сподіваюся, що буду тут набагато частіше. Я обожнюю приїжджати до Росії. Я відвідуватиму Тюмень, Твер. Я дуже радий, що мені пощастить побувати в цих містах.

Мене звуть Брюс Момжіан. Я працюю в EnterpriseDB і працюю з Postgres понад 23 роки. Я живу у Філадельфії, США. Подорожую приблизно 90 днів на рік. І відвідую близько 40 конференцій. Мій веб сайт, який містить слайди, які я вам зараз показуватиму. Тому після конференції ви можете з мого особистого сайту завантажити їх. Там також міститься близько 30 презентацій. А також є відео та велика кількість записів у блозі, понад 500. Це досить змістовний ресурс. І якщо вам цікавий цей матеріал, я вас запрошую ним скористатися.

Я раніше був викладачем, професором, перш ніж почав працювати з Postgres. І я дуже радий, що мені вдасться розповісти вам те, що я збираюся вам розповісти. Це одна із найцікавіших моїх презентацій. І ця презентація містить 110 слайдів. Говорити ми почнемо з простих речей, а до кінця доповідь стане все складнішою і складнішою, і стане досить складною.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це досить неприємна розмова. Блокування – це найпопулярніший предмет. Ми хочемо, щоб це кудись зникло. Це як ходити до стоматолога.

Unlocking Postgres Lock Manager. Брюс Момжіан

  1. Блокування є проблемою для великої кількості людей, які працюють у базах даних та у яких працюють одночасно кілька процесів. Їм необхідне блокування. Т. е. сьогодні я вам дам базові знання з блокування.
  2. Ідентифікатори транзакцій. Це досить нудна частина презентації, але їх потрібно зрозуміти.
  3. Далі ми поговоримо про типи блокування. Це досить механічна частина.
  4. І далі ми наведемо деякі приклади блокувань. І це буде досить складно для сприйняття.

Unlocking Postgres Lock Manager. Брюс Момжіан

Давайте поговоримо про блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

Термінологія у нас досить складна. Скільки знають, звідки цей уривок? Дві людини. Це з гри, яка називається «Колосальна пригода в печері». Це була текстова комп'ютерна гра у 80-х роках, мені здається. Там треба було зайти в печеру, в лабіринт і текст змінювався, але при цьому зміст був приблизно однаковий щоразу. Отак я пам'ятаю цю гру.

Unlocking Postgres Lock Manager. Брюс Момжіан

І тут ми бачимо найменування блокувань, які прийшли до нас із Oracle. Ми використовуємо їх.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тут ми бачимо терміни, які мене бентежать. Наприклад, SHARE UPDATE ECXLUSIVE. Далі SHARE RAW ECXLUSIVE. Щиро кажучи, ці назви не дуже зрозумілі. Ми намагатимемося їх детальніше розглянути. Деякі містять слово «share», яке означає – відокремитися. Деякі містять слово "exclusive" - ​​ексклюзивний. Деякі містять обидва ці слова. Я хотів би почати з того, як ці блокування працюють.

Unlocking Postgres Lock Manager. Брюс Момжіан

І також дуже важливим є слово «доступ» — access. І слова "row" - рядок. Т. е. розподіл доступу, розподіл рядків.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ще одна проблема, яку необхідно зрозуміти у Postgres, я, на жаль, не зможу розповісти про неї у своєму виступі, це MVCC. У мене є окрема презентація на цю тему на моєму веб-сайті. І якщо ви думаєте, що ця презентація складна, то MVCC – це, мабуть, найскладніша. І якщо вам цікаво, можете подивитися її на сайті. Подивитися можете відео.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ще один момент, який нам необхідно зрозуміти, – це ідентифікатори транзакції. Багато транзакцій не можуть працювати без унікальних ідентифікаторів. І тут ми пояснюємо те, що таке транзакція. У Postgres є дві системи нумерації транзакцій. Я знаю, це не дуже гарне рішення.

Unlocking Postgres Lock Manager. Брюс Момжіан

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

Unlocking Postgres Lock Manager. Брюс Момжіан

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

Дивимося. Червоним кольором виділено номер транзакції. Тут показано функцію SELECT pg_back. Вона повертає мою транзакцію та ID цієї транзакції.

Ще один момент, якщо вам подобається ця презентація і ви хочете запустити її у своїй базі даних, то ви можете пройти по цьому посиланню, виділеному рожевим кольором, і завантажити SQL для цієї презентації. І можете просто запустити її у вашому PSQL і вся презентація виявиться у вас на екрані негайно. Вона не міститиме квітів, але принаймні ми зможете її побачити.

Unlocking Postgres Lock Manager. Брюс Момжіан

У разі ми бачимо ID транзакції. Це номер, який ми їй надали. І є ще один тип ID транзакції в Postgres, який називається віртуальний ID транзакція

І ми маємо зрозуміти це. Це дуже важливо, інакше ми не зможемо зрозуміти блокування у Postgres.

Віртуальний ID транзакція – це ID транзакції, яка містить постійних значень. Наприклад, якщо я запускаю команду SELECT, то я, швидше за все, не змінюватиму базу даних, я нічого не блокуватиму. Тому коли ми запускаємо простий SELECT, ми не даємо цієї транзакції постійний ID. Ми там даємо їй лише віртуальний ID.

І це підвищує продуктивність Postgres, покращує можливості для очищення, тому віртуальний ID транзакції складається із двох чисел. Перше число перед слешем – це ID бекенда. А праворуч ми бачимо просто лічильник.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тому якщо запускаю запит, він каже, що ID бекенд – 2.

Unlocking Postgres Lock Manager. Брюс Момжіан

А якщо я запускаю серію таких транзакцій, то бачимо, що лічильник щоразу збільшується, коли я запускаю запит. Наприклад, коли я запускаю запит 2/10, 2/11, 2/12 і т.д.

Unlocking Postgres Lock Manager. Брюс Момжіан

Майте на увазі, що тут є дві колонки. Зліва ми бачимо віртуальний ID транзакції – 2/12. А праворуч у нас є постійний ID транзакції. І це поле пусте. І ця транзакція не модифікує бази даних. Тому я не надаю їй постійного ID транзакції.

Unlocking Postgres Lock Manager. Брюс Момжіан

Як тільки я запускаю команду аналізувати ( (ANALYZE)), той самий запит видає мені постійний ID транзакції. Подивіться, як це змінилося. Раніше я не мав цього ID, тепер з'явився.

Unlocking Postgres Lock Manager. Брюс Момжіан

Отже, тут є ще один запит, ще одна транзакція. Віртуальний номер транзакції – 2/13. І якщо я попрошу постійний ID транзакції, то коли я запущу запит, я його отримаю.

Unlocking Postgres Lock Manager. Брюс Момжіан

Отже, ще раз. У нас є віртуальний ID транзакції та постійний ID транзакції. Просто зрозумійте цей момент, щоб зрозуміти поведінку Postgres.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ми переходимо до третього розділу. Тут ми просто пройдемо через різні типи блокувань у Postgres. Це дуже цікаво. Останній розділ буде набагато цікавішим. Але ми маємо розглянути базові речі, бо інакше ми не зрозуміємо того, що буде далі.

Ми пройдемо через цей розділ, подивимося на кожен тип блокувань. І я вам покажу приклади, як вони встановлюються, як вони працюють, покажу вам деякі запити, які можна використовувати, щоб переглянути, як працює блокування в Postgres.

Unlocking Postgres Lock Manager. Брюс Момжіан

Щоб створити запит та подивитися, що відбувається в Postgres, нам потрібно випустити запит у system view. У цьому випадку червоним кольором у нас виділено pg_lock. Pg_lock – це системна таблиця, яка говорить нам, які блокування зараз використовуються у Postgres.

Тим не менш, мені дуже складно показати вам pg_lock сам по собі, тому що це досить складно. Тому я створив перегляд, який показує pg_locks. І він також виконує для мене деяку роботу, яка дозволяє мені краще зрозуміти. Т. е. він виключає мої блокування, мою власну сесію і т. д. Це просто стандартний SQL і він дозволяє краще показати, що відбувається.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ще одна проблема в тому, що цей view дуже широкий, тому мені доводиться створити другий lockview2.

Unlocking Postgres Lock Manager. Брюс Момжіан І він показує мені ще стовпчики з таблиці. І ще один, який показує мені решту колонок. Це досить складно, тому я постарався уявити це якомога простіше.

Unlocking Postgres Lock Manager. Брюс Момжіан

Отже, ми створили таблицю, яка називається Lockdemo. І ми створили там один рядок. Це наша зразкова таблиця. І ми створюватимемо розділи, щоб просто показати вам приклади блокувань.

Unlocking Postgres Lock Manager. Брюс Момжіан

Отже, один рядок, один стовпчик. Перший тип блокування називається ACCESS SHARE. Це найменше забороняюче блокування. Це означає, що вона практично не конфліктує з іншими блокуваннями.

І якщо хочемо експліцитно визначити блокування, ми запускаємо команду «lock table». І вона явно заблокує, тобто у режимі ACCESS SHARE ми запускаємо lock table. І якщо я запущу PSQL у фоновому режимі, я запускаю таким чином другу сесію з моєї першої сесії. Т. е. що я зроблю тут? Я переходжу до іншої сесії та кажу їй «покажи мені lockview для цього запиту». І тут у мене AccessShareLock у цій таблиці. Це саме те, що я просив. І він каже, що блокування було присвоєно. Дуже просто.

Unlocking Postgres Lock Manager. Брюс Момжіан

Далі, якщо ми дивимось у другу колонку, то там нічого немає. Вони порожні.

Unlocking Postgres Lock Manager. Брюс Момжіан

І якщо я запускаю команду SELECT, то це імпліцитний (явний) спосіб запросити AccessShareLock. Тому я випускаю свою таблицю та запускаю запит, і запит повертає кілька рядків. І в одному з рядків ми бачимо AccessShareLock. Таким чином, SELECT викликає AccessShareLock в таблиці. І він не конфліктує практично ні з чим, бо це є блокування низького рівня.

Unlocking Postgres Lock Manager. Брюс Момжіан

Що якщо я запущу SELECT і маю три різні таблиці? Раніше я запускав лише одну таблицю, тепер я запускаю три: pg_class, pg_namespace та pg_attribute.

Unlocking Postgres Lock Manager. Брюс Момжіан

І тепер, коли я дивлюся на запит, я бачу 9 AccessShareLocks у трьох таблицях. Чому? Синім кольором виділено три таблиці: pg_attribute, pg_class, pg_namespace. Але ви також можете бачити, що всі індекси, які визначені через ці таблиці також мають AccessShareLock.

І це блокування, яке практично не конфліктує з іншими. А все, що вона робить, це просто не дає нам скинути таблицю, доки ми її вибираємо. Це має сенс. Т. е. якщо ми вибираємо таблицю, вона в цей момент зникає, то це неправильно, тому AccessShare – це блокування низького рівня, яке говорить нам "не видаляйте цю таблицю, доки я працюю". По суті це все, що вона робить.

Unlocking Postgres Lock Manager. Брюс Момжіан

ROW SHARE – це блокування трохи відрізняється.

Unlocking Postgres Lock Manager. Брюс Момжіан

Візьмемо приклад. SELECT ROW SHARE спосіб блокування кожного рядка окремо. Таким чином, ніхто не може видалити їх або змінити їх, поки ми їх дивимося.

Unlocking Postgres Lock Manager. Брюс МомжіанОтже, що робить SHARE LOCK? Ми бачимо, що ID транзакції 681 для SELECT'а. І це цікаво. Що тут у нас сталося? Перший раз ми бачимо номер у полі Lock. Ми беремо ID транзакції і він каже, що блокує її в ексклюзивному режимі. Все, що він робить, він каже, що я маю рядок, який технічно заблокований десь у таблиці. Але не каже, де конкретно. Трохи згодом ми докладніше це розглянемо.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тут говоримо, що блокування використовується нами.

Unlocking Postgres Lock Manager. Брюс Момжіан

Отже, ексклюзивне блокування експліцитно(явно) каже, що воно ексклюзивне. І якщо ви видаляєте рядок у цій таблиці, це і станеться, як ви можете бачити.

Unlocking Postgres Lock Manager. Брюс Момжіан

SHARE EXCLUSIVE – це довше блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це (ANALYZE) команда аналізатора, яка використовуватиметься.

Unlocking Postgres Lock Manager. Брюс Момжіан

SHARE LOCK – ви можете заблокувати експліцитно в режимі share.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ви також можете створити унікальний індекс. І там ви можете побачити SHARE LOCK, який є їхньою частиною. І він блокує таблицю та встановлює на неї блокування SHARE LOCK.

За замовчуванням SHARE LOCK на таблиці означає, що інші люди можуть читати таблицю, але не може її модифікувати. І саме це відбувається, коли ви створюєте унікальний індекс.

Якщо я створюю унікальний concurrently індекс, то у мене буде інший тип блокування, тому що, як ви пам'ятаєте, використання concurrently індексів знижує вимогу до блокування. І якщо я використовую нормальне блокування, нормальний індекс, то я таким чином запобігу запису в індекс таблиці під час його створення. Якщо я використовую поточний індекс, то мені потрібно використовувати інший тип блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

SHARE ROW EXCLUSIVE – знову можна задати експліцитно (явно).

Unlocking Postgres Lock Manager. Брюс Момжіан

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

Unlocking Postgres Lock Manager. Брюс Момжіан

EXCLUSIVE блокування означає, що ніхто інший змінювати таблицю не зможе.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тут бачимо різні типи блокувань.

Unlocking Postgres Lock Manager. Брюс Момжіан

ACCESS EXCLUSIVE, наприклад, команда блокування. Наприклад, якщо ви робите CLUSTER table, то це означатиме, що ніхто не зможе записувати туди. І вона блокує не тільки саму таблицю, а й індекси також.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це друга сторінка блокування ACCESS EXCLUSIVE, де бачимо конкретно, що вона блокує у таблиці. Вона блокує окремі рядки таблиці, що досить цікаво.

Це вся базова інформація, яку я хотів надати. Ми говорили про блокування, про ID транзакцій, говорили про віртуальні ID транзакції, про постійні ID транзакції.

Unlocking Postgres Lock Manager. Брюс Момжіан

І тепер ми пройдемося за прикладами блокувань. Це найцікавіша частина. Ми подивимося дуже цікаві випадки. І моє завдання у цій презентації – дати вам найкраще уявлення про те, що Postgres насправді робить, коли він намагається блокувати ті чи інші речі. Мені здається, що він добре вміє блокувати окремі частини.

Давайте розглянемо певні приклади.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ми почнемо з таблиць та з одного рядка в таблиці. Коли я вставляю щось, у мене відображається ExclusiveLock, ID транзакції та ExclusiveLock на таблиці.

Unlocking Postgres Lock Manager. Брюс Момжіан

А що буде, якщо я вставлю ще два ряди? І тепер у нашій таблиці три ряди. І я вставив один ряд і одержав ось це на виході. І якщо я вставляю ще два ряди, що тут дивного? Тут є дивина, тому що я додав три ряди до цієї таблиці, але я все ще два ряди в таблиці блокування. І це, по суті, основна поведінка Postgres.

Багато хто думаю, що якщо в базі даних, ви блокуєте 100 рядів, то вам буде потрібно створити 100 вводів блокувань. Якщо я блокуватиму відразу 1 000 рядів, то тоді мені потрібна буде 1 000 таких запитів. І якщо мені потрібно мільйон чи мільярд заблокувати. Але якщо ми так робитимемо, то це буде не дуже добре працювати. Якщо ви використовували систему, яка створює введення блокування для кожного окремого ряду, ви бачите, що це складно. Тому що вам потрібно визначити відразу таблицю блокування, яка може переповнитись, але Postgres так не робить.

І на цьому слайді дуже важливо, що тут очевидно демонструється, що є ще одна система, яка працює всередині MVCC, яка блокує окремі рядки. Тому коли ви блокуєте мільярди рядів, то Postgres не створює мільярд окремих команд на блокування. І це дуже добре позначається на продуктивності.

Unlocking Postgres Lock Manager. Брюс Момжіан

А як щодо оновлення? Я зараз оновлюю ряд, і ви можете помітити, що він одразу виконав дві різні операції. Він одночасно заблокував таблицю, але також заблокував індекс. І йому потрібно було блокувати індекс, бо є унікальні обмеження на цій таблиці. І ми хочемо переконатись, що ніхто його не змінює, тому ми його блокуємо.

Unlocking Postgres Lock Manager. Брюс Момжіан

А що буде, якщо я хочу оновити два ряди? І ми бачимо, що він поводиться також. Ми проводимо вдвічі більше оновлень, але така сама кількість рядків блокування.

Якщо вам цікаво, як Postgres це робить, вам потрібно послухати мої виступи з MVCC, щоб дізнатися, як Postgres внутрішньо маркує ці рядки, які він змінює. І у Postgres є спосіб, за допомогою якого він це робить, але це не робить на рівні блокування таблиць, він робить на більш низькому і на більш ефективному рівні.

Unlocking Postgres Lock Manager. Брюс Момжіан

А якщо хочу щось видалити? Якщо я видаляю, наприклад, один ряд і у мене все ще є мої два вступні блокування, і навіть якщо я захочу видалити їх усі, то вони все одно там присутні.

Unlocking Postgres Lock Manager. Брюс Момжіан

І, наприклад, я хочу вставити 1 рядків, а потім або видалити, або додати 000 рядків, то ті індивідуальні рядки, які я додаю або змінюю, вони не записуються тут. Вони записуються на нижчому рівні всередині ряду. І під час виступу з MVCC я говорив про це у деталях. Але дуже важливо, коли ви аналізуєте блокування, переконатися, що у вас блокування на рівні таблиці і що ви не бачите, як ведеться запис окремих рядів.

Unlocking Postgres Lock Manager. Брюс Момжіан

А як щодо експліцитного блокування?

Unlocking Postgres Lock Manager. Брюс Момжіан

Якщо я натисну «оновити», то маю два заблоковані ряди. І якщо виокремлю їх все і натисну «оновити скрізь», то в мене все одно залишаються два записи блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ми не створюємо окремих записів на кожен окремий ряд. Тому що тоді падає продуктивність, там може бути надто багато. І ми можемо опинитися у неприємній ситуації.

Unlocking Postgres Lock Manager. Брюс Момжіан

І те ж саме, якщо ми робимо shared, ми можемо робити на всі 30 разів.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ми відновлюємо нашу таблицю, видаляємо все, потім знову вставляємо один ряд.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ще один вид поведінки, який ви бачите в Postgres, це дуже добре відома та бажана поведінка – це те, що ви можете проводити update або select. І ви можете це робити одночасно. І select не блокують update і теж у зворотний бік. Ми говоримо читачеві не блокувати того, хто пише, а той, хто пише, не блокував читача.

Я вам покажу цей приклад. Я зараз зроблю вибір. Ми потім зробимо INSERT. І потім зможете побачити – 694. Ви зможете побачити ID транзакції, яка провела цю вставку. І це те, як це працює.

Unlocking Postgres Lock Manager. Брюс Момжіан

І якщо я зараз подивлюся на свій бекенд ID, він став – 695.

Unlocking Postgres Lock Manager. Брюс Момжіан

І я можу побачити, що 695 з'являється у моїй таблиці.

Unlocking Postgres Lock Manager. Брюс Момжіан

І якщо я проводжу оновлення тут так, то я отримую інший кейс. У цьому випадку 695 – ексклюзивне блокування, а у update така сама поведінка, але між ними не виникає конфлікту, що досить незвичайно.

І ви можете помітити, що вгорі – це ShareLock, а внизу – це ExclusiveLock. І обидві транзакції вийшли.

І треба послухати мій виступ у MVCC, аби зрозуміти, як це відбувається. Але це ілюстрація того, що ви можете робити це одночасно, тобто одночасно робити SELECT та UPDATE.

Unlocking Postgres Lock Manager. Брюс Момжіан

Давайте ми скинемо і вкотре зробимо одну операцію.

Unlocking Postgres Lock Manager. Брюс Момжіан

Якщо ви спробуєте запустити одночасно два update на тому самому ряду, то воно заблокується. І пам'ятайте, я говорив, що читач не блокує письменника, а письменник читача, але один письменник блокує іншого письменника. Т. е. ми не можемо робити так, щоб дві людини одночасно оновлювали один і той же ряд. Потрібно чекати, поки один із них закінчить.

Unlocking Postgres Lock Manager. Брюс Момжіан

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

Ми це оновили до 2-х. 699 – це перше оновлення. І воно пройшло успішно або воно перебуває в транзакції, що очікує, і чекає, коли ми підтвердимо або скасуємо.

Unlocking Postgres Lock Manager. Брюс Момжіан

Але подивіться інше – 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 також самоблокується. І потім у нижньому ряду ви побачите, що ми чекаємо, коли закінчить свою операцію.

Unlocking Postgres Lock Manager. Брюс Момжіан

І у lock_type, tuple ви бачите цифри.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ви можете побачити, що це 0/10. І це номер сторінки, а також offset цього конкретного ряду.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ви бачите, що стає 0/11, коли ми оновлюємо.

Unlocking Postgres Lock Manager. Брюс Момжіан

Але насправді – це 0/10, тому що відбувається очікування на цю операцію. У нас є можливість подивитися, що це той ряд, який я чекаю на підтвердження.

Unlocking Postgres Lock Manager. Брюс Момжіан

Як тільки ми його підтвердили і натиснули на commit, і коли оновлення закінчилося, це те, що ми отримуємо знову. Транзакція 700 – це єдине блокування, воно більше нікого не чекає, бо його закоммітували. Вона просто чекає, щоб транзакція завершилась. Як тільки 699 закінчується, ми більше нічого не чекаємо. І тепер транзакція 700 каже, що все добре, що всі блокування, які потрібні, має у всіх дозволених таблицях.

Unlocking Postgres Lock Manager. Брюс Момжіан

І щоб ще ускладнити всю цю справу, ми створюємо ще один view, який цього разу надасть нам ієрархію. Я не чекаю, що ви зрозумієте цей запит. Але це дасть нам більш чіткий вигляд того, що відбувається.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це рекурсивний вид, який також має ще одну секцію. І вона потім знову повертає все разом. Давайте використати це.

Unlocking Postgres Lock Manager. Брюс Момжіан

Що, якщо ми зробимо три одночасні оновлення і скажемо, що ряд зараз дорівнює трьом. І ми змінимо 3 на 4.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ось ми бачимо 4. І транзакційний ID 702.

Unlocking Postgres Lock Manager. Брюс Момжіан

І потім я поміняю 4 на 5. А 5 на 6, а 6 на 7. І я вибудовую в чергу низку людей, які чекатимуть на те, щоб ця одна транзакція закінчилася.

Unlocking Postgres Lock Manager. Брюс Момжіан

І все стає зрозумілим. Який перший ряд? Це 702. Це транзакційний ID, який спочатку поставив це значення. А що в мене написано у колонці Granted? У мене є позначки f. Це ті мої оновлення, які не можуть бути схвалені (5, 6, 7), тому що ми чекаємо, щоб транзакційний ID 702 закінчився. Там ми маємо блокування транзакційного ID. І виходить 5 транзакційних блокувань ID.

І якщо ви подивіться на 704, 705, то там ще нічого не написано, тому що вони ще не знають, що відбувається. Вони просто пишуть, що без уявлення, що відбувається. І вони просто підуть у сон, тому що вони чекають, поки хтось закінчить і їх розбудять, коли з'явиться можливість змінити низку.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це те, як це виглядає. Зрозуміло, що вони всі чекають 12-го рядка.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це те, що ми бачили тут. Ось 0/12.

Unlocking Postgres Lock Manager. Брюс Момжіан

Отже, як тільки перша транзакція схвалена, ви можете тут побачити, як працює ієрархія. І тепер стає зрозуміло. Вони усі стають чистими. І вони насправді все ще чекають.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ось що відбувається. 702 комітується. І тепер 703 отримує це блокування ряду, а потім 704 починає чекати, коли 703 закоммітується. І 705 теж цього чекає. І коли все це завершується, вони самі себе зачищають. І я хотів би вказати на те, що всі вишиковуються в чергу. І це дуже схоже на ситуацію з пробкою, коли всі чекають на першу машину. Перша машина зупинилася, і всі вишиковуються в довгу лінію. Потім вона рухається, потім наступна машина може проїхати вперед і отримати своє блокування тощо.

Unlocking Postgres Lock Manager. Брюс Момжіан

І якщо вам це здалося недостатньо складним, ми зараз поговоримо з вами про deadlocks. Я не знаю, хто з вас із ними стикався. Це досить поширена проблема у системах баз даних. Але deadlocks – це той випадок, коли одна сесія чекає, щоб щось виконала інша сесія. А зараз інша сесія очікує, щоб перша сесія виконала щось.

І, наприклад, якщо Іван каже: "Дай мені щось", а я кажу: "Ні, я тобі це дам тільки, якщо ти мені даси щось інше". А він каже: "Ні, я не дам тобі це, якщо ти мені не даси". І ми виходить у ситуації мертвого блокування. Я впевнений, що Іван так не зробить, але ви розумієте сенс, що у нас дві людини хочуть щось отримати і вони не готові це віддати, доки інша людина їм не віддасть, що вони хочуть. І тут немає рішення.

І, по суті, вашій базі даних це потрібно виявляти. І потім необхідно видаляти або закривати одну із сесій, тому що у протилежному випадку вони там залишаться назавжди. І ми це бачимо у базах даних, ми це бачимо в операційних системах. І в усіх місцях, де ми маємо паралельні процеси, таке може бути.

Unlocking Postgres Lock Manager. Брюс Момжіан

Та ми доставимо зараз два deadlocks. Ми поставимо 50 та 80. У перший ряд я проведу оновлення з 50 на 50. У мене вийде номер транзакції 710.

Unlocking Postgres Lock Manager. Брюс Момжіан

І потім я поміняю 80 на 81 і 50 на 51.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ось, як це виглядатиме. І тому 710 має блокування ряду, а 711 чекає підтвердження. Ми бачили це, коли оновлювали. 710 – власник нашого ряду. А 711 очікує, щоб 710 закінчив транзакцію.

Unlocking Postgres Lock Manager. Брюс Момжіан

І там навіть написано, на якому саме ряді у нас відбувається deadlocks. І ось де це починає стає дивним.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тепер ми оновлюємо 80-80.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ось де починається deadlocks. 710 очікує відгуку від 711, а 711 чекає 710. І це погано скінчиться. І із цього немає виходу. І вони чекатимуть на відгук один від одного.

Unlocking Postgres Lock Manager. Брюс Момжіан

І це просто все затримуватиме. І ми цього не хочемо.

Unlocking Postgres Lock Manager. Брюс Момжіан

І у Postgres є способи помічати, коли це відбувається. І коли це відбувається, то ви отримуєте таку помилку. І з цього ясно, що такий процес чекає SHARE LOCK'а від іншого процесу, тобто який блокується 711 процесом. А той процес очікував, щоб було дано SHARE LOCK на такий транзакційний ID і був заблокований таким процесом. Тому тут ситуація мертвого блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

Чи буває тристоронній deadlocks? Чи це можливо? Так.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ми вбиваємо ці цифри у таблицю. Ми змінюємо 40 на 40, ми робимо блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

Змінюємо 60 на 61, 80 на 81.

Unlocking Postgres Lock Manager. Брюс Момжіан

А потім ми міняємо 80, а потім бум!

Unlocking Postgres Lock Manager. Брюс Момжіан

І 714 тепер чекає 715. 716-ий 715-го чекає. І із цим уже нічого не зробити.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тут уже не дві людини, тут уже три особи. Я хочу щось від тебе, цей хоче щось від третьої людини, а третя людина хоче щось від мене. І ми виходить у тристоронньому очікуванні, тому що ми всі чекаємо, поки інша людина завершить те, що вона має зробити.

Unlocking Postgres Lock Manager. Брюс Момжіан

І Postgres знає, на якому ряді це відбувається. І тому він видасть вам наступне повідомлення, яке показує, що у вас є проблема, де три вступні блокують один одного. І тут нема обмежень. Це може бути у випадку, де 20 записів блокують один одного.

Unlocking Postgres Lock Manager. Брюс Момжіан

Наступна проблема – це serializable.

Unlocking Postgres Lock Manager. Брюс Момжіан

Якщо спеціальне послідовне блокування.

Unlocking Postgres Lock Manager. Брюс Момжіан

І повертаємось до 719. У нього цілком нормальна видача.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ви можете натиснути, щоб зробити транзакцію з serializable.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ви розумієте, що у вас тепер є інший вид блокування SA це означає serializable.

Unlocking Postgres Lock Manager. Брюс Момжіан

Unlocking Postgres Lock Manager. Брюс Момжіан

І тому ми маємо новий вид блокування, який називається SARieadLock, який є серійним блокуванням і дозволяє вводити серійники.

Unlocking Postgres Lock Manager. Брюс Момжіан

Також ви можете вставляти унікальні індекси.

Unlocking Postgres Lock Manager. Брюс Момжіан

У цій таблиці ми маємо унікальні індекси.

Unlocking Postgres Lock Manager. Брюс Момжіан

Тому, якщо я сюди введу число 2, тому я маю 2. Але в самомугорі я вставляю ще одне 2. І ви можете бачити, що у 721-го ексклюзивне блокування. Але тепер 722 очікує, щоб 721 завершив свою операцію, тому що він не може вставити 2 до тих пір, поки не знає, що станеться з 721.

Unlocking Postgres Lock Manager. Брюс Момжіан

І якщо ми робимо subtransaction.

Unlocking Postgres Lock Manager. Брюс Момжіан

Ось у нас 723.

Unlocking Postgres Lock Manager. Брюс Момжіан

І якщо ми зберігаємо крапку і потім її оновлюємо, то ми отримуємо новий транзакційний ID. Це ще один характер поведінки, який вам потрібно знати. Якщо ми повертаємо це, то транзакційний ID йде. 724 йде. Але тепер ми маємо 725.

І що я намагаюся тут зробити? Я намагаюся показати вам приклади незвичайних блокувань, які ви можете знайти: будь то serializable блокування або SAVEPOINT - це різні види блокувань, які з'являтимуться в таблиці блокувань.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це створення експліцитних (явних) блокувань, які мають pg_advisory_lock.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ви бачите, що тип блокування тут вважається як advisory. І тут червоним написано advisory. І можна одночасно так заблокувати з pg_advisory_unlock.

Unlocking Postgres Lock Manager. Брюс Момжіан

І завершуючи я хотів би вам показати ще одну дахісну річ. Я створю ще один вид. Але я поєднаю таблицю pg_locks з таблицею pg_stat_activity. І навіщо хочу це зробити? Тому що це дозволить мені подивитися та побачити всі поточні сесій та побачити, яких саме блокувань вони очікують. І це досить цікаво, коли ми збираємо воєдино таблицю блокувань та таблицю запитів.

Unlocking Postgres Lock Manager. Брюс Момжіан

І тут ми створюємо pg_stat_view.

Unlocking Postgres Lock Manager. Брюс Момжіан

І ми оновлюємо ряд на один. І тут бачимо 724. А потім ми оновлюємо наш ряд до трьох. І що ви бачите тут зараз? Це запити, тобто ви бачите весь список запитів, які перелічені у лівій колонці. А потім праворуч ви можете бачити блокування і те, що вони створюють. І це може бути більш зрозумілим для вас, щоб вам не було потреби щоразу повертатися до кожної сесії та дивитися – чи потрібно до неї приєднатися чи ні. За нас це роблять.

Ще одна функція, яка дуже корисна – це pg_blocking_pids. Ви, мабуть, ніколи не чули про неї. Що вона робить? Вона дозволяє нам сказати, що для цієї сесії 11740, які саме ID-процесів вона очікує. І ви можете бачити, що 11740 очікує 724. І 724 знаходиться на вершині. А 11306 є вашим ID процесом. По суті, ця функція йде за таблицею блокувань. І я знаю, що це трохи складно, але у вас це виходить розуміти. По суті, ця функція проходить через цю таблицю блокувань і намагається знайти, де цей процес ID, враховуючи ті блокування, на які вона чекає. І також намагається обчислити, який саме процес ID, у того процесу, який чекає на блокування. Тому ви можете запустити цю функцію pg_blocking_pids.

І це дуже корисно. Ми це додали тільки з версії 9.6, тому цієї функції лише 5 років, але вона дуже і дуже корисна. І те саме стосується другого запиту. Він показує те, що нам треба бачити.

Unlocking Postgres Lock Manager. Брюс Момжіан

Це те, про що я хотів поговорити з вами. І як я очікував, ми використовували весь наш час, тому що була така велика кількість слайдів. І слайди доступні для завантаження. Я хотів би подякувати вам за те, що ви були тут. Я впевнений, що вам сподобається залишок конференції, дякую!

Питання:

Наприклад, якщо я намагаюся оновити рядки, а друга сесія намагається видалити всю таблицю. Наскільки я розумію, що там має бути щось на зразок intent lock'а. Чи є таке у Postgres?

Unlocking Postgres Lock Manager. Брюс Момжіан

Повертаємось до самого початку. Можливо, ви пам'ятаєте, що коли ви робите будь-що, наприклад, коли ви робите SELECT, то ми видаємо AccessShareLock. І це запобігає видаленню таблиці. Тому якщо ви, наприклад, хочете оновити ряд у таблиці або видалити ряд, то хтось не може видалити всю таблицю одночасно з цим, тому що ви тримає цей AccessShareLock над всією таблицею і над рядом. І коли ви закінчили, вони можуть це видалити. Але поки що ви безпосередньо щось там міняєте, вони не зможуть зробити.

Давайте ще раз. Давайте перейдемо, наприклад, з видаленням. І ви бачите, як на ряді є ексклюзивний lock над усією таблицею.

Це буде схоже на lock exclusive, правильно?

Так, це схоже на це. Я розумію, про що ви кажете. Ви кажете, що, якщо я виконаю SELECT, то у мене буде ShareExclusive, а потім я перекладаю це в стан Row Exclusive, чи це стає проблемою? Але напрочуд це не створює проблему. Це схоже на збільшення ступеня блокування, але, по суті, я 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 у мене або потрібно це робити відразу і зовсім не чекати, або чекати до якогось часу.

Так, ви можете вибрати тайм-аут на блокування, на ваші locks. Ви також можете видати команду no way, яка буде … якщо ви не зможете відразу отримати блокування. Тому або lock timeout, або інше, що дозволить вам це зробити. Це не робиться на синтаксичному рівні. Це робиться як змінна на сервері. Іноді це не можна використовувати.

Ви можете відкрити 75 слайдів?

Так.

Unlocking Postgres Lock Manager. Брюс Момжіан

І моє питання наступне. Чому очікують обидва процеси оновлення 703?

І це чудове питання. Я не розумію, до речі, чому Postgres це робить. Але коли 703 був створений, він чекав 702. І коли 704 і 705 з'являються, то здається, що вони не знають того, що вони чекають, тому що там ще нічого немає. І Postgres це робить так: коли ви не можете отримати блокування, то він пише «А сенс вас обробляти?», тому що ви й так чекаєте на когось. Тому просто дамо йому повисіти у повітрі, він взагалі не оновлює це. Але що сталося тут? Як тільки 702 завершив процес і 703 отримав своє блокування, система повернулася назад. І сказала, що тепер у нас є дві людини, які чекають. А потім давайте ми їх поновимо разом. І вкажемо, що обоє чекають.

Я не знаю чому Postgres так робить. Але є проблема, яка називається f…. Мені здається, що це не термін російською. Це коли всі чекають на один замк, навіть якщо є 20 інстанцій, на який чекають замку. І раптом вони всі прокидаються одночасно. І всі починають намагатися зреагувати. Але система робить так, що всі очікують 703. Тому що вони всі чекають, і ми їх одразу всіх вишикуємо в чергу. І якщо з'являється будь-який новий запит, який був сформований після цього, наприклад, 707, то там знову буде порожнеча.

І мені здається, що це робиться для того, щоб можна було сказати, що на цьому етапі 702 чекає 703, а всі ті, хто прийдуть після цього, у них не буде жодного запису в цьому полі. Але щойно перший очікуючий залишає, і всі ті, хто чекав у цей момент до оновлення, отримують той самий маркер. І тому, на мою думку, це зроблено для того, щоб ми могли обробляти по порядку, щоб вони були правильно впорядковані.

Я завжди дивився на це як на досить дивний феномен. Бо тут, наприклад, їх взагалі не перераховуємо. Але, мені здається, щоразу, коли ми даємо новий lock, ми дивимося на всіх тих, хто перебуває в процесі очікування. Тоді ми їх усіх вишиковуємо в чергу. А потім будь-яка нова, яка приходить, потрапляє в чергу лише тоді, коли наступна людина перестала оброблятися. Дуже добре питання. Дякую вам велике за питання!

Мені здається, набагато логічніше, коли 705 очікує 704.

Але проблема тут така. Технічно ви можете розбудити або той чи той. І тому ми розбудимо той чи інший. Але що відбувається у роботі системи? Ви бачите, як 703 у самому верху заблокував свій власний транзакційний ID. Це те, як Postgres працює. І 703 блокується власним транзакційним ID, тому якщо хтось хоче почекати, то він чекатиме 703. І, по суті, 703 завершує. І лише після його завершення якийсь із процесів прокидається. І ми не знаємо, який саме цей процес буде. Потім ми поступово все опрацьовуємо. Але не ясно який саме процес прокидається першим, тому що це може бути будь-який із цих процесів. По суті, у нас був планувальник, який казав, що ми тепер можемо будити будь-який із цих процесів. Ми просто вибираємо один випадковим чином. Тому обидва з них слід зазначити, тому що ми можемо пробуджувати будь-який із них.

І проблема в тому, що у нас CP-нескінченність. І тому цілком можливо можемо розбудити пізніший. І якщо, наприклад, ми будитимемо пізніший, то чекатимемо того, хто щойно отримав блокування, тому ми не визначаємо, хто саме буде пробуджений першим. Ми створюємо просто таку ситуацію, і система їх будитиме в довільному порядку.

є статті про locks Єгора Рогова. Подивіться, вони теж цікаві та корисні. Тема, звичайно, дуже складна. Дякую, Брюсе!

Джерело: habr.com

Додати коментар або відгук