Бази даних мають таблиці та індекси, які зберігаються у файлах і кешуються в пам'яті, також відомі як буферний пул. Коли ви створюєте рядки, система бази даних спочатку записує їх у сторінки даних у пам'яті, а потім ці сторінки в кінцевому підсумку записуються у файли даних на диску.
Читання сторінки завжди перевіряє буферний пул пам'яті. Якщо її там немає, вона витягується з диска і поміщається в буферний пул.
Однак є проблема: що станеться, якщо ви втратите живлення, коли дані записуються в файли? Деякі сторінки могли бути записані, а інші — ні. Коли база даних запускається знову, ми стикаємося з втратою даних або навіть з їх пошкодженням.
Знайомтесь з WAL
Розробники баз даних швидко зрозуміли, що їм потрібно щось, що допомогло б при аваріях та втраті живлення, і це — WAL (Write-ahead log) або журнал повтору.
Що, якщо під час запису в таблиці та індекси даних створювати запис у WAL про ці зміни? Ми записуємо WAL у його власні файли та також записуємо на сторінки даних у пам'яті. Не обов'язково записувати дані безпосередньо в таблиці та індекси на диску, вони можуть залишатися в пам'яті, поки ми маємо журнал — ми завжди можемо відтворити таблицю.
WAL набагато менший за реальні файли даних, тому ми можемо швидше їх скидувати. Крім того, записи в ньому є послідовними, на відміну від сторінок даних, які є випадковими. Диски люблять послідовні записи, що є ще однією перевагою WAL.
WAL також можна використовувати для різних завдань, таких як реплікація, резервне копіювання та відновлення після аварій.
Що, якщо сталася аварія під час запису WAL?
Записи WAL також спочатку записуються в пам'ять, а потім скидаються пізніше на основі коміту транзакції. Ось чому стан транзакції є критичним.
База даних може зазнати аварії після запису записів WAL, і це нормально, якщо ми знаємо стан транзакції для кожного запису WAL — ми можемо відкинути або пропустити непідтверджені записи WAL під час відновлення.
Наприклад, якщо ви були посеред транзакції, а база даних зазнала аварії, ми вважаємо транзакцію скасованою за замовчуванням. Записи WAL, скинуті цією непідтвердженою транзакцією, будуть відкинуті під час відновлення.
Але якщо вам вдалося здійснити COMMIT і запис WAL для коміту транзакції потрапив на диск, а клієнт отримав успіх, то ця транзакція вважається підтвердженою, навіть якщо ми зазнали аварії відразу після цього. База даних залишатиметься консистентною в такому випадку.
Повторний запис WAL
Отже, ми встановили, що WAL є джерелом істини, ми записуємо зміни на сторінки даних у пам'яті, звісно (щоб поточні транзакції могли використовувати найновіші дані), але ми затримуємо скидання сторінок даних на диск, оскільки WAL потрапив на диск. WAL знаходиться попереду сторінок даних, тому і назва — write-ahead log (журнал попереднього запису).
Тепер у нас є файли даних на диску, які не синхронізовані з тим, що є в пам'яті, і це абсолютно нормально. Поки база даних працює, ми будемо читати тільки з пам'яті, яка має найновіші дані. Проблема виникає, якщо база даних зазнає аварії.
Коли база даних знову запускається, файл застарілий, ми не можемо просто забрати його в пам'ять і дати клієнтам їх прочитати, оскільки WAL є джерелом істини, нам потрібно ПОВТОРНО виконати зміни з WAL на файлах даних, і під час цього процесу ніхто не має права читати (інакше ми отримаємо пошкодження).
Час запуску бази даних сповільнюється, чим більше дані файли не синхронізовані з WAL (багато записів відбулося, але файли даних не були оновлені протягом тривалого часу).
Точки відновлення (Checkpointing)
Запитання, яке виникає: чи можу я контролювати, як часто сторінки даних скидаються на диск? І відповідь так, це називається точками відновлення (checkpointing), коли після того, як всі сторінки даних у пам'яті скидаються на диск, створюється новий запис WAL, який називається записом точки відновлення.
Однак точку відновлення важко виконувати з точки зору введення/виведення (I/O), оскільки сторінки даних можуть бути дуже великими і випадковими.
Занадто часті точки відновлення справді прискорюють запуск, але вони негативно впливають на пропускну здатність I/O вашого диска, що може забирати дорогоцінний час від критичних операцій читання або запису.
Деякі системи баз даних також мають журнали скасувань (undo logs), але це вже для іншої статті.
Як часто потрібно скидувати WAL?
WAL містить зміни від кількох транзакцій. Якщо одна з транзакцій була підтверджена, ми повинні скинути WAL на диск, якщо хочемо зберегти стійкість (durability). Звісно, це скине й інші дані транзакцій, які могли ще не бути підтверджені, що, як ми обговорювали, теж нормально, оскільки стан транзакції нас рятує.
Але деякі бази даних надають певні конфігурації, щоб послабити, як часто скидається WAL. Одна з них — це fsync
, що гарантує, що вміст файлу WAL буде скинутий на диск з кешу сторінок операційної системи. Інша — це затримка коміту для братських транзакцій (transaction sibling commit delay), яка чекає на підтвердження інших транзакцій, щоб зменшити кількість скидань.
Дійсно, інженерія баз даних — це захоплюючий світ сам по собі. Якщо ви хочете вивчити основи інженерії баз даних, ознайомтесь з моїм курсом на https://databases.win
Перекладено з: What happens when databases crash mid backend request?