1. Read uncommitted

Під «рівнем ізоляції транзакцій» розуміється ступінь захисту, що забезпечується внутрішніми механізмами СУБД (тобто не потребує спеціального програмування) від усіх або деяких вищезгаданих видів неузгодженості даних, що виникають під час паралельного виконання транзакцій. Стандарт SQL-92 визначає шкалу із чотирьох рівнів ізоляції:

  • Read uncommitted
  • Read committed
  • Repeatable read
  • Serializable

Перший з них є найслабшим, останній — найсильнішим, кожен наступний включає всі попередні.

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

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

2. Read committed

Більшість промислових СУБД, зокрема Microsoft SQL Server, PostgreSQL і Oracle, за замовчуванням використовують саме цей рівень. На цьому рівні забезпечується захист від чорнового, «брудного» читання, проте, в процесі роботи однієї транзакції, інша може бути успішно завершена і зроблені нею зміни зафіксовані. У результаті перша транзакція працюватиме з іншим набором даних.

Реалізація завершеного читання може ґрунтуватися на одному з двох підходів: блокуванні або версійності.

Блокування даних, що читаються та змінюються.

Полягає в тому, що пишуча транзакція блокує змінні дані для читаючих транзакцій, що працюють на рівні read committed або вищому, до свого завершення, перешкоджаючи таким чином «брудному» читанню, а дані, що зчитуються, які блокуються транзакцією, звільняються одразу після завершення операції SELECT (отже ситуація «неповторного читання» може виникати на даному рівні ізоляції).

Збереження декількох версій рядків, що змінюються паралельно.

При кожній зміні рядка СУБД створює нову версію цього рядка, з якої продовжує працювати транзакція, що змінила дані, в той час як будь-якій іншій «читає» транзакції повертається остання зафіксована версія. Перевага такого підходу в тому, що він забезпечує більшу швидкість, оскільки запобігає блокуванню. Однак він вимагає, порівняно з першим, значно більшої витрати оперативної пам'яті, яка витрачається на зберігання версій рядків.

Орім цього, під час паралельної зміни даних декількома транзакціями може виникнути ситуація, коли кілька паралельних транзакцій зроблять неузгоджені зміни тих самих даних (оскільки блокування відсутні, ніщо не завадить це зробити). Тоді та транзакція, яка зафіксується першою, збереже свої зміни до основної БД, а решта паралельних транзакцій виявиться неможливо зафіксувати (оскільки це призведе до втрати оновлення першої транзакції). Єдине, що може в такій ситуації СУБД — це відкотити решту транзакцій і видати повідомлення про помилку «Запис уже змінено».

Конкретний спосіб реалізації обирається розробниками СУБД, а в деяких випадках може налаштовуватися. Так, за замовчуванням MS SQL використовує блокування, але (у версії 2005 і вище) під час установки параметра READ_COMMITTED_SNAPSHOT бази даних переходить на стратегію версійності, Oracle працює лише за версійною схемою. У Informix можна запобігти конфліктам між транзакціями, що читають і пишуть, встановивши параметр конфігурації USELASTCOMMITTED (починаючи з версії 11.1), при цьому читаюча транзакція отримуватиме останні підтверджені дані.

3. Repeatable read

Рівень, при якому транзакція, що читає, «не бачить» зміни даних, які були нею раніше прочитані. При цьому жодна інша транзакція не може змінювати дані, які читає поточна транзакція, поки та не закінчена.

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

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

4. Serializable

Найвищий рівень ізольованості; транзакції повністю ізолюються одна від одної, кожна виконується так, ніби паралельних транзакцій немає. Тільки на цьому рівні паралельні транзакції не схильні до ефекту «фантомного читання».

5. Підтримка ізоляції транзакцій у реальних СУБД

СУБД, що забезпечують транзакційність, не завжди підтримують усі чотири рівні, а також можуть вводити додаткові. Можливі також різні нюанси щодо забезпечення ізоляції.

Так, Oracle у принципі не підтримує нульовий рівень, тому що його реалізація транзакцій виключає «брудні читання», і формально не дозволяє встановлювати рівень Repeatable read, тобто підтримує лише Read committed (за замовчуванням) та Serializable. При цьому на рівні окремих команд він фактично гарантує повторюваність читання (якщо команда SELECT у першій транзакції вибирає з бази набір рядків, і в цей час паралельна друга транзакція змінює якісь з цих рядків, то результуючий набір, отриманий першою транзакцією, міститиме незмінені рядки, начебто другої транзакції не було). Також Oracle підтримує так звані READ-ONLY транзакції, які відповідають Serializable, але при цьому не можуть самі змінювати дані.

А Microsoft SQL Server підтримує всі чотири стандартні рівні ізоляції транзакцій, а додатково — рівень SNAPSHOT, на якому транзакція бачить стан даних, який було зафіксовано до її запуску, а також зміни, внесені нею самою. Тобто поводиться так, ніби отримала під час запуску моментальний знімок даних БД і працює з ним. Відмінність від Serialized полягає в тому, що не використовуються блокування, але в результаті фіксація змін може виявитися неможливою, якщо паралельна транзакція змінила ті ж дані раніше; у цьому випадку друга транзакція під час спроби виконати COMMIT викликає повідомлення про помилку і буде скасована.