Write-Ahead Logging: надёжность баз данных
Write-Ahead Logging (WAL) — фундаментальная техника в базах данных для обеспечения надёжности. WAL гарантирует, что данные не будут потеряны при сбое питания или аварии сервера. Эта техника используется в PostgreSQL, SQLite, Oracle и других СУБД.
Концепция
WAL меняет порядок операций для гарантии сохранности данных при сбое.
Традиционный подход:
1. Изменить данные на диске
2. Записать в лог
WAL:
1. Записать в лог (WAL)
2. Изменить данные на диске
Преимущество: лог записывается последовательно (быстро), данные могут записываться в любом порядке. При сбое можно восстановить состояние из лога.
Структура WAL
┌────────────────────────────────┐
│ WAL Segment │
├────────────────────────────────┤
│ Header (checkpoint, segno) │
├────────────────────────────────┤
│ XLOG Record 1: │
│ - LSN (Log Sequence Number) │
│ - prev LSN │
│ - Transaction ID │
│ - redo/undo │
│ - Page data │
├────────────────────────────────┤
│ XLOG Record 2: │
│ ... │
└────────────────────────────────┘
PostgreSQL WAL
-- Проверка WAL
SELECT pg_current_wal_lsn();
-- Checkpoint
CHECKPOINT;
-- Настройка
ALTER SYSTEM SET wal_level = 'replica';
ALTER SYSTEM SET max_wal_size = '1GB';
Восстановление
WAL позволяет восстанавливать базу данных до точки во времени (point-in-time recovery):
# Восстановление до точки во времени
pg_restore -h localhost -d mydb backup.dump
# Восстановление из WAL
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2025-01-01 00:00:00'
SQLite WAL
-- Включить WAL mode
PRAGMA journal_mode = WAL;
-- Synchronous режим
PRAGMA synchronous = NORMAL; # 0=off, 1=normal, 2=full, 3=extra
-- Checkpoint
PRAGMA wal_checkpoint(TRUNCATE);
Уровни durability
// SQLite sync modes
// NORMAL: fsync() только для commit record
// FULL: fsync() для каждой записи (безопаснее, медленнее)
// EXTRA: ещё один fsync() (самый безопасный)
Реализация в приложении
// Простой WAL на Node.js
const fs = require('fs');
const path = require('path');
class WAL {
constructor(dir) {
this.dir = dir;
this.currentFile = 0;
this.buffer = [];
}
async append(record) {
// Добавить в буфер
this.buffer.push({
lsn: this.getNextLSN(),
timestamp: Date.now(),
data: record
});
// Флюш в файл при накоплении
if (this.buffer.length >= 100) {
await this.flush();
}
}
async flush() {
const filepath = path.join(this.dir, `wal-${this.currentFile}.log`);
const data = this.buffer.map(b => JSON.stringify(b)).join('\n');
await fs.promises.appendFile(filepath, data + '\n');
this.buffer = [];
}
}
ARIES
Algorithm for Recovery and Isolation Exploiting Semantics — алгоритм WAL в IBM DB2, Oracle, SQL Server.
Компоненты
- Log Sequence Number (LSN) — уникальный ID записи
- Transaction Table — активные транзакции
- Dirty Page Table — модифицированные страницы
Фазы восстановления
- Analysis — определить точку восстановления
- Redo — проигрывание лога вперёд
- Undo — откат незакоммиченных транзакций
Performance considerations
Batch commits
// Вместо записи на каждый запрос
await db.query('BEGIN');
for (const op of operations) {
await db.query(op); // не пишет в WAL сразу
}
await db.query('COMMIT'); // один fsync
WAL shipping
-- PostgreSQL репликация через WAL
-- Master
ALTER SYSTEM SET synchronous_standby_names = 'replica1';
-- Standby
recovery_target_timeline = 'latest'
Заключение
WAL — основа надёжности БД. Понимание принципов помогает правильно настраивать и оптимизировать базы данных.