Что такое лог предзаписи (Write-Ahead Log, WAL) и какова его роль в базах данных?

Ответ

Лог предзаписи (Write-Ahead Log, WAL) — это стандартный механизм обеспечения атомарности и долговечности (две из четырех гарантий ACID) в системах управления базами данных.

Принцип работы:

Перед тем как изменить данные в основных файлах БД на диске, все операции изменения сначала последовательно дописываются в специальный файл — лог (WAL-файл). И только после успешной записи в лог транзакция считается зафиксированной (committed).

Пошаговый процесс:

  1. Начало транзакции.
  2. Запись в лог: Все изменения (например, INSERT, UPDATE, DELETE) записываются в конец WAL-файла.
  3. Подтверждение: Как только запись в лог успешно завершена, система может подтвердить клиенту успешное выполнение транзакции.
  4. Применение изменений (Checkpointing): Позже, в фоновом режиме или в определенные моменты, эти изменения из лога переносятся в основные файлы данных.

Основные преимущества:

  • Надежность (Durability): Если система выйдет из строя до того, как изменения будут записаны в основные файлы, после перезапуска СУБД сможет восстановить свое состояние, "проиграв" записи из WAL.
  • Производительность: Последовательная запись в один файл (лог) значительно быстрее, чем множество случайных записей в разные места на диске (основные файлы данных).

Примеры систем, использующих WAL: PostgreSQL, SQLite, Oracle, LevelDB.

В Go, встраиваемые key-value хранилища, такие как bbolt (форк BoltDB), используют этот механизм. Хотя вы не взаимодействуете с WAL напрямую, каждая транзакция db.Update полагается на него для обеспечения безопасности данных:

// Этот код использует bbolt, который под капотом реализует WAL
db, err := bbolt.Open("my.db", 0600, nil)
if err != nil {
    log.Fatal(err)
}
defer db.Close()

// Эта транзакция будет сначала записана в WAL, а затем применена
err = db.Update(func(tx *bbolt.Tx) error {
    b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
    if err != nil {
        return err
    }
    return b.Put([]byte("key"), []byte("value"))
})