WAL: журнал опережающей записи

28 октября 2026 · ~9 мин чтения

база-данных sqlite хранение конкурентность

WAL: журнал опережающей записи

WAL (write-ahead log, журнал опережающей записи) — способ фиксировать изменения базы сначала в отдельном журнале, а уже потом переносить их в основной файл. В SQLite это позволяет читателям продолжать видеть согласованный снимок данных, пока другая транзакция записывает новые изменения.

История

Идея опережающей записи возникла не в SQLite: журналы восстановления давно применяются в системах управления базами данных. Если работа прервалась, журнал позволяет выяснить, какие операции успели завершиться, и восстановить согласованное состояние. Ключевой принцип write-ahead logging: информацию, необходимую для восстановления, записывают до того, как считают соответствующее изменение базы устойчивым.

SQLite появилась в 2000 году. Её создал Д. Ричард Хипп (D. Richard Hipp); впоследствии она стала самостоятельной встраиваемой базой данных. Изначально SQLite применяла rollback journal (журнал отката): до изменения основного файла сохраняла прежние версии страниц, чтобы при незавершённой транзакции можно было вернуться назад. Это решало задачу восстановления, но читатели и писатель иногда мешали друг другу.

Режим WAL появился в SQLite 3.7.0 в 2010 году. Новые версии страниц стали дописываться в журнал, а основной файл обновляться позже. Так запросы чтения смогли чаще выполняться параллельно с записью. Дальнейшее развитие касалось, в частности, checkpoint (контрольной точки), управления блокировками и восстановления после прерывания. WAL сейчас является режимом журналирования SQLite, а не отдельной базой или сервером.

Что это такое

Основной файл SQLite содержит страницы таблиц и индексов. Когда транзакция меняет данные в режиме WAL, новые версии страниц сначала попадают в соседний файл журнала. Изменение считается видимым новым читателям после фиксации транзакции. Позднее checkpoint переносит накопленное из журнала в основной файл. Поэтому подтверждённые изменения не обязаны немедленно появляться в самом файле базы.

Зачем это полезно? Читателю нужна стабильная картина данных на протяжении операции, а писателю нужно сохранить обновления. WAL позволяет читателю продолжать читать прежний согласованный снимок, пока писатель добавляет новые страницы в конец журнала. Следующее чтение после фиксации записи может получить более свежий снимок. Это улучшает совместную работу операций разных типов без необходимости превращать SQLite в сетевой сервер.

Но «читатели не блокируют писателей» — полезное упрощение, а не обещание отсутствия любых конфликтов. Параллельно может работать только одна транзакция записи; другие писатели ждут или получают ошибку занятости. Долгие читатели могут мешать очистке WAL. В SQLite существуют ограничения для конкретных транзакций, запросов, файловых систем и соединений: режим не следует включать только потому, что название звучит быстрее.

WAL отличается от rollback journal направлением сохранения изменений. Rollback journal хранит данные для отмены незавершённой записи в основной файл. WAL хранит новые версии для последующего переноса в основной файл. Оба режима помогают обеспечивать атомарность транзакций; ни один не подменяет резервное копирование, управление доступом или синхронизацию с удалённым сервером.

Аналогии из жизни

Черновик изменений к книге. Редактор фиксирует правки отдельно, пока читатели пользуются прежним изданием; позднее правки попадают в основной экземпляр. Аналогия объясняет отдельное хранение новых страниц до checkpoint. Где ломается: журнал не является свободным текстом предложений, а читатели SQLite могут увидеть зафиксированные изменения до переноса в основной файл.

Кассовая лента. Покупки последовательно печатаются на ленте, а итоговые остатки обновляются позже. Если компьютер выключился, записи помогают восстановить произошедшее. Это показывает ценность журнала при сбое. Где ломается: обычная лента сама по себе не гарантирует атомарность нескольких операций и согласованные снимки для параллельных читателей.

Входящая почта. Новые письма поступают во входящий ящик, пока сотрудники читают уже рассортированные материалы; затем накопившееся разносят по папкам. Аналогия показывает разделение поступления и переноса изменений. Где ломается: WAL не ждёт «разбора всей почты», чтобы новые читатели увидели зафиксированную транзакцию; каждая операция чтения работает с определённым снимком.

