Транзакція (бази даних)

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

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

  1. Забезпечення надійних робочих елементів, які дозволяють правильно відновити роботу у випадку збоїв та зберігати цілісність бази даних у випадку системних відмов, коли виконання операцій зупиняється (повністю або частково) і більшість операцій над базою даних залишаються незавершеними з нез'ясованим статусом.
  2. Для забезпечення роздільного доступу для процесів, що одночасно звертаються до бази даних. При відсутності ізоляції операцій результати, отримані процесами, можуть бути помилковими.

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

Приклад транзакції[ред. | ред. код]

Приклад: необхідно переказати з банківського рахунку номер 5 на рахунок номер 7 суму в 10 грошових одиниць. Цього можна досягти, наприклад, наведеною послідовністю дій:

  • Почати транзакцію: прочитати баланс на рахунку номер 5
зменшити баланс на 10 грошових одиниць: зберегти новий баланс рахунку номер 5
прочитати баланс на рахунку номер 7
збільшити баланс на 10 грошових одиниць: зберегти новий баланс рахунку номер 7
  • Закінчити транзакцію

Ці дії являють собою логічну одиницю роботи «переказ суми між рахунками», і, таким чином, є транзакцією. Якщо перервати дану транзакцію, наприклад, в середині, і не анулювати всі зміни, легко залишити власника рахунку номер 5 без 10 одиниць, тоді як власник рахунку номер 7 їх не отримає.

Властивості транзакцій[ред. | ред. код]

Одним з найбільш розповсюджених наборів вимог до транзакцій і транзакційних систем є набір ACID (англ. Atomicity, Consistency, Isolation, Durability). Вимоги ACID були в головному сформульовані наприкінці 70-х років Джимом Ґреєм[1]. Разом з тим, існують спеціалізовані системи з ослабленими транзакційними властивостями[2].

Atomicity — Атомарність[ред. | ред. код]

Докладніше: Атомарність

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

Consistency — Узгодженість[ред. | ред. код]

Одна з найскладніших і неоднозначних властивостей з четвірки ACID. У відповідності до цієї вимоги, система знаходиться в узгодженому стані до початку транзакції і повинна залишитись в узгодженому стані після завершення транзакції. Не можна плутати вимогу узгодженості з вимогами цілісності (англ. integrity). Останні правила є більш вузькими і, багато в чому, специфічні для реляційних СКБД: є вимоги цілісності типів (англ. domain integrity), цілісності посилань (англ. referential integrity), цілісності сутностей (англ. entity integrity), які не можуть бути порушені фізично в силу особливостей реалізації системи.

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

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

Isolation — Ізольованість[ред. | ред. код]

Під час виконання певної транзакції, транзакції, які відбуваються паралельно, не повинні впливати на її результат. Ця властивість не дотримується на рівні ізольованості Repeatable Read та нижче.

Durability — Надійність[ред. | ред. код]

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

Рівні ізоляції транзакцій[ред. | ред. код]

В ідеалі транзакції різних користувачів повинні виконуватись так, щоб створювалась ілюзія, що користувач поточної транзакції — єдиний. Однак в реальності, з міркувань продуктивності і для виконания деяких спеціальних задач, СКБД забезпечують певні рівні ізоляції транзакцій.

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

  • 0 — Читання непідтверджених даних (брудне читання) (Read Uncommitted, Dirty Read) — читання незафіксованих змін як своєї транзакції, так і паралельних транзакцій. Немає гарантії, що дані, змінені іншими транзакціями, не будуть в будь-який момент змінені в результаті їх відката, тому таке читання є потенційним джерелом помилок. Неможливими є втрачені зміни (lost changes), можливими є неповторювані читання і фантоми.
  • 1 — Читання підтверджених даних (Read Committed) — читання всіх змін своєї транзакції і зафіксованих змін паралельних транзакцій. Втрачені зміни і брудне читання не дозволяються, можливими є неповторюване читання і фантоми.
  • 2 — Повторюване читання (Repeatable Read, Snapshot) — читання всіх змін своєї транзакції, будь-які зміни, внесені паралельними транзакціями після початку своєї, є недоступними. Втрачені зміни, брудне і неповторюване читання не є можливими; можливі фантоми.
  • 3 — Впорядкований — (Serializable) — впорядковані транзакції. Результат паралельного виконання впорядкованої транзакції з іншими транзакціями повинен бути логічно еквівалентний результату їх будь-якого послідовного виконання. Проблеми синхронізації не виникають.

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

