← На главную

SQLite могут повредить: перезапись, баги и сбои

26.06.2026 18:05 · hackernews

SQLite — одна из самых надёжных баз данных. Если приложение или ОС упали, а потом включилось питание, SQLite автоматически откатит partially written transaction при следующем доступе к файлу. Никаких действий от пользователя не требуется. Но устойчивость не означает неуязвимость. Статья детально описывает, как можно (случайно) испортить SQLite-базу.

Первая и самая очевидная причина — другой процесс или поток перезаписал файл поверх. SQLite — это обычный файл на диске. Любой процесс может открыть его и записать туда мусор. Защититься от этого библиотека не может.

Классический пример: файловый дескриптор закрыли, переоткрыли на SQLite-базу, а старый код продолжал в него писать. Так в 2013 году в репозитории Fossil DVCS assert() писал сообщение об ошибке в файловый дескриптор 2 (stderr), но тот уже был переоткрыт SQLite — и сообщение попало прямо в базу. Начиная с версии 3.8.1 SQLite отказывается использовать низкие номера дескрипторов, но это не панацея.

Проблемы с блокировками — ещё один огромный пласт. SQLite relies on file locks for coordination. Но POSIX advisory locking — штука коварная. Любой close() в любом потоке снимает все блокировки на файл для всего процесса. Если сторонний поток просто открыл базу, прочитал 16 байт и закрыл, он может убить блокировки всех других потоков, которые об этом не узнают. С версии 3.51.0 SQLite добавил дополнительную защиту для WAL mode, но разработчиков предупреждают: никогда не делайте close() на файле базы, пока есть открытые соединения, даже в других потоках.

Ещё одна грабля — линковка двух копий SQLite в одно приложение. У каждой будет свой глобальный список открытых файлов, и они не увидят блокировки друг друга. В статье упоминается коммерческий продукт, который мучился с редкими коррапшнами на Linux и Mac именно из-за этого. Решение — линковать одну копию.

Проблемы с синхронизацией (sync). Большинство consumer-grade дисков врут: сообщают, что данные записаны, когда они ещё в track buffer. При потере питания порядок записи может нарушиться. USB-флешки — особенно злостные лгуны: COMMIT возвращается, LED ещё мигает, выдёргиваешь — получаешь коррапшн. Выход — использовать WAL mode и checkpoint как можно реже.

Аппаратные сбои тоже случаются: бит может перевернуться в секторе, flash-контроллеры с плохой wear-leveling могут повредить случайные файлы, а fake capacity USB-флешки при переполнении перезаписывают соседние данные.

Повреждение памяти в приложении (stray pointer, buffer overflow) тоже может испортить базу, особенно при memory-mapped I/O.

Статья перечисляет и баги самого SQLite, которые в разное время приводили к коррапшну. Например, race condition при одновременной записи в WAL-mode (WAL-reset bug, жил с версии 3.7.0 до 3.51.2), проблемы при переключении между rollback и WAL, ошибки при recovery на Windows, утечка страниц из free list. Большинство уже исправлено, но подчёркивается: ни один софт не идеален, и полагаться на отключение защит (например, PRAGMA synchronous=OFF) — плохая идея.

Читать оригинал →