1. Вступ

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

Рівень ізольованості транзакцій — умовне значення, що визначає, якою мірою в результаті виконання логічно паралельних транзакцій до СУБД допускається отримання неузгоджених даних. Шкала рівнів ізольованості транзакцій містить ряд значень, проранжованих від найнижчого до найвищого; більш високий рівень ізольованості відповідає кращій узгодженості даних, але його використання може знижувати кількість транзакцій, що фізично виконуються паралельно.

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

Проблеми паралельного доступу за допомогою транзакцій

Під час паралельномого виконання транзакцій можливі такі проблеми:

  • втрачене оновлення (англ. lost update) — за одночасної зміни одного блоку даних різними транзакціями втрачаються всі зміни, окрім останньої;
  • «брудне» читання (англ. dirty read) — читання даних, доданих або змінених транзакцією, яка згодом не підтвердиться (відкотиться);
  • неповторюване читання (англ. non-repeatable read) — під час повторного читання в межах однієї транзакції раніше прочитані дані виявляються зміненими;
  • фантомне читання (англ. phantom reads) — одна транзакція в ході свого виконання кілька разів обирає безліч рядків за тими самими критеріями. Інша транзакція в інтервалах між цими вибірками додає рядки або змінює стовпці деяких рядків, які використовуються за умов вибірки першої транзакції, та успішно закінчується. У результаті вийде, що одні й ті самі вибірки у першій транзакції дають різні множини рядків.

Розглянемо ситуації, в яких можливе виникнення цих проблем.

2. Втрачене оновлення

Ситуація, коли за одночасної зміни одного блоку даних різними транзакціями одна зі змін втрачається.

Припустимо, є дві транзакції, які виконуються одночасно:

Транзакція 1 Транзакція 2
UPDATE tbl1 SET f2=f2+20 WHERE f1=1; UPDATE tbl1 SET f2=f2+25 WHERE f1=1;

В обох транзакціях змінюється значення поля f2, після завершення значення поля має бути збільшено на 45. Насправді може виникнути наступна послідовність дій:

  1. Обидві транзакції одночасно читають поточний стан поля. Точна фізична одночасність тут не є обов'язковою, достатньо щоб друга за порядком операція читання виконалася до того, як інша транзакція запише свій результат.
  2. Обидві транзакції обчислюють нове значення поля, додаючи відповідно 20 і 25 до раніше прочитаного значення.
  3. Транзакції намагаються записати результат обчислень у поле f2. Оскільки фізично одночасно два записи виконати неможливо, насправді одну з операцій запису буде виконано раніше, а іншу — пізніше. Водночас друга операція запису перезапише результат першої.

У результаті значення поля f2 після завершення обох транзакцій може збільшитися не на 45, а на 20 або 25, тобто одна із змінних даних транзакцій «пропаде».

3. "Брудне" читання

Читання даних, доданих або змінених транзакцією, яка не підтвердиться (відкотиться).

Припустимо, є дві транзакції, відкриті різними програмами, в яких виконані такі SQL-оператори:

Транзакція 1 Транзакція 2
UPDATE tbl1 SET f2=f2+1 WHERE f1=1;
SELECT f2 FROM tbl1 WHERE f1=1;
ROLLBACK WORK;

У транзакції 1 змінюється значення поля f2, а потім у транзакції 2 обирається значення цього поля. Після цього відбувається відкат транзакції 1. В результаті значення, отримане другою транзакцією, відрізнятиметься від значення, що зберігається в базі даних.

4. Неповторне читання

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

Припустимо, є дві транзакції, відкриті різними програмами, в яких виконані такі SQL-оператори:

Транзакція 1 Транзакція 2
SELECT f2 FROM tbl1 WHERE f1=1;
UPDATE tbl1 SET f2=f2+3 WHERE f1=1;
COMMIT;
SELECT f2 FROM tbl1 WHERE f1=1;

У транзакції 2 обирається значення поля f2, потім у транзакції 1 змінюється значення поля f2. У разі повторної спроби вибору значення з поля f2 транзакції 2 буде отримано інший результат. Ця ситуація особливо неприйнятна, коли дані зчитуються з метою їхньої часткової зміни та зворотного запису до бази даних.

5. Читання «фантомів»

Ситуація, коли при повторному читанні в рамках однієї транзакції одна і та ж вибірка дає різні множини рядків.

Припустимо, є дві транзакції, відкриті різними програмами, в яких виконані такі SQL-оператори:

Транзакція 1 Транзакція 2
SELECT SUM(f2) FROM tbl1;
INSERT INTO tbl1 (f1,f2) VALUES (15,20);
COMMIT;
SELECT SUM(f2) FROM tbl1;

У транзакції 2 виконується SQL-оператор, який використовує всі значення поля f2. Потім у транзакції 1 виконується вставлення нового рядка, що призводить до того, що повторне виконання SQL-оператора в транзакції 2 видасть інший результат. Така ситуація називається читанням фантома (фантомним читанням). Від читання, що не повторюється, воно відрізняється тим, що результат повторного звернення до даних змінився не через зміну/видалення самих цих даних, а через появу нових (фантомних) даних.