Ассаламу алейкум, сьогодні ми розглянемо Транзакції в реляційних базах даних. Дізнаємось, для чого вони потрібні та як вони імплементовані в деяких базах даних.
Процес транзакції
Примітка: Ця стаття не про транзакції в платіжних системах.
Що таке транзакція?
Транзакція — це сукупність кількох SQL запитів, які виконуються разом як одне ціле. Основна причина, чому їх розглядають як одне ціле, полягає в тому, що основне правило SQL баз даних полягає в тому, щоб дані були чітко структуровані, і тому у вас може бути багато таблиць, і не всі їх можна змінити через один запит. Ось чому нам допомагає транзакція. Транзакцію можна розглядати як набір логічних операцій, які повинні бути виконані одночасно.
Наприклад, ви хочете замовити кімнату в готелі. Ви вибираєте кімнату, натискаєте кнопку "замовити", і з'являється форма для введення даних картки. Після заповнення, коли ви натискаєте кнопку оплати, запит йде на сервер, де ми перевіряємо, чи є вільна кімната в базі даних. Якщо вона є, перевіряється баланс картки, і якщо грошей достатньо, відбувається оплата. У разі, якщо кімната вже заброньована, система не дасть можливості провести платіж. Для таких процесів і використовується транзакція.
Що входить до складу транзакції?
Транзакція складається з трьох основних частин: BEGIN — ключове слово, яке позначає початок транзакції, після того як всі зміни виконано, за допомогою ключового слова COMMIT ми записуємо їх в базу даних. Якщо результати не відповідають нашим очікуванням, ми використовуємо ключове слово ROLLBACK, щоб повернути все назад.
Розглянемо приклад коду:
BEGIN;
-- 1-Крок: Перевірка наявності вільної кімнати
SELECT room_id
FROM rooms
WHERE room_id = 101 AND status = 'available'
FOR UPDATE;
-- 2-Крок: Перевірка балансу картки
SELECT card_balance
FROM guests
WHERE guest_id = 1
FOR UPDATE;
-- Встановлюємо ціну кімнати 500
IF (SELECT card_balance FROM guests WHERE guest_id = 1) >= 500 THEN
-- 3-Крок: Зняття грошей з картки
UPDATE guests
SET card_balance = card_balance - 500
WHERE guest_id = 1;
-- 4-Крок: Додавання замовлення до історії
INSERT INTO bookings (guest_id, room_id, check_in_date, check_out_date, booking_date, amount_paid)
VALUES (1, 101, '2025-01-10', '2025-01-15', CURRENT_DATE, 500);
-- 5-Крок: Оновлення статусу кімнати на "заброньовано"
UPDATE rooms
SET status = 'booked'
WHERE room_id = 101;
-- Завершення транзакції
COMMIT;
ELSE
-- Якщо на картці недостатньо коштів, скасовуємо всі зміни
ROLLBACK;
RAISE EXCEPTION 'Недостатньо коштів на картці.';
END IF;
Тепер давайте розглянемо, як COMMIT і ROLLBACK реалізовані в базах даних.
Перед тим, як продовжити читання, спробуйте подумати, як це може виглядати у вашій системі. Можливо, ви знайдете кращі рішення.
Commit та Rollback зазвичай реалізуються двома основними способами:
1-й спосіб. Всі зміни записуються спочатку в буферну пам'ять (buffer pool), і під час процесу commit ці зміни записуються відразу в базу даних.
У цьому випадку кожен запит може бути виконаний швидко (наприклад, update, delete), оскільки вони знаходяться в пам'яті, але запис в базу даних може зайняти більше часу, що може призвести до аварії бази даних.
2-й спосіб. Кожен запит відразу записується в базу даних. В такому випадку запити виконуються повільніше, оскільки вони записуються безпосередньо в базу даних. Однак кожен спосіб має свої переваги і недоліки.
Якщо ви хочете виконати rollback, то вам потрібно повернути кожну змінену інформацію до попереднього стану, що потребує часу. У випадку з першим методом всі зміни просто видаляються з буфера, що значно спрощує процес.
Швидкість цих методів не завжди впливає на весь процес транзакції. Наприклад, якщо виконується транзакція з 1000 запитів, обидва методи можуть завершити процес одночасно. Однак, при першому методі кожен запит виконується за 1 секунду, а запис в базу даних займає ще 10 секунд, загалом 1010 секунд. У другому методі на кожен запит витрачається 1.01 секунди, що робить його повільнішим для окремих запитів, але загальний час виконання буде таким же (хоча це не завжди так, я навів це як приклад для легшого розуміння).
Тут у вас може виникнути питання: що відбувається, якщо база даних припиняє свою роботу під час процесу commit (краш)?
Щоб уникнути таких проблем, бази даних застосовують транзакції наступним чином:
До внесення змін у базу даних усі дані транзакції записуються у transaction log (журнал транзакцій). Це відомо як WAL (Write-Ahead Logging) в PostgreSQL.
Процес commit складається з двох етапів, що називаються Two-Phase Commit:
-
Фаза підготовки (Prepare Phase):
- Транзакція готується, і всі зміни записуються у журнал транзакцій.
- На цьому етапі транзакція ще не завершена.
-
Фаза commit (Commit Phase):
- На цьому етапі записуються зміни в журнал, і ці зміни застосовуються до бази даних.
У випадку крашу є два варіанти:
Якщо транзакція знаходиться на етапі Prepare, але не була комітована, то після перезавантаження бази даних вона перевіряє журнал транзакцій і, на його основі, виконує rollback. Якщо транзакція була вже на етапі Commit, то база даних змінює дані відповідно до журналу.
Різні бази даних працюють по-різному:
- MySQL/SQL Server не запускаються, поки транзакція не буде повністю скасована.
- PostgreSQL, у свою чергу, починає rollback, після чого нові транзакції не враховуються, а застарілі дані видаляються через Vacuum.
Чи відразу видимі зміни, зроблені в транзакціях?
Зазвичай ні, оскільки зміни ще не commit-нуті, навіть якщо дані вже записані в таблиці, committed column буде false, і інші користувачі не зможуть їх побачити. Однак користувач, що виконує транзакцію, побачить ці зміни. Це також залежить від isolation level, і ми обговоримо це в наступних статтях.
Ми знаємо, що транзакції зазвичай використовуються для оновлення або зміни даних, але іноді вони можуть бути Read-only транзакціями. Це корисно, коли потрібно отримати звіт на основі поточних даних, і в таких випадках нам допомагає здатність транзакцій до snapshot, що дозволяє виключити нові зміни з цього звіту. Snapshot — це точна копія бази даних на певний момент часу.
Примітка: Транзакція має здійснюватися в межах одного підключення до бази даних, ви не можете почати одну транзакцію в одному підключенні, а завершити її в іншому.
Примітка: Зазвичай навіть прості операції автоматично обгортаються в транзакцію, і зазвичай вони виконуються з автоматичним commit.
У підсумку, транзакція допомагає запобігти несанкціонованим змінам у базі даних і є важливим інструментом, який надають бази даних для забезпечення цілісності даних.
Якщо стаття сподобалася, не забудьте поставити аплодісменти (можна навіть 50 разів).
Вибачте за можливі помилки та недоліки!
linkedin.com => Ulug’bek Habibov | LinkedIn
telegram channel => @habibov_ulugbek
Перекладено з: Tranzaksiya nima ? (Siz bilmagan ma’lumotlar)