Журнал предзаписи
Содержание
1. Обзор
По умолчанию SQLite реализует атомарные операции коммита и отката с помощью журнала отката. Начиная с версии 3.7.0 (2010-07-21), доступен новый режим "Журнал предзаписи" (далее "WAL").
Использование WAL вместо журнала отката имеет свои преимущества и недостатки. Преимущества включают:
- WAL значительно быстрее в большинстве сценариев.
- WAL обеспечивает большую конкурентность, так как чтение не блокирует запись, и запись не блокирует чтение. Чтение и запись могут происходить одновременно.
- Операции ввода-вывода на диск при использовании WAL, как правило, являются последовательными.
- WAL использует значительно меньше операций fsync() и поэтому менее уязвим к проблемам на системах, где вызов системы fsync() некорректен.
Но существуют и недостатки:
- Все процессы, использующие базу данных, должны находиться на одном компьютере; WAL не работает с сетевыми файловыми системами. Это связано с тем, что WAL требует, чтобы все процессы делили небольшое количество памяти, а процессы на разных компьютерах очевидно не могут делиться памятью друг с другом.
- Транзакции, включающие изменения в нескольких присоединённых базах данных (ATTACH), атомны для каждой отдельной базы данных, но не являются атомными для всех баз данных в совокупности.
- Невозможно изменить размер страницы (page_size) после перехода в режим WAL, ни на пустой базе данных, ни с помощью VACUUM, ни при восстановлении из резервной копии с использованием API резервного копирования. Для изменения размера страницы необходимо использовать режим журнала отката.
-
Невозможно открыть базы данных WAL только для чтения. Процесс открытия должен иметь права записи для файла общей памяти "Начиная с версии 3.22.0 (2018-01-22), базу данных WAL только для чтения можно открыть, если файлы-shm" wal-index, если он существует, или права на запись в каталоге, содержащем файл базы данных, если файла "-shm" нет.-shmи-walуже существуют или могут быть созданы, или база данных является неизменяемой. - WAL может быть немного медленнее (возможно, на 1% или 2%) по сравнению с традиционным подходом с журналом отката в приложениях, которые в основном читают и редко пишут.
- Для каждой базы данных ассоциируется дополнительный квазипостоянный файл "
-wal" и файл общей памяти "-shm", что может сделать SQLite менее привлекательным в качестве формата файлов приложения. - Существует дополнительная операция – точка восстановления, которая, хотя по умолчанию автоматическая, требует внимания разработчиков приложений.
-
WAL лучше всего работает с меньшими транзакциями. WAL плохо работает с очень большими транзакциями. Для транзакций больше 100 мегабайт традиционные режимы журнала отката, вероятно, будут быстрее. Для транзакций свыше гигабайта режим WAL может завершиться ошибкой ввода-вывода или заполнения диска. Рекомендуется использовать один из режимов журнала отката для транзакций больше нескольких десятков мегабайт.Начиная с версии 3.11.0 (2016-02-15), режим WAL работает так же эффективно с большими транзакциями, как и режим отката.
2. Как работает WAL
Традиционный журнал отката работает, записывая копию исходных данных базы данных без изменений в отдельный файл журнала отката, а затем записывая изменения непосредственно в файл базы данных. В случае сбоя или ROLLBACK исходное содержимое журнала отката восстанавливается в файл базы данных, возвращая его в исходное состояние. COMMIT происходит при удалении журнала отката.
Подход WAL меняет это. Исходное содержимое сохраняется в файле базы данных, а изменения добавляются в отдельный файл WAL. COMMIT происходит при добавлении в WAL специальной записи, указывающей на коммит. Таким образом, COMMIT может произойти, не записывая в исходную базу данных, что позволяет читателям продолжать работу с исходной неизменённой базой данных, в то время как изменения одновременно коммитингуются в WAL. Несколько транзакций могут быть добавлены в конец одного файла WAL.
2.1. Точки восстановления
Конечно, в конечном итоге необходимо перенести все транзакции из файла WAL обратно в исходную базу данных. Перенос транзакций из файла WAL в базу данных называется "точкой восстановления".
Другой способ понять разницу между журналом отката и журналом предзаписи заключается в том, что в подходе с журналом отката есть две основные операции: чтение и запись, а с журналом предзаписи — теперь три: чтение, запись и точка восстановления.
По умолчанию SQLite выполняет автоматическую точку восстановления, когда файл WAL достигает порога в 1000 страниц. (Опция компиляции SQLITE_DEFAULT_WAL_AUTOCHECKPOINT может быть использована для указания другого значения по умолчанию.) Приложения, использующие WAL, не должны ничего делать для выполнения этих точек восстановления. Но если они захотят, приложения могут настроить порог автоматической точки восстановления. Или они могут отключить автоматические точки восстановления и запускать их во время простоев или в отдельном потоке или процессе.
2.2. Конкурентность
Когда операция чтения начинается на базе данных в режиме WAL, она сначала запоминает расположение последней корректной записи коммита в WAL. Назовем эту точку "точкой окончания". Поскольку WAL может расти и добавлять новые записи коммита, в то время как различные читатели подключаются к базе данных, у каждого читателя может быть своя точка окончания. Но для любого конкретного читателя точка окончания неизменна в течение всей транзакции, гарантируя, что одна транзакция чтения видит содержимое базы данных только в один момент времени.
Когда читателю требуется страница содержимого, он сначала проверяет WAL, чтобы увидеть, присутствует ли эта страница там, и если да, то извлекает последнюю копию страницы, которая появляется в WAL перед точкой окончания читателя. Если копия страницы не существует в WAL перед точкой окончания читателя, то страница считывается из исходного файла базы данных. Читатели могут существовать в отдельных процессах, поэтому для предотвращения принудительного сканирования всей WAL каждым читателем (файл WAL может вырасти до нескольких мегабайт, в зависимости от того, как часто выполняются точки восстановления), поддерживается структура данных "wal-index" в общей памяти, которая помогает читателям быстро находить страницы в WAL с минимальным объемом ввода-вывода. Индекс wal значительно улучшает производительность читателей, но использование общей памяти означает, что все читатели должны существовать на одном компьютере. Именно поэтому реализация журнала предзаписи не работает с сетевыми файловыми системами.
Записывающие процессы просто добавляют новое содержимое в конец файла WAL. Поскольку действия записывающих процессов никак не влияют на действия читающих процессов, запись и чтение могут выполняться одновременно. Однако, так как существует только один файл WAL, в один момент времени может быть только один записывающий процесс.
Операция точки восстановления берет содержимое из файла WAL и переносит его обратно в исходный файл базы данных. Точка восстановления может выполняться параллельно с читателями, однако точка восстановления должна остановиться, когда достигнет страницы в WAL, которая находится за точкой окончания любого текущего читателя. Точка восстановления должна остановиться на этом этапе, потому что в противном случае она может перезаписать часть файла базы данных, которую читатель активно использует. Точка восстановления запоминает (в индексе wal), на каком этапе она остановилась, и на следующей итерации продолжит перемещать содержимое из WAL в базу данных с того места, где она остановилась.
Таким образом, длительная транзакция чтения может препятствовать прогрессу точки восстановления. Но, предположительно, любая транзакция чтения в конечном итоге завершится, и точка восстановления сможет продолжить работу.
Всякий раз, когда происходит операция записи, записывающий процесс проверяет, насколько продвинулась точка восстановления, и если все WAL перенесено в базу данных и синхронизировано, и если читатели не используют WAL, то записывающий процесс перемотает WAL назад к началу и начнет помещать новые транзакции в начало WAL. Этот механизм предотвращает бесконтрольный рост файла WAL.
2.3. Учет производительности
Транзакции записи очень быстры, так как они требуют записи содержимого только один раз (вместо двух раз, как при использовании журнала отката), а также потому, что все записи последовательные. Кроме того, синхронизация содержимого на диске не требуется, пока приложение готово пожертвовать целостностью данных после отключения питания или жесткой перезагрузки. (Записывающие процессы синхронизируют WAL при каждом коммите транзакции, если PRAGMA synchronous установлено в FULL, но пропускают эту синхронизацию, если PRAGMA synchronous установлено в NORMAL.)
С другой стороны, производительность чтения ухудшается по мере роста файла WAL, так как каждый читатель должен проверить файл WAL, чтобы найти содержимое, и время, необходимое для проверки файла WAL, пропорционально размеру файла WAL. Индекс wal помогает найти содержимое в файле WAL гораздо быстрее, но производительность все равно падает с увеличением размера файла WAL. Таким образом, для поддержания хорошей производительности чтения важно поддерживать небольшой размер файла WAL, выполняя точки восстановления через регулярные интервалы.
Для предотвращения повреждения базы данных после отключения питания или жёсткого перезапуска, операции проверок (checkpointing) требуют синхронизации. Журнал транзакций (WAL) должен быть синхронизирован с постоянным хранилищем перед перемещением содержимого из журнала в базу данных, а файл базы данных должен быть синхронизирован перед сбросом журнала. Проверка также требует большего количества операций поиска. Программа проверки (checkpointer) старается выполнить как можно больше последовательных записей страниц в базу данных (страницы передаются из журнала в базу данных в порядке возрастания), но даже в этом случае, как правило, будет много операций поиска между записями страниц. Эти факторы приводят к тому, что операции проверки выполняются медленнее, чем операции записи.
По умолчанию, последовательные операции записи позволяют увеличивать размер журнала транзакций (WAL) до примерно 1000 страниц, а затем для каждой последующей операции COMMIT запускается операция проверки до тех пор, пока размер журнала не станет меньше 1000 страниц. По умолчанию, проверка будет запускаться автоматически тем же потоком, который выполняет COMMIT, который увеличивает размер журнала. Это приводит к тому, что большинство операций COMMIT выполняются очень быстро, но изредка (те, которые вызывают проверку) выполнение COMMIT замедляется. Если этот эффект нежелателен, приложение может отключить автоматическую проверку и запускать периодические проверки в отдельном потоке или процессе. (Ссылки на команды и интерфейсы для выполнения этого показаны ниже.)
Обратите внимание, что при значении PRAGMA synchronous установленной в NORMAL, операция проверки является единственной операцией, которая выполняет барьер или синхронизацию ввода-вывода (fsync() в Unix или FlushFileBuffers() в Windows). Поэтому, если приложение запускает проверку в отдельном потоке или процессе, основной поток или процесс, выполняющий запросы и обновления базы данных, никогда не будет блокироваться на операции синхронизации. Это помогает предотвратить «зависание» приложений, работающих на загруженном накопителе. Недостатком такой конфигурации является то, что транзакции больше не являются долговременными и могут быть отменены после сбоя питания или жёсткого сброса.
Заметьте также, что существует компромисс между средней скоростью чтения и средней скоростью записи. Для максимальной скорости чтения желательно, чтобы журнал транзакций был как можно меньше, и поэтому проверки необходимо проводить чаще, возможно, после каждой операции COMMIT. Для максимальной скорости записи желательно распределить затраты каждой проверки на как можно большее количество записей, то есть проводить проверки реже и позволять журналу транзакций расти как можно больше перед каждой проверкой. Таким образом, решение о частоте проверок может варьироваться в зависимости от приложения в зависимости от требований к скорости чтения и записи приложения. По умолчанию проверка выполняется, когда журнал транзакций достигает 1000 страниц, и эта стратегия, похоже, хорошо работает в тестовых приложениях на рабочих станциях, но другие стратегии могут работать лучше на разных платформах или для различных задач.
3. Активация и настройка режима WAL
Подключение к базе данных SQLite по умолчанию имеет journal_mode=DELETE. Для переключения в режим WAL используйте следующую команду:
PRAGMA journal_mode=WAL;
Команда journal_mode возвращает строку, которая представляет новый режим журнала. При успехе команда вернёт строку "wal". Если преобразование в режим WAL не удалось (например, если VFS не поддерживает необходимые примитивы совместной памяти), то режим журнализации останется без изменений, а возвращаемая строка будет предыдущим режимом журнализации (например, "delete").
3.1. Автоматическая проверка
По умолчанию, SQLite автоматически выполняет проверку при каждой операции COMMIT, которая приводит к увеличению размера файла журнала транзакций (WAL) до 1000 страниц или более, или при закрытии последнего подключения к файлу базы данных. Конфигурация по умолчанию предназначена для работы со большинством приложений. Но программы, которые хотят иметь больший контроль, могут принудительно выполнить проверку, используя команду wal_checkpoint pragma или вызвав C-интерфейс sqlite3_wal_checkpoint(). Пороговое значение автоматической проверки может быть изменено, или автоматическая проверка может быть полностью отключена с помощью команды wal_autocheckpoint pragma или вызвав C-интерфейс sqlite3_wal_autocheckpoint(). Программа также может использовать sqlite3_wal_hook() для регистрации обратного вызова, который будет вызываться всякий раз, когда любая транзакция сохраняется в журнале WAL. Этот обратный вызов может затем вызвать sqlite3_wal_checkpoint() или sqlite3_wal_checkpoint_v2() на основе любых критериев, которые он считает подходящими. (Механизм автоматической проверки реализован как простой оболочкой вокруг sqlite3_wal_hook().)
3.2. Проверки, инициированные приложением
Приложение может инициировать проверку, используя любое записьное подключение к базе данных, просто вызвав sqlite3_wal_checkpoint() или sqlite3_wal_checkpoint_v2(). Существует три типа проверок, которые различаются по своей агрессивности: PASSIVE, FULL и RESTART. По умолчанию используется стиль проверки PASSIVE, который выполняет как можно больше работы без вмешательства в другие подключения к базе данных и может не завершиться, если есть одновременные читатели или писатели. Все проверки, инициированные sqlite3_wal_checkpoint() и автоматическим механизмом проверки, являются PASSIVE. Проверки FULL и RESTART пытаются завершить проверку и могут быть инициированы только вызовом sqlite3_wal_checkpoint_v2(). См. документацию sqlite3_wal_checkpoint_v2() для получения дополнительной информации о проверках FULL и RESET.
3.3. Сохранение режима WAL
В отличие от других режимов журнализации, PRAGMA journal_mode=WAL сохраняется. Если процесс устанавливает режим WAL, а затем закрывает и повторно открывает базу данных, база данных будет возвращена в режим WAL. В противоположность этому, если процесс устанавливает (например) PRAGMA journal_mode=TRUNCATE, а затем закрывает и повторно открывает базу данных, база данных будет восстановлена в режиме отмены DELETE по умолчанию, а не в предыдущем режиме TRUNCATE.
Сохранение режима WAL означает, что приложения могут быть переведены на использование SQLite в режиме WAL без внесения изменений в само приложение. Достаточно просто выполнить "PRAGMA journal_mode=WAL;" на файлах базы данных с помощью интерпретатора командной строки или другого инструмента, а затем перезапустить приложение.
Режим журнала WAL будет установлен для всех подключений к одному файлу базы данных, если он установлен для одного подключения.
4. Файл WAL
Пока подключение к базе данных в режиме WAL открыто, SQLite поддерживает дополнительный файл журнала, называемый "Журнал записей вперёд" или "Файл WAL". Имя этого файла обычно совпадает с именем файла базы данных с дополнительным "-wal" суффиксом, хотя могут применяться другие правила именования, если SQLite скомпилирован с SQLITE_ENABLE_8_3_NAMES.
Файл WAL существует до тех пор, пока открыто хотя бы одно подключение к базе данных. Обычно файл WAL автоматически удаляется при закрытии последнего подключения к базе данных. Однако, если последний процесс, имевший открытую базу данных, завершился без корректного закрытия подключения к базе данных, или если используется файловый контроль SQLITE_FCNTL_PERSIST_WAL, файл WAL может остаться на диске после закрытия всех подключений к базе данных. Файл WAL является частью постоянного состояния базы данных и должен храниться вместе с базой данных, если база данных копируется или перемещается. Если файл базы данных отделён от файла WAL, транзакции, ранее сохранённые в базе данных, могут быть потеряны или файл базы данных может быть повреждён. Единственный безопасный способ удалить файл WAL — открыть файл базы данных с помощью одного из интерфейсов sqlite3_open(), а затем немедленно закрыть базу данных с помощью sqlite3_close().
Формат файла WAL точно определён и кросс-платформенный.
5. Базы данных только для чтения
Старые версии SQLite не могли читать базу данных в режиме WAL, которая была только для чтения. Другими словами, для чтения базы данных в режиме WAL требовался доступ для записи. Это ограничение было снято начиная с SQLite версии 3.22.0 (2018-01-22).
В более новых версиях SQLite база данных в режиме WAL на носителе только для чтения или база данных в режиме WAL, для которой отсутствует разрешение на запись, всё ещё может быть прочитана, если выполнено одно или несколько из следующих условий:
- Файлы
-shmи-walуже существуют и доступны для чтения - Есть разрешение на запись в каталоге, содержащем базу данных, чтобы файлы
-shmи-walмогли быть созданы. - Подключение к базе данных открывается с использованием параметра запроса immutable.
Хотя открыть базу данных только для чтения в режиме WAL возможно, рекомендуется преобразовать её в PRAGMA journal_mode=DELETE перед записью образа базы данных SQLite на носитель только для чтения.
6. Избегание чрезмерно больших файлов WAL
В нормальных случаях новое содержимое добавляется в файл WAL до тех пор, пока файл WAL не накопит примерно 1000 страниц (и, следовательно, около 4 МБ), после чего автоматически выполняется проверка, и файл WAL переиспользуется. Проверка обычно не обрезает файл WAL (если не установлено ограничение размера журнала). Вместо этого, она просто заставляет SQLite начать перезапись файла WAL с начала. Это делается потому, что обычно быстрее перезаписать существующий файл, чем добавить к нему. Когда последнее подключение к базе данных закрывается, это подключение выполняет последнюю проверку, а затем удаляет файл WAL и связанный с ним файл совместно используемой памяти для очистки диска.
Таким образом, в подавляющем большинстве случаев приложениям не нужно беспокоиться о файле WAL. SQLite автоматически позаботится о нём. Но существует возможность привести SQLite в состояние, когда файл WAL будет неограниченно расти, вызывая избыточное использование дискового пространства и медленные скорости запросов. Следующие пункты перечисляют некоторые способы, как это может произойти и как этого избежать.
Отключение механизма автоматических контрольных точек. В стандартной конфигурации SQLite создаёт контрольную точку в файле WAL по завершении каждой транзакции, если размер файла WAL превышает 1000 страниц. Однако существуют варианты компиляции и выполнения, которые позволяют отключить или отложить создание этой автоматической контрольной точки. Если приложение отключает автоматическое создание контрольных точек, то ничего не мешает файлу WAL чрезмерно разрастаться.
-
Голод контрольных точек. Контрольная точка может завершиться и обновить файл WAL только в том случае, если другие подключения к базе данных не используют файл WAL. Если у другого подключения открыта транзакция чтения, контрольная точка не может обновить файл WAL, так как это может удалить данные, которые читает другой процесс. Контрольная точка выполнит как можно больше работы, не нарушая чтения, но не сможет завершиться полностью. Контрольная точка возобновит работу с того места, где остановилась, после следующей транзакции записи. Этот процесс повторяется до тех пор, пока какая-то контрольная точка не завершит работу.
Однако, если база данных имеет много одновременных перекрывающихся операций чтения и всегда есть хотя бы один активный процесс чтения, то никакие контрольные точки не смогут завершиться, и в результате файл WAL будет неограниченно расти.
Эта ситуация может быть избегнута, если обеспечить «пробелы чтения» — моменты, когда никакие процессы не читают данные из базы данных, и контрольные точки выполняются в эти моменты. В приложениях с множеством одновременных операций чтения можно также рассмотреть возможность выполнения ручных контрольных точек с параметрами SQLITE_CHECKPOINT_RESTART или SQLITE_CHECKPOINT_TRUNCATE, которые гарантируют, что контрольная точка завершится перед возвратом. Недостатком использования SQLITE_CHECKPOINT_RESTART и SQLITE_CHECKPOINT_TRUNCATE является то, что процессы чтения могут заблокироваться во время выполнения контрольной точки.
-
Очень большие транзакции записи. Контрольная точка может завершиться только тогда, когда нет других транзакций, что означает, что файл WAL не может быть обновлён в середине транзакции записи. Поэтому большая операция изменения большой базы данных может привести к большому файлу WAL. Файл WAL будет подвергнут контрольной точке после завершения транзакции записи (при условии, что нет других процессов чтения, которые блокируют её), но тем временем он может стать очень большим.
Начиная с SQLite версии 3.11.0 (2016-02-15), размер файла WAL для одной транзакции должен быть пропорционален самой транзакции. Изменённые страницы должны записываться в файл WAL только один раз. Однако в более ранних версиях SQLite одна и та же страница может записываться в файл WAL несколько раз, если транзакция становится больше, чем кэш страниц.
7. Реализация совместно используемой памяти для индекса WAL
Индекс wal-index реализуется с помощью обычного файла, который отображается в память для повышения надёжности. Ранние (дорелизные) реализации режима WAL хранили индекс wal-index в динамической совместно используемой памяти, такой как файлы, созданные в /dev/shm на Linux или /tmp на других Unix-системах. Проблема этого подхода заключается в том, что процессы с другим корневым каталогом (изменённым с помощью chroot) увидят разные файлы и, следовательно, будут использовать разные области совместно используемой памяти, что приведёт к повреждению базы данных. Другие методы создания безымянных блоков совместно используемой памяти не являются портативными для различных вариантов Unix. И мы не нашли никакого метода создания безымянных блоков совместно используемой памяти в Windows. Единственный способ гарантировать, что все процессы, обращающиеся к одному файлу базы данных, используют одну и ту же область совместно используемой памяти, — это создание совместно используемой памяти путём отображения файла в память в том же каталоге, что и сама база данных.
Использование обычного файла на диске для предоставления совместно используемой памяти имеет недостаток, заключающийся в том, что он может выполнять ненужную операцию ввода-вывода на диск, записывая совместно используемую память на диск. Однако разработчики считают это несерьёзной проблемой, так как размер индекса wal-index редко превышает 32 КБ и никогда не синхронизируется. Кроме того, файл-основа индекса wal-index удаляется при отключении последнего подключения к базе данных, что часто предотвращает выполнение реальных операций ввода-вывода на диск.
Специализированные приложения, для которых стандартная реализация совместно используемой памяти неприемлема, могут разработать альтернативные методы с помощью пользовательского VFS. Например, если известно, что к конкретной базе данных будут обращаться только потоки в пределах одного процесса, индекс wal-index можно реализовать с помощью памяти кучи вместо реальной совместно используемой памяти.
8. Использование WAL без совместно используемой памяти
Начиная с SQLite версии 3.7.4 (2010-12-07), WAL-базы данных могут создаваться, читаться и записываться даже при отсутствии совместно используемой памяти, если режим блокировки locking_mode установлен в EXCLUSIVE до первой попытки доступа. Другими словами, процесс может взаимодействовать с базой данных WAL без использования совместно используемой памяти, если этот процесс гарантированно является единственным процессом, обращающимся к базе данных. Эта функция позволяет создавать, читать и записывать WAL-базы данных с помощью устаревших VFS, которые не имеют методов совместно используемой памяти «версии 2» xShmMap, xShmLock, xShmBarrier и xShmUnmap в объекте sqlite3_io_methods.
Если режим блокировки EXCLUSIVE установлен до первого доступа к базе данных в режиме WAL, то SQLite никогда не пытается вызвать какие-либо методы совместно используемой памяти, и, следовательно, индекс wal-index совместно используемой памяти никогда не создаётся. В этом случае подключение к базе данных остаётся в режиме EXCLUSIVE, пока режим журнала равен WAL; попытки изменения режима блокировки с помощью "PRAGMA locking_mode=NORMAL;" являются бездействиями. Единственный способ выйти из режима EXCLUSIVE — сначала выйти из режима журнала WAL.
Если режим блокировки NORMAL действует при первом доступе к базе данных в режиме WAL, то создаётся индекс wal-index совместно используемой памяти. Это означает, что основной VFS должен поддерживать методы совместно используемой памяти «версии 2». Если VFS не поддерживает методы совместно используемой памяти, то попытка открыть базу данных, которая уже находится в режиме WAL, или попытка преобразовать базу данных в режим WAL, завершится неудачей. Пока ровно одно подключение использует индекс wal-index совместно используемой памяти, режим блокировки можно свободно изменять между NORMAL и EXCLUSIVE. Это происходит только тогда, когда индекс wal-index совместно используемой памяти отсутствует, когда режим блокировки EXCLUSIVE установлен перед первым доступом к базе данных в режиме WAL, что режим блокировки застревает в EXCLUSIVE.
9. Иногда запросы возвращают SQLITE_BUSY в режиме WAL
Вторым преимуществом режима WAL является то, что записывающие потоки не блокируют читающие, а читающие не блокируют записывающие. Это в основном верно. Но существуют некоторые редкие случаи, когда запрос к базе данных в режиме WAL может вернуть SQLITE_BUSY, поэтому приложения должны быть готовы к такому случаю.
Случаи, когда запрос к базе данных в режиме WAL может вернуть SQLITE_BUSY, включают следующее:
Если другое подключение к базе данных имеет режим базы данных открытым в режиме исключительной блокировки, то все запросы к базе данных вернут SQLITE_BUSY. Например, Chrome и Firefox открывают свои файлы базы данных в режиме исключительной блокировки, поэтому попытки чтения баз данных Chrome или Firefox во время работы приложений столкнутся с этой проблемой.
Когда закрывается последнее подключение к определённой базе данных, это подключение получит исключительную блокировку на короткое время, пока оно очищает файлы WAL и совместно используемой памяти. Если будет сделана отдельная попытка открыть и запросить базу данных, пока первое подключение всё ещё находится в процессе очистки, второе подключение может получить ошибку SQLITE_BUSY.
Если последнее подключение к базе данных потерпело сбой, то первое новое подключение для открытия базы данных начнёт процесс восстановления. Исключительная блокировка удерживается во время восстановления. Таким образом, если третье подключение к базе данных попытается подключиться и выполнить запрос, в то время как второе подключение выполняет восстановление, третье подключение получит ошибку SQLITE_BUSY.
10. Обратная совместимость
Формат файла базы данных не изменяется для режима WAL. Однако файл WAL и индекс wal-index представляют собой новые понятия, поэтому более старые версии SQLite не будут знать, как восстановить аварийно завершившуюся базу данных SQLite, которая работала в режиме WAL во время аварии. Чтобы предотвратить попытки более старых версий SQLite (до версии 3.7.0, 2010-07-22) восстановить базу данных в режиме WAL (и ухудшить ситуацию), номера версии формата файла базы данных (байты 18 и 19 в заголовке базы данных) увеличиваются с 1 до 2 в режиме WAL. Таким образом, если более старая версия SQLite попытается подключиться к базе данных SQLite, которая работает в режиме WAL, она сообщит об ошибке, подобной «файл зашифрован или не является базой данных».
Можно явно выйти из режима WAL, используя такой оператор pragma:
PRAGMA journal_mode=DELETE;
Намеренное изменение из режима WAL изменяет номера версии формата файла базы данных обратно на 1, чтобы более старые версии SQLite снова могли получить доступ к файлу базы данных.
Эта страница была последним раз изменена 2024-07-14 23:07:20 UTC
SQLite is in the Public Domain.
https://sqlite.org/wal.html