Основное правило: Записывайте логи до того, как прыгаете
Весь протокол записи в журнал полного авансирования сводится к одному незыблемому правилу: перед внесением каких-либо изменений в фактические файлы данных на диске необходимо записать запись, описывающую это изменение, в последовательный файл журнала, предназначенный только для добавления, и подтвердить ее как надежную — то есть она была полностью выгружена во физическое хранилище, а не просто находится в буфере операционной системы. Отсюда происходит название: запись в журнал записывается заранее перед фактическим изменением. Транзакция никогда не считается завершенной до тех пор, пока ее логическая запись не будет надежно записана на диске, хотя соответствующие страницы основных файлов данных могут отражать старые, предотделенные значения в течение некоторого времени после этого. Это гарантийное упорядочение является единственным несущим нагрузку предположением под почти всеми заявлениями о долговечности базы данных, на которые вы когда-либо полагались. Если запись журнала происходит первой и подтверждается как надежная, база данных имеет все необходимое для последующего восстановления изменения, независимо от того, что происходит с более медленным и грязным процессом обновления фактических страниц данных. Нарушение этого порядка — сначала выгрузите страницу данных, а затем журнал — приведет к краху всей модели безопасности, поскольку сбой между этими двумя шагами оставит измененный файл данных без записи о том, что изменилось, или было ли оно когда-либо предназначено для постоянства.
Відновлення після збою: Відтворення запису
Виплата правила про попереднє записування настає в момент, коли щось піде не так. Якщо база даних аваріює в будь-який час — від втрати електроенергії до зупинки процесу або паніки операційної системи — їй не потрібно здогадуватися, в якому стані знаходяться її файли даних. При перезавантаженні процес відновлення читає вперед по запису, починаючи з останньої відомої контрольної точки, та відтворює кожен запис, який він знаходить. Для будь-якого транзакції, запис коміту якої потрапив у стійкий журнал, відновлення повторно застосовує зміну, якщо вона ще не була записана там. Для будь-якої транзакції, яка була в процесі, але не досягла запису коміту, відновлення скасовує будь-які часткові зміни, які були зроблені, використовуючи попередні записи журналу для відкату даних до їхнього стану до транзакції. Цей процес передубання-відкату є детермінованим і механічним: журнал є повним, упорядкованою історією намірів, тому відновлення насправді є ретельною бухгалтерією, а не здогадками. Результатом є те, що база даних може аварійним чином — в середині транзакції, в середині контрольної точки, в середині будь-чого — і все ще відновитися в стані, який точно так же, якби відбулися лише транзакції, які були підтверджені, ні більше і ні менше. Ця гарантія дозволяє додаткам довіряти підтвердження коміту без турбот про фізичний сміття під ним.
Чому послідовні записи є виграшем у продуктивності
Write-ahead logging не просто механізм безпеки, це також розумний трюк для підвищення продуктивності, прихований під ним. Фактичні файли даних бази даних організовані на сторінки, розсіяні по диску за таблицями, індексами та рядками, тому безпосереднє оновлення їх вимагає постійного пошуку в різних, непередбачуваних місцях — випадкові записи, які порівняно повільні, особливо на обертових дисках і навіть у меншій мірі на флеш-пам’яті. Лог, навпаки, є тільки для додавання: кожен новий запис просто прикріплюється до кінця поточного файлу логу, що означає, що пристрій зберігання може записати його як один швидкий послідовний потік. Послідовні записи можуть бути значно швидшими за випадкові записи, оскільки вони уникають накладних витрат пошуку та дозволяють основному пристрою зберігання ефективно пакетувати та стімовувати дані. Write-ahead logging безпосередньо використовує цей проміжок: оскільки довговічність вимагає лише послідовного запису логу, база даних може відкласти повільне розсіяне оновлення фактичних сторінок файлів даних на пізніший час, групуючи їх разом і ефективніше записуючи їх масами. Клієнт отримує швидке та стійке підтвердження миттю, коли запис логу очищений, тоді як дорогий випадковий запис проти реальних сторінок файлів даних відбувається неохоче, поза критичним шляхом кожного транзакційного процесу.
Контроль цілісності: Обмеження розміру журналу
Якщо база даних лише додавала записи до журналу без взаємодії з файлами даних, виникли б дві проблеми: журнал би рос нескінченно, і після аварії в пізніший час відновлення потрібно було б відтворити величезну історію з першого запису. Контроль цілісності вирішує обидві ці проблеми. Періодично база даних призупиняє роботу, щоб застосувати всі зафіксовані зміни до певної точки безпосередньо у файли даних, повністю оновлюючи їх відповідно до опису журналу. Після завершення цієї роботи та підтвердження її надійності, усі записи в журна до цього контрольного моменту більше не потрібні для відновлення — вони описують зміни, які тепер постійно відображені у файлах даних, тому ці старі записи можна видалити або архівувати. Контроль цілісності фактично стає новою відправною точкою для відновлення: після аварії потрібно лише пройти журнал від останнього контрольного моменту вперед, а не з початку часу. Це прямий регуляторний важіль для адміністраторів баз даних: частіші контрольні точки означають коротший час відновлення після аварії, але збільшують навантаження від додаткових записів у файли даних, тоді як рідкісніші контрольні точки зменшують це навантаження, але залишають довгий журнал для відтворення в разі виникнення проблем.
Реальні Системи, Будовані на Записних Книгах
Записна книга не є теоретичним дивом, вона є основою майже кожної виробничої системи зберігання даних, яка використовується сьогодні. PostgreSQL реалізує її буквально під назвою WAL, стімулюючи кожен зміст до записних книг перед зверненням до сторінок таблиць, і використовуючи той самий потік логів для живлення реплікації та відновлення в певний момент часу. Двигун InnoDB MySQL використовує еквівалентну структуру під назвою redo log, яка виконує ту ж саму мету, гарантуючи, що зафіксовані транзакції виживуть після збою, навіть якщо буфер пам'яті, який містить змінені сторінки в пам’яті, втрачено. За межами реляційних баз даних такий же основний принцип повторюється в сучасних файлових системах під назвою journaling — ext4, NTFS і APFS усі тримають невелику записку про очікувані операції метаданих (і іноді дані), щоб перерване записування диска під час відключення живлення не пошкодило внутрішні структури файлової системи. Розподелені системи та черги повідомлень також позичають цей шаблон, використовуючи лише записи для визнання того, що все інше можна побудувати з них. Де б ви не знайшли систему, яка обіцяє, що ваші дані виживуть раптового збою, там, ймовірно, тихо працює записна книга.
Frequently asked questions
Що саме гарантує ведення заздалегідь записів (write-ahead logging)?
Воно гарантує довговічність і узгодженість після аварій: кожне записане повідомлення про коміт (commit record) записується в журнал, що означає, що зміни від транзакції витримають збій і будуть присутні після відновлення, а будь-яка транзакція, яка не була підтверджена, буде повністю скасована, не залишаючи за собою часткових змін.
Чому не просто записувати безпосередньо в файли даних і пропускати журнал?
Оновлення файлів даних – це розкидані випадкові записи на багато різних сторінок, що є повільним і неможливим для застосування атомарно як група. Якщо збій стався під час оновлення, файли даних можуть залишитися в невідповідному, невиправному стані без будь-якого запису про те, що мало відбутися. Журнал забезпечує швидкий, упорядкований, відтворюваний запис намірів, який робить відновлення можливим.
Чи означає WAL, що база даних ніколи не втрачає даних?
Це означає, що база даних ніколи не втрачатиме або пошкоджуватиме дані для транзакцій, які були підтверджені як комітовані, за умови, що журнал сам був надійно записаний. Дані, які ніколи не були визнані як комітовані, або повне знищення пристрою зберігання, є тим, що WAL само по собі не може захистити.
Яка різниця між redo та undo під час відновлення?
Redo повторює зміни від комітованих транзакцій, які могли ще не досягти файлів даних, гарантуючи, що нічого комітованого не буде втрачено. Undo скасовує часткові зміни від транзакцій, які були в процесі, але не були комітовані, гарантуючи, що нічого незавершеного не потрапить у остаточний стан.
Як верифікація (checkpointing) впливає на час відновлення після збою?
Верифікація застосовує зафіксовані зміни до файлів даних і дозволяє відкидати старі записи в журналі, тому для відновлення потрібно лише відтворити журнал від останньої верифікації вперед. Частіші верифікації скорочують час відновлення за рахунок додаткової роботи з фоновим записом; рідкісніші верифікації зменшують навантаження, але збільшують довжину журналу, який потрібно відтворити.
Спробуйте наживо
Усе, що вище, працює прямо у вашому браузері — відкрийте Write-Ahead Logging: How Databases Survive a Crash і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.
▶ Відкрити симуляцію Write-Ahead Logging: How Databases Survive a Crash