В СКБД рівень ізоляції транзакцій можна вибрати як для всіх транзакцій разом, так і для одної конкретної транзакції. За умовчанням в більшості баз даних використовується рівень 1 (Read Committed). Рівень 0 використовується в основному для відстеження змін тривалих транзакцій або для читання даних, що рідко змінюються. Рівні 2 и 3 використовуються при підвищених вимогах до ізольованості транзакцій.

Реалізація[ред. | ред. код]

Повноцінна реалізація рівнів ізоляції і властивостей ACID є нетривіальною задачею. Обробка вхідних даних призводить до великої кількості маленьких змін, включаючи оновлення як самих таблиць, так і індексів. Ці зміни потенційно можуть зазнати поразку: закінчилось місце на диску, операція займає забагато часу (timeout) і т. д. Система повинна у випадку невдачі коректно повернути базу даних у стан до транзакції.

Перші комерційні СКБД (наприклад, IBM DB2), скористались виключно блокуванням доступу до даних для забезпечення властивостей ACID. Але велика кількість блокувань призводить до суттєвого зменшення продуктивності. Є два популярних сімейства рішень цієї проблеми, які знижують кількість блокувань:

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

При упереджуючій журнализації, яка використовується в Sybase і MS SQL Server до версії 2005, всі зміни записуються до журналу, і тільки після успішного завершення — до бази даних. Це дозволяє СКБД повернутись до робочого стану після неочікуваного падіння системи. Тіньові сторінки містять копії тих сторінок бази даних на початок транзакції, в яких трапляються зміни. Ці копії активуються після успішного завершення. Хоча тіньові сторінки легше реалізуються, упереджуюча журналізація більш ефективна[4]

Подальший розвиток технологій керування базами даних привів до появи безблоковних технологій. Ідея контроля за паралельним доступом з допомогою часових міток (timestamp-based concurrency control) була розвинена і привела до появи багатоверсійної архітектури MVCC. Ці технології не потребують ні журнализації змін, аніж тіньових сторінок. Архітектура, що реалізована в Oracle 7.х і вище, записує старі версії сторінок в спеціальний сегмент відкату, але вони все ще доступні для читання. Якщо транзакція при читанні попадає на сторінку, часова мітка якої новіше за початок читання, дані беруться з сегменту відката (тобто використовується «стара» версія). Для підтримки такої роботи ведеться журнал транзакцій, але на відміну від «упереджуючої журналізації», він не містить даних. Робота з ним складається з трьох логічних кроків:

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

Журнал транзакцій в комбінації з сегментом відката (область, в якій зберігається копія всіх даних, що змінюються в ході транзакції) гарантує цілісність даних. У випадку збою запускається процедура відновлення, яка перевіряє окремі його записи наступним чином:

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

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

Див. також[ред. | ред. код]

Примітки[ред. | ред. код]

  1. Gray, Jim. The Transaction Concept: Virtues and Limitations. Proceedings of the 7th International Conference on Very Large Databases: pages 144—154, 1981(англ.)
  2. Advanced Transaction Models and Architectures [Архівовано 2009-01-06 у Wayback Machine.](англ.)
  3. Семейство алгоритмов ARIES. Архів оригіналу за 20 вересня 2008. Процитовано 6 травня 2014.
  4. Gray, J., McJones, P., Blasgen, M., Lindsay, B., Lorie, R., Price, T., Putzolu, F., and Traiger, I. The recovery manager of the System R database manager. ACM Comput. Surv. 13, 2 (June 1981).