Розшифруйте кожну літеру ACID
Щоб мати можливість відкотити транзакцію при помилці:
🟢 Junior Level
ACID — це фундаментальний акронім, що описує чотири ключові вимоги до транзакційної системи (СУБД), які гарантують надійність, цілісність та передбачуваність обробки даних:
- A — Atomicity (Атомарність): «Все або нічого». Транзакція не може бути виконана частково. Якщо одна з операцій всередині транзакції завершується збоєм, усі попередні зміни відкочуються (ROLLBACK), і база даних повертається до початкового стану.
- C — Consistency (Узгодженість / Цілісність): Транзакція переводить базу даних з одного коректного (валідному) стану в інший коректний стан. Усі правила схеми (обмеження
PRIMARY KEY,FOREIGN KEY,CHECK,NOT NULL,UNIQUE) та бізнес-інваріанти повинні суворо дотримуватися. - I — Isolation (Ізольованість): Паралельні транзакції не повинні взаємно впливати на результати одна одної. Для кожної транзакції створюється ілюзія, що вона виконується в системі одноосібно.
- D — Durability (Довговічність / Стійкість): Якщо транзакція була успішно зафіксована (COMMIT), внесені нею зміни гарантовано зберігаються в енергонезалежній пам’яті й не будуть втрачені навіть при раптовому вимкненні живлення або падінні сервера СУБД.
Практичний приклад: банківський переказ
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class BankTransferService {
private final AccountRepository accountRepository;
public BankTransferService(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}
// Атомарна транзакція: або обидва рахунки оновляться, або жоден
@Transactional
public void transferMoney(Long fromAccountId, Long toAccountId, Double amount) {
// 1. Списання з рахунку А
accountRepository.withdraw(fromAccountId, amount);
// Імітація раптової помилки (перевищення ліміту або збій)
if (amount > 100_000) {
throw new IllegalStateException("Перевищено ліміт переказу");
}
// 2. Зарахування на рахунок Б
accountRepository.deposit(toAccountId, amount);
}
}
Аналогія: ACID нагадує відправку цінної посилки кур’єрською службою: посилка або доставляється адресату цілком, або повертається відправнику без втрат (Atomicity); коробка зобов’язана відповідати габаритам і правилам пошти (Consistency); кур’єри не плутають посилки різних клієнтів на сортувальному складі (Isolation); після вручення під підпис запис у реєстрі доставки неможливо стерти чи оскаржити (Durability).
🟡 Middle Level
Архітектурна реалізація кожного компонента ACID
+-------------------+---------------------------------------------------------------+
| Властивість ACID | Механізм реалізації в сучасних СУБД (PostgreSQL / MySQL) |
+-------------------+---------------------------------------------------------------+
| Atomicity | Undo Logs (MySQL) / Журнал випереджального запису WAL (Postgres)|
| Consistency | Схемні Constraints + Application Business Logic Checks |
| Isolation | MVCC (багатоверсійність) + Блокування (Pessimistic / 2PL) |
| Durability | Write-Ahead Logging (WAL / Redo Log) + системний виклик fsync |
+-------------------+---------------------------------------------------------------+
1. Atomicity: Журналювання змін
Щоб мати можливість відкотити транзакцію при помилці:
- MySQL (InnoDB): Створює сегмент Undo Log, куди перед зміною рядка записується його попередній стан. При команді
ROLLBACKСУБД читає Undo Log у зворотному порядку та повертає початкові значення. - PostgreSQL: Не перезаписує дані «на місці» (In-Place Update). Новий рядок записується як нова версія кортежу (Tuple), а старий позначається як видалений транзакцією
xmax. ПриROLLBACKтранзакція просто позначається в карті статусів (pg_xact) якABORTED, і її зміни ігноруються всіма транзакціями.
2. Consistency: Дворівнева модель
Цілісність даних гарантується спільними зусиллями бази даних та застосунку:
- Database-level: Декларативні обмеження схеми:
ALTER TABLE accounts ADD CONSTRAINT chk_positive_balance CHECK (balance >= 0);Якщо операція порушує обмеження
CHECK, СУБД аварійно перериває операцію та ініціює відкат усієї транзакції. - Application-level: Закон збереження балансу (сума дебету повинна дорівнювати сумі кредиту). База даних не може знати специфіку предметної області — цю відповідальність несе програмний код.
3. Isolation: MVCC проти блокувань
Традиційні блокування (Pessimistic 2PL) блокували читання рядків під час їх оновлення. Сучасні СУБД використовують MVCC (Multi-Version Concurrency Control):
- Транзакції запису створюють нові версії рядків, не блокуючи читачів.
- Транзакції читання бачать знімок даних (Snapshot) на момент старту транзакції або оператора.
- Читачі не блокують письменників, а письменники не блокують читачів.
4. Durability: Випереджальний запис у журнал (WAL)
Прямий запис змінених сторінок даних (Data Pages) на диск у випадкові сектори є надто повільним. Тому застосовується концепція Write-Ahead Logging (WAL):
- При зміні даних нова інформація спочатку послідовно записується в буфер журналу в оперативній пам’яті.
- При виклику
COMMITбуфер журналу примусово скидається на фізичний диск за допомогою системного викликуfsync(). - Самі сторінки даних в оперативній пам’яті (
Buffer Pool/shared_buffers) залишаються «брудними» (Dirty Pages) і скидаються на диск асинхронно у фоновому режимі (Checkpointer). - Якщо живлення сервера вимкнеться, при перезапуску процедура Crash Recovery перечитає WAL з диска та повторно застосує підтверджені зміни (REDO).
🔴 Senior Level
Глибокий аналіз реалізації в PostgreSQL та MySQL (InnoDB)
Atomicity & Crash Recovery: Алгоритм ARIES
Процес відновлення після збоїв у реляційних СУБД базується на класичному алгоритмі ARIES (Algorithms for Recovery and Isolation Exploiting Semantics), що складається з трьох фаз:
- Analysis: Аналіз WAL з моменту останнього чекпоїнту (Checkpoint) для визначення активних транзакцій на момент збою та сторінок, які перебували в буферному пулі.
- Redo: Повтор абсолютно всіх змін (включно з непідтвердженими транзакціями) вперед по журналу до точки падіння, відновлюючи точний стан сторінок у пам’яті.
- Undo: Відкат назад усіх транзакцій, які були активними в момент збою і не встигли виконати
COMMIT.
Архітектурна відмінність MVCC: PostgreSQL Heap Bloat vs MySQL Undo Log
- PostgreSQL (Append-Only MVCC): Кожен рядок таблиці зберігає службові поля
xmin(ID транзакції, що створила рядок) таxmax(ID транзакції, що видалила/оновила рядок). ПриUPDATEу файл таблиці (Heap) дописується нова версія рядка. Старі «мертві» версії рядків залишаються на диску доти, доки процес VACUUM не очистить їх. При інтенсивних апдейтах це призводить до розпухання таблиць (Table Bloat) та деградації продуктивності. - MySQL InnoDB (Rollback Segment): Нова версія рядка записується безпосередньо поверх старої прямо в сторінку таблиці. Стара версія витісняється в системний табличний простір Undo Tablespace у вигляді зворотної дельти (Undo Log Chain). Якщо читачу потрібна стара версія, він на льоту збирає її з ланцюжка Undo-логів. Таблиці не розпухають, але довгі транзакції переповнюють Undo Log.
Компроміси Durability у High-Load архітектурі
Фізичне скидання даних на диск через fsync() при кожному коміті обмежує пропускну здатність (TPS) характеристиками IOPS накопичувача.
-- PostgreSQL: Асинхронний коміт
SET synchronous_commit = off;
- Плюси: Транзакція повертає успіх клієнту відразу після запису в буфер WAL, не чекаючи
fsync(). Пропускна здатність на запис зростає у 3–10 разів. - Мінуси (Trade-off): Порушується сувора властивість Durability. При раптовому збої ОС або живлення можуть бути безповоротно втрачені останні 200–600 мс підтверджених транзакцій (поки демон
walwriterне скинув буфер).
4 Tricky Questions
1. У чому фундаментальна різниця між Consistency в абревіатурі ACID та Consistency в CAP-теоремі?
Відповідь: Це дві абсолютно різні концепції, що збігаються лише за назвою:
- Consistency в ACID (C-ACID) — Узгодженість / Валідність: Означає дотримання інваріантів та обмежень схеми даних (
FOREIGN KEY,UNIQUE,CHECK). База даних не допускає появи невалідних даних усередині однієї системи. - Consistency в CAP-теоремі (C-CAP) — Лінеаризовність / Атомарна видимість: Характеристика розподіленої системи. Означає, що всі репліки (вузли кластера) в один і той самий момент часу бачать абсолютно однакові дані. Будь-яке читання повертає результат останнього завершеного запису незалежно від того, до якого вузла звернувся клієнт.
2. Якщо транзакція зафіксована (COMMIT), чи означає властивість Durability, що дані фізично записані у файли таблиць на диску?
Відповідь:
Ні, не означає.
У переважній більшості СУБД (PostgreSQL, MySQL, Oracle) дані фізично скидаються тільки в журнал випереджального запису (WAL / Redo Log).
Самі сторінки табличних просторів (.ibd у MySQL або base/* у PostgreSQL) продовжують залишатися в оперативній пам’яті у вигляді брудних сторінок (Dirty Pages) протягом хвилин. Вони будуть скинуті на диск асинхронно фоновим процесом контрольних точок (Checkpointer). Durability гарантується тим, що у випадку збою сервер при перезапуску відтворить незаписані сторінки зі збереженого журналу WAL.
3. Чи гарантує анотація Spring @Transactional дотримання всіх чотирьох властивостей ACID за замовчуванням?
Відповідь:
Ні, властивість Isolation за замовчуванням гарантується лише частково.
За замовчуванням у Spring анотація @Transactional використовує рівень ізоляції базової бази даних (Isolation.DEFAULT):
- Для PostgreSQL та Oracle це Read Committed.
- Для MySQL (InnoDB) це Repeatable Read.
На рівні Read Committed паралельні транзакції піддаються аномаліям: Non-Repeatable Read (неповторюване читання) та Phantom Read (фантомне читання). Таким чином, абсолютна ізоляція транзакцій відсутня, якщо розробник явно не налаштує рівень Isolation.SERIALIZABLE або не застосує песимістичні блокування (SELECT FOR UPDATE).
4. Що таке Deferred Constraints (відкладені обмеження) у реляційних базах даних і як вони впливають на властивість Consistency?
Відповідь:
У реляційних СУБД (наприклад, PostgreSQL, Oracle) обмеження цілісності можуть бути оголошені з модифікатором DEFERRABLE INITIALLY DEFERRED.
У штатному режимі обмеження (FOREIGN KEY, CHECK) перевіряються негайно після кожного SQL-виклику (INSERT, UPDATE).
Якщо обмеження оголошено як DEFERRED, СУБД дозволяє тимчасове порушення цілісності даних всередині транзакції (наприклад, створення дочірнього запису до створення батьківського при циклічному посиланні зовнішніх ключів). Перевірка цілісності відкладається до фінального виклику COMMIT. Якщо до моменту коміту інваріант не відновився, транзакція відкочується. Це забезпечує гнучкість без шкоди для фінальної властивості Consistency.
🎯 Interview Cheat Sheet
30-секундна відповідь
Розшифровка ACID:
- A (Atomicity): Все або нічого (реалізується через Undo Log / WAL).
- C (Consistency): Збереження інваріантів схеми (FK, UNIQUE, CHECK) та бізнес-правил.
- I (Isolation): Незалежність паралельних транзакцій (реалізується через MVCC та блокування).
- D (Durability): Гарантія збереження зафіксованих даних (WAL +
fsyncна диск).MVCC принцип: «Читачі не блокують письменників, а письменники не блокують читачів». WAL принцип: Журнал змін послідовно скидається на диск через
fsync()при коміті; сторінки даних у пам’яті (dirty pages) скидаються пізніше асинхронно через Checkpoint.
Червоні прапорці (чого категорично не можна говорити)
- ❌ Плутати C в ACID (цілісність інваріантів) з C в CAP (лінеаризовність реплік).
- ❌ Вважати, що
COMMITгарантує негайний запис у файли таблиць на диску (він гарантує запис тільки в WAL). - ❌ Стверджувати, що
@Transactionalгарантує абсолютну ізоляцію з коробки (вона працює на рівніRead Committedу Postgres, допускаючи аномалії).