Ноу Інти, лекція, транзакції

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

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

В "Введення в клієнт-серверні БД. InterBase" ми згадували, що транзакції - це пакет запитів, який послідовно проводить зміни БД і або приймається, якщо всі зміни записи підтверджені, або відкидається, якщо хоч один запит завершився неуспішно. Запити можуть складатися з операторів SELECT / INSERT / UPDATE / DELETE. причому в контексті однієї транзакції може бути як один такий запит. так і безліч запитів. Однак поняття "транзакція" набагато глибше цього короткого визначення.

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

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

Пояснимо цю суть на класичному прикладі перекладу грошей в банку з одного рахунку на інший.

Припустимо, наша БД працює без транзакцій. і нам потрібно зробити згаданий переклад. Тут ми можемо піти двома різними способами:

  1. Спочатку знімаємо гроші з одного рахунку, потім додаємо їх до іншого рахунку.
  2. Спочатку додаємо гроші до іншого рахунку, потім знімаємо їх з першого рахунку.

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

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

Атомарність (Atomicity)

Атомарність має на увазі, що транзакція є одиницею роботи з базою даних. Усередині транзакції може відбуватися безліч модифікацій БД. однак транзакція діє за принципом "все або нічого". Коли транзакція підтверджується (Commit), то підтверджуються всі зміни даних, зроблені в її контексті. Коли вона відкидається (відкочується, Rollback), то відкидаються і всі зміни. У разі виникнення збою, система, відновлюючись, ліквідує наслідки транзакцій. які не встигли завершитися.

Узгодженість (Consistency)

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

На стороні сервера за узгодженість відповідають обмеження CHECK. обмеження посилальної цілісності і тригери. Програміст, проте, повинен ретельно спроектувати механізми бізнес-логіки.

Ізольованість (Isolation)

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

Стійкість (Durability)

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

Неявний і явний старт транзакцій

Всі дії над базою даних, що здійснюються в клієнтському додатку, відбуваються всередині (в контексті) транзакції. У прикладах попередніх лекцій ми з'єднували клієнтські програми з базою даних, взагалі не використовуючи ніяких транзакцій. Однак це не означає, що їх не було. Просто транзакції запускалися неявно, автоматично. Причому з параметрами, створеними Delphi "за замовчуванням". У серйозних додатках БД це неприпустимо, тому що може привести до численних конфліктів.

Транзакцію можна стартувати і явно. Зі стандартних механізмів доступу ми будемо використовувати, в основному, InterBase Express (IBX). У додатку повинен бути присутнім як мінімум. один компонент IBTransaction. За допомогою цього компонента можна явно вказати параметри транзакції. управляти стартом, підтвердженням або відкотом транзакції. Робиться це за допомогою таких методів компонента:

  • StartTransaction - Старт транзакції.
  • Commit - Підтвердження транзакції з подальшим її закриттям.
  • CommitRetaining - Підтвердження транзакції без її закриття.
  • Rollback - Відкат транзакції з подальшим її закриттям.
  • RollbackRetaining - Відкат транзакції без її закриття.

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

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

Як транзакція працює

У базі даних є спеціальна область, яка називається TIP (Transaction Inventory Page - Інвентарна Сторінка Транзакцій). При старті транзакції. їй присвоюється ідентифікатор (TID. Transaction ID) - інвентарний номер, який зберігається в TIP. При цьому у самій останньої транзакції буде найбільший ідентифікатор. У TIP. крім номера стартувала транзакції. зберігається і її стан, яке може бути Active (В роботі), Committed (Підтверджена), Rolled Back (Скасована, відкат) і In Limbo (Невизначена).

Активною називається транзакція. яка зараз виконується.

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

Скасованої називають транзакцію. яка завершилася невдало. При цьому проводиться відкат зроблених їй дій, як правило, командою RollBack.

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

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

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

Якщо транзакція завершилася неуспішно, оригінал запису так і залишається оригіналом.

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

Однак можуть виникати і конфлікти транзакцій. Припустимо, що стартувала транзакція Т1. Вона створила версію запису, і поміняла її дані. В цей час стартувала конкуруюча транзакція Т2, і створила версію тієї ж записи. Оскільки Т1 ще не завершилася, Т2 при старті не могла бачити зміни даних, зроблені Т1, а значить, створила свою версію зі старого оригіналу. Тепер Т1 завершує роботу по Commit. Як повинен поступити InterBase. Якщо він помітить версію запису Т1 як оригінал, а старий запис. як віддалену, то у версії Т2 виявляться помилкові дані! Дії InterBase в цьому випадку будуть залежати від параметрів цих транзакцій. про що нижче ми поговоримо детальніше.

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

Таким чином, кажуть, що InterBase має багатоверсійності архітектуру (MGA - Multi Generation Architecture). Така архітектура дозволяє організувати роботу з базою даних так, щоб читають користувачі не блокували пишуть. Крім того, при виникненні збоїв в системі, InterBase дуже швидко відновлюється, завдяки саме MGA. До речі, InterBase є першим SQL-сервер, який підтримує багатоверсійності архітектуру.

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

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

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

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

Таким чином, молоді транзакції прибирають базу даних від сміття, залишеного старішими транзакціями. Але чистять вони не всі старі версії поспіль, а тільки версії того запису (або записів), до якої звертаються самі.

Оскільки на сервері одночасно може виконуватися безліч транзакцій. є термінологія визначення цих транзакцій.

  • Активна транзакція - транзакція, яка стартувала, але ще не завершена.
  • Зацікавлена ​​транзакція - це транзакція, що конкурує з поточної транзакцією.
  • Найстарша активна транзакція - це така активна транзакція. яка стартувала раніше інших. Або інакше, це активна транзакція з найменшим ідентифікатором.
  • Найстарша зацікавлена ​​транзакція - це така зацікавлена ​​транзакція. яка стартувала раніше інших. Або інакше, це зацікавлена ​​транзакція з найменшим ідентифікатором.

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

Схожі статті