Формат файла режима WAL
Содержание
В данном документе описываются низкоуровневые детали реализации режима WAL (Write-Ahead Logging) в системах Unix и Windows.
Отдельный описания формата файла [ссылка на файл] содержит подробности о структуре файла базы данных и файла журнала предварительной записи, используемого в режиме WAL. Однако детали протокола блокировок и формата индекса WAL намеренно опущены, поскольку эти детали остаются на усмотрение отдельных реализаций VFS. Данный документ заполняет эти недостающие детали для VFS Unix и Windows.
Для полноты некоторые сведения о формате, содержащиеся в документе описания формата файла [ссылка на файл] и других местах, дублируются здесь, когда они относятся к обработке в режиме WAL.
1.Файлы на диске
При активном использовании состояние базы данных в режиме WAL описывается тремя отдельными файлами:
- Основной файл базы данных с произвольным именем "X".
- Файл журнала предварительной записи, обычно имеющий имя "X-wal".
- Файл индекса WAL, обычно имеющий имя "X-shm".
1.1.Основной файл базы данных
Формат основного файла базы данных описан в документе [ссылка на файл]. Номера версий формата файла (в смещениях 18 и 19 в основном файле базы данных) должны оба быть равны 2, чтобы указать, что база данных находится в режиме WAL. Основной файл базы данных может иметь произвольное имя, разрешенное файловой системой. Специальные суффиксы файлов не требуются, хотя ".db", ".sqlite" и ".sqlite3" кажутся популярными вариантами.
1.2.Журнал предварительной записи или файл "-wal"
Файл журнала предварительной записи или "wal" представляет собой журнал прокрутки, который записывает транзакции, которые были подтверждены, но еще не применены к основной базе данных. Подробности о формате файла wal описаны в подразделе "Формат WAL" основного документа формата файла [ссылка на файл]. Файл wal имеет имя, образованное путем добавления четырех символов "-wal" в конец имени основного файла базы данных. За исключением файловых систем с поддержкой 8+3 символов, такие имена запрещены, и в этом случае суффикс файла меняется на ".WAL". Но поскольку файловые системы с поддержкой 8+3 символов встречаются все реже, этим исключительным случаем обычно можно пренебречь.
1.3.Файл индекса Wal или "-shm"
Файл индекса wal или "shm" фактически не используется как файл. Вместо этого отдельные клиенты базы данных используют mmap для отображения файла shm в общую память, чтобы координировать доступ к базе данных и использовать кэш для быстрого поиска фреймов в файле wal.
Имя файла shm образуется путем добавления четырех символов "-shm" к имени основного файла базы данных. Или, в случае файловых систем с поддержкой 8+3 символов, файл shm имеет суффикс ".SHM".
Файл shm не содержит никаких данных базы данных и не требуется для восстановления базы данных после сбоя. По этой причине первый клиент, подключившийся к неиспользуемой базе данных, обычно обрезает файл shm, если он существует. Поскольку содержимое файла shm не нужно сохранять после сбоя, файл shm никогда не синхронизируется с диском (fsync). Фактически, если бы существовал механизм, позволяющий SQLite сообщить операционной системе, что файл shm не должен сохраняться на диск, а должен храниться в кэше памяти, SQLite использовал бы этот механизм, чтобы избежать ненужной записи на диск, связанной с файлом shm. Однако такого механизма нет в стандартной POSIX.
Поскольку файл shm используется только для координации доступа между одновременными клиентами, файл shm опускается, если установлен режим эксклюзивной блокировки [ссылка на страницу с прагмой]. В режиме эксклюзивной блокировки SQLite использует кусковую память вместо отображаемого в память файла shm.
1.4.Жизненные циклы файлов
Когда база данных в режиме WAL активна, обычно существуют все три файла выше. За исключением файла индекса Wal, который опускается, если установлен режим эксклюзивной блокировки [ссылка на страницу с прагмой].
Если последний клиент, использующий базу данных, закрывается корректно, вызвав sqlite3_close(), то автоматически выполняется точка восстановления, чтобы передать все данные из файла wal в основную базу данных, и файлы shm и wal удаляются. Таким образом, когда к базе данных не подключен ни один клиент, обычно на диске существует только основной файл базы данных. Однако, если последний клиент не вызвал sqlite3_close() перед закрытием, или если последний отключившийся клиент был только для чтения, то завершающая операция очистки не выполняется, и файлы shm и wal могут остаться на диске даже когда база данных не используется.
1.5.Варианты
Когда установлена прагма PRAGMA locking_mode=EXCLUSIVE (режим эксклюзивной блокировки), только один клиент может иметь базу данных открытой в одно время. Поскольку только один клиент может использовать базу данных, файл shm опускается. Единственный клиент использует буфер в кусковой памяти в качестве замены отображаемому в память файлу shm.
Если клиент с правами чтения/записи вызывает sqlite3_file_control(SQLITE_FCNTL_PERSIST_WAL) перед завершением, то при завершении все равно выполняется точка восстановления, но файлы shm и wal не удаляются. Это позволяет последующим клиентам только для чтения подключаться и читать базу данных.
2.Формат файла индекса WAL
Файл индекса WAL или "shm" используется для координации доступа к базе данных несколькими клиентами и как кэш для быстрого поиска фреймов в файле wal.
Поскольку файл shm не участвует в восстановлении, файл shm не должен быть независимым от порядка байтов машины. Следовательно, числовые значения в файле shm записываются в родном порядке байтов компьютера-хоста, а не преобразуются в определённый кроссплатформенный порядок байтов, как это делается с основным файлом базы данных и файлом wal.
Файл shm состоит из одной или нескольких хэш-таблиц, где каждая хэш-таблица имеет размер 32768 байт. За исключением 136-байтового заголовка, выделенного в начале первой хэш-таблицы, поэтому первая хэш-таблица имеет размер только 32632 байта. Общий размер файла shm всегда кратен 32768. В большинстве случаев общий размер файла shm составляет ровно 32768 байт. Размер файла shm может увеличиться до нескольких хэш-таблиц только если файл wal становится очень большим (более 4079 фреймов). Поскольку по умолчанию порог автоматической точки восстановления WAL составляет 1000, файлы WAL редко достигают порога 4079, необходимого для увеличения файла shm.
2.1.Заголовок файла индекса WAL
Первые 136 байт файла shm составляют заголовок. Заголовок файла shm разделён на три основные части:
Разделы заголовка индекса WAL| Байты | Описание |
|---|---|
| 0..47 | Первая копия информации индекса WAL |
| 48..95 | Вторая копия информации индекса WAL |
| 96..135 | Информация о точке восстановления и блокировки |
Отдельные поля заголовка shm, за исключением значений соли, скопированных из заголовка WAL, являются беззнаковыми целыми числами в родном порядке байтов машины-хоста. Значения соли представляют собой точные копии из заголовка WAL и имеют тот порядок байтов, который используется в файле WAL.
Размер целых чисел может быть 8, 16, 32 или 64 бита. Далее следует подробное описание отдельных полей заголовка shm:
Подробности заголовка индекса WAL| Байты | Имя | Значение |
|---|---|---|
| 0..3 | iVersion | Номер версии формата индекса WAL. Всегда 3007000. |
| 4..7 | Неиспользуемое поле для выравнивания. Должно быть равно нулю. | |
| 8..11 | iChange | Беззнаковое целое число, увеличивающееся при каждой транзакции |
| 12 | isInit | Флаг "isInit". 1, если файл shm был инициализирован. |
| 13 | bigEndCksum | Истина, если файл WAL использует контрольные суммы в формате big-endian. 0, если используется little-endian. |
| 14..15 | szPage | Размер страницы базы данных в байтах или 1, если размер страницы равен 65536. |
| 16..19 | mxFrame | Количество допустимых и подтвержденных фреймов в файле WAL. |
| 20..23 | nPage | Размер файла базы данных в страницах. |
| 24..31 | aFrameCksum | Контрольная сумма последнего фрейма в файле WAL. |
| 32..39 | aSalt | Два значения соли, скопированные из заголовка файла WAL. Эти значения находятся в порядке байтов файла WAL, который может отличаться от родного порядка байтов машины. |
| 40..47 | aCksum | Контрольная сумма байтов с 0 по 39 этого заголовка. |
| 48..95 | Копия байтов с 0 по 47 этого заголовка. | |
| 96..99 | nBackfill | Количество фреймов WAL, которые уже были возвращены в базу данных предыдущими точками восстановления. |
| 100..119 | read-mark[0..4] | Пять "меток чтения". Каждая метка чтения - 32-битное беззнаковое целое число (4 байта). |
| 120..127 | Неиспользуемое пространство, зарезервированное под 8 файловых блокировок. | |
| 128..132 | nBackfillAttempted | Количество фреймов WAL, которые пытались быть возвращены, но которые могли не быть успешно возвращены. |
| 132..136 | Неиспользуемое пространство, зарезервированное для дальнейшего расширения. |
2.1.1. Поле mxFrame
32-битное беззнаковое целое число со смещением 16 (и повторённое со смещением 64) — это количество допустимых кадров в журнале WAL. Так как кадрам журнала WAL присваиваются номера, начиная с 1, mxFrame также является индексом последнего допустимого кадра подтверждения в WAL. Кадр подтверждения — это кадр, имеющий ненулевое значение «размер базы данных» в байтах с 4 по 7 заголовка кадра и указывающий на конец транзакции.
Когда поле mxFrame равно нулю, это указывает, что журнал WAL пуст, и всё содержимое должно быть получено непосредственно из файла базы данных.
Когда mxFrame равен nBackfill, это означает, что всё содержимое журнала WAL было записано обратно в базу данных. В этом случае всё содержимое можно прочитать непосредственно из базы данных. Кроме того, следующий писатель свободен сбросить журнал WAL, если никакие другие соединения не удерживают блокировки WAL_READ_LOCK(N) для N>0.
Значение mxFrame всегда больше или равно как nBackfill, так и nBackfillAttempted.
2.1.2. Поле nBackfill
32-битное беззнаковое целое число со смещением 128 в заголовке индекса WAL называется «nBackfill». Это поле содержит количество кадров в файле журнала WAL, которые были скопированы обратно в основную базу данных.
Число nBackfill никогда не больше mxFrame. Когда nBackfill равно mxFrame, это означает, что содержимое журнала WAL было полностью записано обратно в базу данных, и можно сбросить журнал WAL, если не удерживаются никакие блокировки WAL_READ_LOCK(N) для N>0.
nBackfill может быть увеличен только при удержании блокировки WAL_CKPT_LOCK. Однако nBackfill устанавливается в ноль во время сброса журнала WAL, и это происходит при удержании блокировки WAL_WRITE_LOCK.
2.1.3. Блокировки WAL
Восемь байтов места отводятся в заголовке для поддержки блокировки файлов с помощью метода xShmLock() в объекте sqlite3_io_methods. Эти восемь байтов никогда не читаются и не пишутся SQLite, так как некоторые VFS (например, Windows) могут реализовывать блокировки с помощью обязательных блокировок файлов.
Вот восемь поддерживаемых блокировок:
Блокировки индекса WAL, управляемые xShmLock()| Имя | Смещение | |
|---|---|---|
| xShmLock | Файл | |
| WAL_WRITE_LOCK | 0 | 120 |
| WAL_CKPT_LOCK | 1 | 121 |
| WAL_RECOVER_LOCK | 2 | 122 |
| WAL_READ_LOCK(0) | 3 | 123 |
| WAL_READ_LOCK(1) | 4 | 124 |
| WAL_READ_LOCK(2) | 5 | 125 |
| WAL_READ_LOCK(3) | 6 | 126 |
| WAL_READ_LOCK(4) | 7 | 127 |
TBD: Дополнительная информация о заголовке
2.2. Хэш-таблицы индекса WAL
Хэш-таблицы в файле shm предназначены для быстрого ответа на следующий вопрос:
FindFrame(P,M): Учитывая номер страницы P и максимальный индекс кадра WAL M, вернуть наибольший индекс кадра WAL для страницы P, который не превышает M, или вернуть NULL, если для страницы P нет кадров, которые не превышают M.
Пусть типы данных "u8", "u16" и "u32" обозначают целые беззнаковые числа длиной 8, 16 и 32 бита соответственно. Затем первая единица файла shm размером 32768 байт организована следующим образом:
u8 aWalIndexHeader[136]; u32 aPgno[4062]; u16 aHash[8192];
Вторая и все последующие единицы файла shm размером 32768 байт аналогичны:
u32 aPgno[4096]; u16 aHash[8192];
В совокупности записи aPgno записывают номер страницы базы данных, хранящейся во всех кадрах файла WAL. Запись aPgno[0] в первой хэш-таблице записывает номер страницы базы данных, хранящейся в первом кадре файла WAL. Запись aPgno[i] из первой хэш-таблицы — это номер страницы базы данных для i-го кадра в файле WAL. Запись aPgno[k] из второй хэш-таблицы — это номер страницы базы данных для (k+4062)-го кадра в файле WAL. Запись aPgno[k] для n-й хэш-таблицы в файле shm размером 32768 байт (для n>1) содержит номер страницы базы данных, хранящейся в (k+4062+4096*(n-2))-ом кадре файла WAL.
Вот несколько иной способ описания значений aPgno: если вы представляете все значения aPgno как непрерывный массив, то номер страницы базы данных, хранящейся в i-ом кадре файла WAL, хранится в aPgno[i]. Конечно, aPgno не является непрерывным массивом. Первые 4062 записи находятся в первой единице файла shm размером 32768 байт, а последующие значения находятся в блоках из 4096 записей в последующих единицах файла shm.
Один из способов вычисления FindFrame(P,M) — это просмотр массива aPgno, начиная с M-й записи и двигаясь назад к началу, и возвращение J, где aPgno[J]==P. Такой алгоритм будет работать и будет быстрее, чем поиск во всем файле WAL последнего кадра с номером страницы P. Но поиск можно сделать еще быстрее, используя структуру aHash.
Номер страницы базы данных P отображается в значение хэша с помощью следующей функции хэширования:
h = (P * 383)%8192
Эта функция отображает каждый номер страницы в целое число от 0 до 8191 включительно. Поле aHash каждой единицы файла shm размером 32768 байт отображает значения P в индексы поля aPgno той же единицы следующим образом:
- Вычислить значение хэша: h = P * 383
- Пусть X — наибольший набор последовательных целых чисел {h, h+1, h+2, ..., h+N}, таких что для каждого j в X, aPgno[j%8192]!=0. Множество X будет пустым, если aPgno[h%8192]==0. Множество X легко вычисляется, начиная со значения h%8192, добавляя h%8192 к X и увеличивая h, пока не встретится первая запись aPgno[h%8192], которая равна нулю.
- Множество X содержит индекс в aPgno каждой записи в текущей единице файла shm размером 32768 байт, которая потенциально может быть решением функции FindFrame(P,M). Каждая из этих записей должна быть проверена индивидуально, чтобы убедиться, что значение aPgno равно P, и что номер кадра не превышает M. Наибольший номер кадра, который удовлетворяет этим двум условиям, является ответом.
Каждая запись в массиве aPgno имеет одну соответствующую запись в массиве aHash. В массиве aHash есть больше доступных слотов, чем в aPgno. Неиспользуемые слоты в aHash заполняются нулями. И поскольку гарантированно существуют неиспользуемые слоты в aHash, это означает, что цикл, вычисляющий X, гарантированно завершится. Ожидаемый размер X меньше 2. В худшем случае X будет таким же, как количество записей в aPgno, в этом случае алгоритм работает примерно с той же скоростью, что и линейный поиск в aPgno. Но такая худшая производительность крайне редка. Обычно размер X будет небольшим, и использование массива aHash позволяет вычислить FindFrame(P,M) намного быстрее.
Вот альтернативный способ описания алгоритма поиска по хэшу: начать с h = (P * 383)%8192 и посмотреть на aHash[h] и последующие записи, оборачивая h до нуля, когда h достигает 8192, пока не найдется запись с aHash[h]==0. Все записи aPgno с номером страницы P будут иметь индекс, который является одним из значений aHash[h], вычисленных таким образом. Но не все вычисленные значения aHash[h] будут соответствовать критериям, поэтому вы должны проверить их независимо. Преимущество в скорости заключается в том, что обычно этот набор значений h очень мал.
Обратите внимание, что каждая единица файла shm размером 32768 байт имеет свои массивы aHash и aPgno. Массив aHash для одной единицы полезен только для поиска записей aPgno в той же самой единице. Общая функция FindFrame(P,M) должна выполнять поиск по хэшу, начиная с последней единицы и двигаясь назад к самой старой единице, пока не найдет ответ.
2.3. Матрица блокировок
Доступ координируется в режиме WAL с использованием как блокировок режима DELETE, наследуемых от метода xLock и xUnlock объекта sqlite3_io_methods, так и блокировок WAL, управляемых методом xShmLock объекта sqlite3_io_methods.
По сути, существует только одна блокировка режима DELETE. Блокировка режима DELETE для одного соединения с базой данных может находиться ровно в одном из следующих состояний:
- SQLITE_LOCK_NONE (разблокировано)
- SQLITE_LOCK_SHARED (чтение)
- SQLITE_LOCK_RESERVED (чтение, ожидание записи)
- SQLITE_LOCK_PENDING (новые читатели заблокированы, ожидание записи)
- SQLITE_LOCK_EXCLUSIVE (запись)
Блокировки режима DELETE хранятся на странице «байт блокировки» основного файла базы данных. Только SQLITE_LOCK_SHARED и SQLITE_LOCK_EXCLUSIVE имеют значение для баз данных в режиме WAL. Другие состояния блокировки используются в режиме отката, но не в режиме WAL.
Блокировки режима WAL описаны выше.
2.3.1. Как используются различные блокировки
Следующие правила показывают, как используется каждая из блокировок.
-
SQLITE_LOCK_SHARED
Все подключения удерживают SQLITE_LOCK_SHARED непрерывно во время подключения к базе данных в режиме WAL. Это справедливо как для подключений с чтением/записью, так и для только для чтения. Блокировка SQLITE_LOCK_SHARED удерживается даже подключениями, которые не находятся внутри транзакции. Это отличается от режима отката, где SQLITE_LOCK_SHARED освобождается в конце каждой транзакции.
-
SQLITE_LOCK_EXCLUSIVE
Подключения удерживают эксклюзивную блокировку при изменении между режимом WAL и любым из различных режимов отката. Подключения также могут попытаться получить эксклюзивную блокировку при отключении от режима WAL. Если подключение способно получить эксклюзивную блокировку, это означает, что оно является единственным подключением к базе данных, и поэтому оно может попытаться выполнить контрольную точку и затем удалить индекс WAL и файлы WAL.
Когда подключение удерживает общую блокировку на основной базе данных, это предотвращает любое другое подключение от получения эксклюзивной блокировки, что, в свою очередь, предотвращает удаление индекса WAL и файлов WAL от других пользователей и предотвращает переход из режима WAL, пока другие пользователи не обращаются к базе данных в режиме WAL.
-
WAL_WRITE_LOCK
Блокировка WAL_WRITE_LOCK блокируется только в эксклюзивном режиме. Никогда не используется общая блокировка на WAL_WRITE_LOCK.
ЭКСКЛЮЗИВНАЯ блокировка WAL_WRITE_LOCK удерживается любым подключением, которое добавляет содержимое в конец WAL. Таким образом, только один процесс за раз может добавлять содержимое в WAL. Если происходит сброс WAL в результате записи, то поле nBackfill заголовка индекса WAL сбрасывается до нуля при удержании этой блокировки.
ЭКСКЛЮЗИВНАЯ блокировка также удерживается на WAL_WRITE_LOCK, и на нескольких других байтах блокировки, когда подключение выполняет восстановление общего индекса WAL.
-
WAL_CKPT_LOCK
Блокировка WAL_CKPT_LOCK блокируется только в эксклюзивном режиме. Никогда не используется общая блокировка на WAL_CKPT_LOCK.
ЭКСКЛЮЗИВНАЯ блокировка WAL_CKPT_LOCK удерживается любым подключением, выполняющим контрольную точку. Поле nBackfill заголовка индекса WAL может быть увеличено при удержании этой эксклюзивной блокировки, но не может быть уменьшено.
ЭКСКЛЮЗИВНАЯ блокировка также удерживается на WAL_CKPT_LOCK, и на нескольких других байтах блокировки, когда подключение выполняет восстановление общего индекса WAL.
-
WAL_RECOVER_LOCK
Блокировка WAL_RECOVER_LOCK блокируется только в эксклюзивном режиме. Никогда не используется общая блокировка на WAL_RECOVER_LOCK.
ЭКСКЛЮЗИВНАЯ блокировка WAL_RECOVER_LOCK удерживается любым подключением, которое выполняет восстановление для реконструкции общего индекса WAL.
Подключение только для чтения, которое перестраивает свой частный WAL-индекс памяти, эту блокировку не удерживает. (Это невозможно, так как подключения только для чтения не могут удерживать эксклюзивные блокировки.) Эта блокировка удерживается только при перестроении глобального общего индекса WAL, содержащегося в файле SHM с отображением памяти.
В дополнение к блокировке этого байта подключение, выполняющее восстановление, также получает эксклюзивную блокировку на все остальные блокировки WAL, за исключением WAL_READ_LOCK(0).
-
WAL_READ_LOCK(N)
Существует пять отдельных блокировок чтения, номера от 0 до 4. Блокировки чтения могут быть как общими, так и эксклюзивными. Подключения получают общую блокировку на одном из байтов блокировок чтения, пока находятся внутри транзакции. Подключения также получают эксклюзивную блокировку на блокировки чтения по одной за раз, в течение краткого времени, пока обновляют значения соответствующих меток чтения. Блокировки чтения 1-4 удерживаются в эксклюзивном режиме при выполнении восстановления.
Каждый байт блокировки чтения соответствует одному из пяти 32-битных целых чисел меток чтения, расположенных в байтах с 100 по 119 заголовка индекса WAL, следующим образом:
Имя блокировки Смещение блокировки Имя метки чтения Смещение метки чтения WAL_READ_LOCK(0) 123 read-mark[0] 100..103 WAL_READ_LOCK(1) 124 read-mark[1] 104..107 WAL_READ_LOCK(2) 125 read-mark[2] 108..111 WAL_READ_LOCK(3) 126 read-mark[3] 112..115 WAL_READ_LOCK(4) 127 read-mark[4] 116..119 Когда подключение удерживает общую блокировку на WAL_READ_LOCK(N), это означает, что подключение будет использовать WAL, а не файл базы данных, для любых страниц базы данных, измененных первыми записями метки чтения [N] в WAL. Метка чтения [0] всегда равна нулю. Если подключение удерживает общую блокировку на WAL_READ_LOCK(0), это означает, что подключение ожидает возможности игнорировать WAL и читать любое содержимое, которое оно хочет, из основной базы данных. Если N>0, то подключение свободно использовать больше файла WAL за пределами метки чтения [N], если захочет, до первой кадровой рамки mxFrame. Но когда подключение удерживает общую блокировку на WAL_READ_LOCK(0), это означает, что оно никогда не будет читать содержимое из WAL и получит все содержимое напрямую из основной базы данных.
Когда выполняется контрольная точка, если она видит блокировку на WAL_READ_LOCK(N), она не должна перемещать содержимое WAL в основную базу данных более чем для первых меток чтения [N] кадров. В противном случае она перепишет содержимое, которое процесс, удерживающий блокировку, ожидает прочитать из файла основной базы данных. Следствием этого является то, что если файл WAL содержит более чем метки чтения [N] кадров (если mxFrame>read-mark[N] для любой метки чтения, для которой WAL_READ_LOCK(N) удерживается другим процессом), то контрольная точка не может завершиться.
Когда автор хочет сбросить WAL, он должен убедиться, что нет блокировок на WAL_READ_LOCK(N) для N>0, потому что такие блокировки указывают, что какое-то другое подключение все еще использует текущий файл WAL, и сброс WAL удалит содержимое из этих других подключений. Разрешено выполнение сброса WAL, если другие подключения удерживают WAL_READ_LOCK(0), потому что, удерживая WAL_READ_LOCK(0), эти другие подключения обещают не использовать какое-либо содержимое из WAL.
2.3.2. Операции, требующие блокировок, и используемые блокировки
-
Переход в режим WAL и из него
Подключение, которое хочет перейти в режим WAL или из него, должно удерживать эксклюзивную блокировку SQLITE_LOCK_EXCLUSIVE. Таким образом, переход в режим WAL такой же, как и любая другая операция записи, так как каждая операция записи в режиме отката требует эксклюзивной блокировки SQLITE_LOCK_EXCLUSIVE. Если файл базы данных уже находится в режиме WAL (следовательно, если необходимо изменить его обратно в режим отката) и если существует два или более подключений к базе данных, то каждое из этих подключений будет удерживать общую блокировку SQLITE_LOCK_SHARED. Это означает, что эксклюзивная блокировка SQLITE_LOCK_EXCLUSIVE не может быть получена, и переход из режима WAL не будет разрешен. Это предотвращает одному подключению от удаления режима WAL от другого. Это также означает, что единственный способ перевести базу данных из режима WAL в режим отката — закрыть все подключения к базе данных, кроме одного.
-
Закрытие подключения к базе данных в режиме WAL
При закрытии подключения к базе данных (через sqlite3_close() или sqlite3_close_v2()) предпринимается попытка получить эксклюзивную блокировку SQLITE_LOCK_EXCLUSIVE. Если эта попытка окажется успешной, это означает, что закрываемое подключение является последним подключением к базе данных. В этом случае желательно очистить файлы WAL и индекса WAL, поэтому закрываемое подключение выполняет контрольную точку (при удержании SQLITE_LOCK_EXCLUSIVE) и удаляет файлы WAL и индекса WAL. Блокировка SQLITE_LOCK_EXCLUSIVE не освобождается до тех пор, пока не будут удалены оба файла, WAL и индекса WAL.
Если приложение вызовет sqlite3_file_control(SQLITE_FCNTL_PERSIST_WAL) для подключения к базе данных перед закрытием, то последняя контрольная точка все равно выполняется, но файлы WAL и индекса WAL не удаляются, как это обычно происходит. Это оставляет базу данных в состоянии, позволяющем другим процессам без прав записи на файлы базы данных, WAL или индекса WAL открыть базу данных только для чтения. Если файлы WAL и индекса WAL отсутствуют, процесс, которому не разрешено создавать и инициализировать эти файлы, не сможет открыть базу данных, если база данных не назначена как неизменяемая с помощью параметра запроса immutable.
-
Реконструкция глобального общего индекса WAL во время восстановления
Все блокировки индекса WAL, за исключением WAL_READ_LOCK(0), удерживаются в эксклюзивном режиме во время реконструкции глобального общего индекса WAL во время восстановления.
-
Добавление новой транзакции в конец WAL
Эксклюзивная блокировка WAL_WRITE_LOCK удерживается при добавлении новых кадров в конец файла WAL.
Чтение содержимого из базы данных и WAL в рамках транзакции
Выполнение контрольной точки
-
Сброс файла WAL
Сброс WAL означает перемотку WAL и добавление новых кадров в начало. Это происходит при добавлении новых кадров в WAL, у которого mxFrame равно nBackfill и у которого нет блокировок на WAL_READ_LOCK(1) до WAL_READ_LOCK(4). Удерживается блокировка WAL_WRITE_LOCK.
3. Восстановление
Восстановление — это процесс перестроения индекса WAL, чтобы он был синхронизирован с WAL.
Восстановление выполняется первым потоком, подключившимся к базе данных в режиме WAL. Восстановление восстанавливает индекс WAL так, чтобы он точно описывал файл WAL. Если файл WAL отсутствует при подключении первого потока к базе данных, восстанавливать нечего, но процесс восстановления все равно выполняется для инициализации индекса WAL.
Если индекс WAL реализован как файл с отображением памяти и этот файл является только для чтения для первого подключившегося потока, то этот поток создает частный индекс WAL в памяти и выполняет процедуру восстановления для заполнения этого частного индекса WAL. Результат данных тот же, но он хранится в частном порядке, а не записывается в общедоступную общую область памяти.
Восстановление работает, выполняя один проход по WAL от начала до конца. Контрольные суммы проверяются для каждого кадра WAL по мере чтения. Сканирование останавливается в конце файла или на первой неверной контрольной сумме. Поле mxFrame устанавливается в индекс последнего допустимого кадра в WAL. Так как номера кадров WAL индексируются, начиная с 1, mxFrame также является количеством допустимых кадров в WAL. «Кадр подтверждения» — это кадр, имеющий ненулевое значение в байтах с 4 по 7 заголовка кадра. Поскольку процедура восстановления не может знать, сколько кадров WAL могло быть скопировано обратно в базу данных, она инициализирует значение nBackfill нулем.
Во время восстановления глобального совместно используемого WAL-индекса, эксклюзивные блокировки удерживаются на WAL_WRITE_LOCK, WAL_CKPT_LOCK, WAL_RECOVER_LOCK и WAL_READ_LOCK(1) через WAL_READ_LOCK(4). Другими словами, все блокировки, связанные с WAL-индексом, кроме WAL_READ_LOCK(0), удерживаются эксклюзивно. Это предотвращает любую другую нить от записи в базу данных и от чтения каких-либо транзакций, которые хранятся в WAL, пока восстановление не будет завершено.
Эта страница была последним обновлена 2024-09-19 15:54:51 UTC
SQLite is in the Public Domain.
https://sqlite.org/walformat.html