Как это работает

Упрощённо рядом с app.db при использовании WAL могут появиться app.db-wal и app.db-shm. Первый файл содержит саму базу, второй — новые версии страниц, третий помогает соединениям координировать чтение журнала. Приложение не должно вручную менять эти служебные файлы: их жизненным циклом управляет SQLite.

  1. Писатель начинает транзакцию, получает доступ к записи и изменяет необходимые страницы. SQLite следит, чтобы другая запись не смешалась с текущей.
  2. Новые страницы попадают в WAL. В конце транзакции записывается отметка commit (фиксации). Пока её нет, незавершённые изменения не должны стать подтверждённым результатом для других соединений.
  3. Когда транзакция завершена, новый читатель может читать последние зафиксированные данные из основного файла и WAL. Уже работающий читатель продолжает видеть snapshot (снимок), соответствующий моменту начала его операции.
  4. Checkpoint переносит подходящие страницы журнала в основной файл. Это отдельная внутренняя работа, которая не меняет смысла пользовательских транзакций.
  5. Когда старые версии больше никому не нужны, журнал можно переиспользовать, сбросить или усечь в зависимости от режима checkpoint и состояния соединений.

Представь, что чтение началось в 10:00, а запись завершилась в 10:01. У начатого раньше чтения остаётся прежний согласованный снимок. Новое чтение после commit уже может увидеть изменения. Это свойство помогает не получить половину старого и половину нового результата. Границы транзакций важнее того, сколько миллисекунд прошло между запросами.

читатель: снимок A --------------------- завершение
писатель:          запись WAL -> commit -> checkpoint
файл базы:         версия A --------------> версия B

Checkpoint может запускаться автоматически по порогу накопленного WAL или явно приложением. Если читатель долго держит старый снимок, checkpoint не всегда может завершить работу и освободить все старые версии: файл WAL растёт. Поэтому полезно наблюдать за его размером и временем жизни транзакций, а не просто принудительно удалять служебные файлы.

Настройка synchronous задаёт компромисс между скоростью и сохранностью подтверждённых данных при внезапном отключении питания. В режиме WAL значение FULL предусматривает синхронизацию журнала при commit; NORMAL уменьшает число таких операций и сохраняет целостность базы, но последние подтверждённые транзакции могут потеряться при отказе питания. Фактические гарантии зависят также от устройства и ОС.

PRAGMA journal_mode = WAL;

После этой команды проверь возвращённый режим: запрос показывает, удалось ли его установить. Если ожидается конкуренция за запись, продумай обработку SQLITE_BUSY и busy_timeout; таймаут позволяет подождать освобождения блокировки, но не делает параллельные записи возможными. Открытые соединения следует корректно закрывать, а транзакции — завершать без неоправданных пауз.

Где встречается в обычной жизни

Пользователь обычно не видит WAL напрямую. Соседний с базой файл -wal сам по себе не является признаком повреждения: при активной работе он ожидаем.

Где встречается в IT и бизнесе

Кто пользуется

SQLite широко применяется в браузерах, мобильных и настольных приложениях, а также во встраиваемом ПО. Однако наличие SQLite у конкретного продукта не доказывает, что он включил именно WAL: режим зависит от настроек приложения. Поэтому было бы неверно приписывать всем пользователям SQLite работу именно с этим журналом.

Универсальной цифры «сколько запросов в секунду выдерживает WAL» нет. Пропускная способность зависит от размера транзакций, скорости хранилища, частоты синхронизаций, количества писателей и продолжительности чтения. Для решения о внедрении нужны измерения на устройстве и нагрузке, похожих на настоящие.

Альтернативы и конкуренты

Когда НЕ стоит использовать

Связанные понятия

Литература и источники

Где встретилось у меня

Понятие возникло при обсуждении хранения состояния в локальной базе и одновременного обслуживания операций чтения и записи. Контекст за вчера частично обрезан системным маркером, поэтому тема выбрана только по доступным записям.

Краткое резюме