Всім привіт. На зв'язку Владислав Родін. В даний час я керівник курсу «Архітектор високих навантажень» в OTUS, а також викладаю на курсах, присвячених архітектурі ПЗ.
Окрім викладання, як ви могли помітити, я займаюся написанням авторського матеріалу для блогу OTUS на хабрі і сьогоднішню статтю хочу приурочити до запуску курсу
Запровадження
В
Ізоляція
Ізоляція вирішує завдання доступу до даних у конкурентному середовищі, фактично надаючи захист від race condition'ів. В ідеалі, ізоляція означає серіалізацію, тобто властивість, що забезпечує те, що результат виконання транзакцій паралельно такий же, як якщо вони виконувались послідовно. Основна проблема цієї властивості полягає в тому, що вона дуже важко забезпечується технічно і як наслідок сильно б'є за продуктивністю системи. Саме тому ізоляцію часто послаблюють, приймаючи ризики виникнення деяких аномалій, про які йтиметься нижче. Можливість виникнення тих чи інших аномалій якраз і характеризує рівень ізоляції транзакцій.
Найбільш відомими аномаліями є: dirty read, non-repeatable read, phantom read, але насправді їх ще 5: dirty write, cursor lost update, lost update, read skew, write skew.
Dirty write
Суть аномалії у тому, що транзакції можуть перезаписувати незакоммічені дані.
Ця аномалія небезпечна не лише тим, що дані можуть конфліктувати після коміту обох транзакцій (як на картинці), але й тим, що порушується атомарність: тому що ми дозволимо перезаписувати незакоммічені дані, то незрозуміло, як відкотити одну транзакцію, не зачепивши при цьому іншу .
Лікується аномалія досить просто: вішаємо блокування на запис перед початком запису, забороняючи іншим транзакціям змінювати запис доти, доки блокування не буде знято.
Dirty read
Dirty read означає прочитання незакоммічених даних.
Проблеми виникають, коли на основі вибірки необхідно здійснити якісь дії або ухвалити рішення.
Для виправлення аномалії можна повісити блокування читання, але це сильно вдарить по продуктивності. Набагато простіше сказати, що для rollback'а транзакції вихідний стан даних (до початку запису) обов'язково має бути збережений у системі. Чому б не читати звідти? Це досить недорого, тому більшість баз даних прибирають dirty read за замовчуванням.
Lost update
Lost update означає втрачені оновлення, і переклад досить точно відображає суть проблеми:
Фактично, результат транзакції Т2 було скасовано. Виправляється така ситуація явними чи неявними блокуваннями запису. Тобто ми або просто здійснюємо оновлення запису, і тоді виникає неявне блокування, або ми виконуємо виберіть для оновлення, викликаючи блокування на читання і на записи. Зверніть увагу, що така операція досить небезпечна: своїм «невинним» читанням, ми блокуємо інші читання. Деякі бази пропонують безпечніший select for share, що дозволяє читати дані, але не дозволяє їх змінювати.
Cursor lost update
Для більш тонкого контролю бази можуть пропонувати інші інструменти, наприклад курсор. Курсор- це структура, що містить набір рядків і що дозволяє за ними ітеруватися. declare cursor_name for select_statement. Вміст курсору описується select'ом.
Навіщо потрібний курсор? Справа в тому, що деякі бази даних пропонують блокування на всі записи, вибрані select'ом (read stability), або тільки на той запис, на якому знаходиться зараз курсор (cursor stability). При cursor stability здійснюється short lock, що дозволяє знизити кількість блокувань у разі, якщо ми итерируемся з великої вибірці даних. Тому аномалію lost update виділяють для курсору окремо.
Non-repeatable read
Non-repeatable read полягає в тому, що під час виконання нашої транзакції 2 послідовні читання одного і того ж запису призведе до отримання різних результатів, тому що інша транзакція втрутилася між цими двома читаннями, змінила наші дані і була закоммічена.
Чому це взагалі проблема? Уявіть собі, що мета транзакції Т2 на зображенні вибрати всі товари, ціна яких менша, ніж 150 у.о. Хтось інший оновив ціну до 200 у.о. Таким чином, встановлений фільтр не спрацював.
Дані аномалії перестають виникати при додаванні двофазних блокувань або використання механізму MVCC, про що хотілося б поговорити окремо.
Phantom read
Фантомним називається читання даних, доданих іншою транзакцією.
Як приклад можна спостерігати неправильну вибірку найдешевшого товару у разі даної аномалії.
Позбутися фантомних читань вже досить складно. Звичайного блокування недостатньо, адже не можемо заблокувати те, чого ще немає. 2PL-системи використовують предикативне блокування, тоді як MVCC-системи планувальник транзакцій скасовує транзакції, які можуть бути порушені вставкою. Як перший, так і другий механізми досить важковагові.
Read skew
Read skew виникає коли ми працюємо з кількома таблицями, зміст яких має змінюватись узгоджено.
Припустимо, є таблиці, що представляють посади та їх метаінформацію:
Одна транзакція читає з таблиць, інша їх змінює:
В результаті виконання транзакції Т1 у посту title = Good, а updated_by = T2, що є деякою невідповідністю.
Фактично, це non-repeatable read, але у складі кількох таблиць.
Для виправлення, Т1 може вішати блокування на всі рядки, які вона читатиме, що не дасть транзакції Т2 змінювати інформацію. У випадку MVCC транзакція Т2 буде скасована. Захист від цієї аномалії може стати важливим, якщо ми використовуємо курсори.
Write skew
Цю аномалію теж простіше пояснити на прикладі: припустимо, що в нашій системі хоча б один лікар повинен бути на чергуванні, але обидва лікарі вирішили своє чергування скасувати:
Аномалія призвела до того, що жоден із лікарів не вийде на чергування. Чому так вийшло? Тому що транзакція перевіряла умову, яка може бути порушена іншою транзакцією, а через ізоляцію ми не побачили цієї зміни.
Це той же non-repeatable read. Як варіант, select'и можуть вішати блокування на ці записи.
Write skew та read skew є комбінаціями попередніх аномалій. Можна розглянути write skew, що є по суті phantom read'ом. Розглянемо таблицю, в якій є імена співробітників, їх зарплата та проект, на якому вони працюють:
У підсумку ми отримуємо наступну картину: кожен менеджер думав, що його зміна не призведе до виходу за бюджет, тому вони внесли кадрові зміни, які в сумі призвели до перевитрати.
Причина виникнення проблеми така сама, як і у фантомному читанні.
Висновки
Ослаблення рівня ізоляції транзакцій у базі даних є компромісом між безпекою та продуктивністю, до вибору цього рівня слід підходити виходячи з потенційних ризиків для бізнесу у разі виникнення тих чи інших аномалій.
Джерело: habr.com