RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Small. Fast. Reliable.
Choose any three.
Формат файла базы данных
Оглавление

Этот документ описывает и определяет формат файла базы данных на диске, используемый всеми выпусками SQLite начиная с версии 3.0.0 (2004-06-18).

1. Файл базы данных

Полное состояние базы данных SQLite обычно содержится в единственном файле на диске, названном "главным файлом базы данных".

Во время транзакции SQLite хранит дополнительную информацию во втором файле, названном "журналом обратной перемотки", или если SQLite находится в режиме WAL, файле журнала с упреждающей записью.

1.1. Горячие журналы

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

1.2. Страницы

Главный файл базы данных состоит из одной или более страниц. Размер страницы это степень двух между 512 и 65536 включительно. Все страницы в той же самой базе данных имеют тот же самый размер. Размер страницы для файла базы данных определяется 2-байтовым целым числом, расположенным в смещении 16 байтов с начала файла базы данных.

Страницы пронумерованы, начав с 1. Максимальный номер страницы равняется 4294967294 (232 - 2). Минимальная база данных SQLite это единственная 512-байтовая страница. Максимальная база данных составила бы 4294967294 страницы по 65536 байтов на страницу или 281,474,976,579,584 байтов (приблизительно 281 терабайт). Обычно SQLite будет поражать максимальный предел размера файла основной файловой системы или дисковых аппаратных средств задолго до того, как это поражает свой собственный внутренний предел размера.

Широко использующиеся базы данных SQLite имеют тенденцию располагаться в размере от нескольких килобайт до нескольких гигабайт, хотя базы данных SQLite размера терабайта, как известно, существуют в производстве.

В любом моменте времени у каждой страницы в главной базе данных есть единственное использование, которое является одним из следующего:

  • lock-byte
  • freelist
    • freelist страница ствола
    • freelist страница листа
  • b-tree
    • Страница интерьера b-дерева таблицы
    • Страница листа b-дерева таблицы
    • Страница интерьера b-дерева индекса
    • Страница листа b-дерева индекса
  • Страница переполнения полезной нагрузки
  • Страница карты указателей

Все чтения и записи главного файла базы данных начинаются на границе страницы, все записи охватывают целое число страниц в размере. Читается также обычно целое число страниц в размере, за одним исключением: когда база данных сначала открыта, первые 100 байтов файла базы данных (заголовок файла базы данных) прочитаны как единица подразмера страницы.

Прежде чем любая имеющая информацию страница базы данных изменяется, оригинальное неизмененное содержание той страницы написано в журнал обратной перемотки. Если транзакция прервана и должна быть отменена, журнал обратной перемотки может тогда использоваться, чтобы вернуть базу данных к ее исходному состоянию. Страницы листа Freelist не имеют информации, которая должна была бы быть восстановлена на обратной перемотке и таким образом, они не написаны в журнал до модификации, чтобы уменьшить дисковый I/O.

1.3. Заголовок базы данных

Первые 100 байтов файла базы данных включают заголовок файла базы данных. Заголовок файла базы данных разделен на области как показано приведенной ниже таблицей. Все мультибайтные области в заголовке файла базы данных снабжены первым старшим значащим байтом (обратный порядок байтов).

Формат заголовка базы данных
СмещениеРазмерОписание
0 16 Строка заголовка: "SQLite format 3\000"
16 2 Размер страницы базы данных в байтах. Должен быть степенью 2 от 512 до 32768 включительно или 1 означает 65536.
18 1 Версия записи формата файла. 1 = legacy; 2 = WAL.
19 1 Версия чтения формата файла. 1 = legacy; 2 = WAL.
20 1 Байты неиспользованного "зарезервированного" пространства в конце каждой страницы. Обычно 0.
21 1 Максимальная вложенная часть полезной нагрузки. Должен быть 64.
22 1 Минимальная вложенная часть полезной нагрузки. Должен быть 32.
23 1 Часть полезной нагрузки листа. Должен быть 32.
24 4 Счетчик изменения файла.
28 4 Размер файла базы данных в страницах. "Размер базы данных в заголовке".
32 4 Номер страницы первой freelist страницы ствола.
36 4 Общее количество freelist страниц.
40 4 cookie схемы.
44 4 Номер формата схемы. Поддержанные форматы схемы 1, 2, 3 и 4.
48 4 Размер кэша страницы по умолчанию.
52 4 Номер страницы самой большой страницы b-дерева корня, когда в режиме автовакууме или возрастающего вакуума, иначе ноль.
56 4 Текстовое кодирование базы данных. 1 = UTF-8. 2 = UTF-16le. 3 = UTF-16be.
60 4 "user version", как прочитано и установлено user_version pragma.
64 4 True (не 0) для режима возрастающего вакуума. False (0) иначе.
68 4 "Application ID", заданный PRAGMA application_id.
72 20 Зарезервировано для расширения. Должен быть ноль.
92 4 Номер version-valid-for.
96 4 SQLITE_VERSION_NUMBER

1.3.1. Волшебная последовательность заголовка

Каждый действительный файл базы данных SQLite начинается со следующих 16 байтов (в hex): 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00. Эта последовательность байт соответствует последовательности UTF-8 "SQLite format 3", включая символ nul в конце.

1.3.2. Размер страницы

Двухбайтовое значение, начинающееся в смещении 16, определяет размер страницы базы данных. Для версий SQLite 3.7.0.1 (2010-08-04) и ранее это значение интерпретируется как целое число с обратным порядком байтов и должно быть степенью двух между 512 и 32768, включительно. Начиная с SQLite version 3.7.1 (2010-08-23), размер страницы в 65536 байтов поддерживается. Значение 65536 не поместится в двухбайтовое целое число, таким образом, чтобы определить 65536-байтовый размер страницы, значение в смещении 16 будет 0x00 0x01. Это значение может интерпретироваться как обратный порядок байтов 1 и считаться магическим числом, чтобы представлять 65536 как размер страницы. Или можно рассмотреть двухбайтовую область как небольшое индийское число и сказать, что это представляет размер страницы, разделенный на 256. Эти две интерпретации области размера страницы эквивалентны.

1.3.3. Номера версий формата файла

Версии записи и чтения формата файла находятся в смещениях 18 и 19 и нужны, чтобы допускать улучшения формата файла в будущих версиях SQLite. В текущих версиях SQLite оба значения 1 для обратной перемотки и 2 для режима WAL. Если версия SQLite, закодированного к текущей спецификации формата файла, сталкивается с файлом базы данных, где версия чтения равняется 1 или 2, но версия записи больше 2, то файл базы данных нужно рассматривать как только для чтения. Если сталкиваются с файлом базы данных с версией чтения больше 2, то та база данных не может быть прочитана или написана.

1.3.4. Зарезервированные байты на страницу

У SQLite есть способность отложить небольшое количество дополнительных байтов в конце каждой страницы для использования расширениями. Эти дополнительные байты используются, например, SQLite Encryption Extension, чтобы сохранить данный случай и/или шифровальную контрольную сумму, связанную с каждой страницей. "Зарезервированное место" имеет размер 1-байтового целого числа в смещении 20 и является числом байт места в конце каждой страницы, чтобы зарезервировать для расширений. Это значение обычно 0.

"Применимый размер" страницы базы данных является размером страницы, определенным 2-байтовым целым числом в смещении 16 в заголовке до "зарезервированного" размера, зарегистрированного в 1-байтовом целом числе в смещении 20 в заголовке. Применимый размер страницы мог бы быть нечетным числом. Однако, применимому размеру не позволяют быть меньше чем 480. Другими словами, если размер страницы 512, то зарезервированный размер не может превысить 32.

1.3.5. Части полезной нагрузки

Максимальные и минимальные вложенные части полезной нагрузки и значения части полезной нагрузки листа должны быть 64, 32, и 32. Эти значения были первоначально предназначены, чтобы быть настраиваемыми параметрами, которые могли использоваться, чтобы изменить формат хранения алгоритма b-дерева. Однако та функциональность не поддерживается и нет никаких текущих планов добавить поддержку в будущем. Следовательно, эти три байта фиксируются при определенных значениях.

1.3.6. Счетчик изменения файла

Счетчик изменения файла это 4-байтовое целое число с обратным порядком байтов в смещении 24, которое увеличен каждый раз, когда файл базы данных разблокируют после изменений. Когда два или больше процесса читают тот же самый файл базы данных, каждый процесс может обнаружить изменение базы данных от других процессов, контролируя этот счетчик. Процесс будет обычно хотеть сбросить свой кэш страницы базы данных, когда другой процесс изменил базу данных, так как кэш стал несвежим. Счетчик изменения файла облегчает это.

В режиме WAL изменения базы данных обнаружены, используя wal-индекс и таким образом счетчик изменения не необходим. Следовательно, он не увеличен на каждой транзакции в режиме WAL.

1.3.7. Размер базы данных в заголовке

4-байтовое целое число с обратным порядком байтов в смещении 28 в заголовок хранит размер файла базы данных в страницах. Если этот размер размера данных в заголовке не действителен (см. следующий параграф), то размер базы данных вычисляется, смотря на фактический размер файла базы данных. Более старые версии SQLite проигнорировали размер базы данных в заголовке и использовали фактический размер файла исключительно. Более новые версии SQLite используют размер базы данных в заголовке, если это доступно, но отступают к фактическому размеру файла, если размер базы данных в заголовке не действителен.

Размер базы данных в заголовке считается действительным только, если это отличное от нуля и если 4-байтовый change counter в смещении 24 точно соответствует 4-байтовому номеру version-valid-for в смещении 92. Размер базы данных в заголовке всегда действителен, когда база данных только изменяется, используя последние версии SQLite, версии 3.7.0 (2010-07-21) и позже. Если устаревшая версия SQLite напишет базу данных, это не будет знать, что надо обновить размер базы данных в заголовке и таким образом, размер базы данных в заголовке может быть неправильным. Но устаревшие версии SQLite также оставят номер version-valid-for в смещении 92 неизменным, таким образом, это не будет соответствовать счетчику изменения. Следовательно, недействительные размеры базы данных в заголовке могут быть обнаружены (и проигнорированы), наблюдая, когда счетчик изменения не соответствует номеру version-valid-for.

1.3.8. Список свободных страниц

Неиспользованные страницы в файле базы данных сохранены в freelist. 4-байтовое целое число с обратным порядком байтов в смещении 32 хранит номер первой страницы freelist или ноль, если freelist пуст. 4-байтовое целое число с обратным порядком байтов в смещении 36 хранит общее количество страниц в freelist.

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

1.3.10. Номер формата схемы

Номер формата схемы это 4-байтовое целое число с обратным порядком байтов в смещении 44. Номер формата схемы подобен формату файла чтения и записи в смещениях 18 и 19 за исключением того, что номер формата схемы относится к форматированию SQL высокого уровня, а не форматированию b-дерева низкого уровня. Четыре номера формата схемы в настоящее время определяются:

  1. 1 понят всеми версиями SQLite до version 3.0.0 (2004-06-18).
  2. 2 добавляет способность строк в той же самой таблице иметь переменное количество колонок, чтобы поддержать функциональность ALTER TABLE ... ADD COLUMN. Поддержка чтения и записи формата 2 была добавлена в SQLite version 3.1.3 2005-02-20.
  3. 3 добавляет способность дополнительных столбцов, добавленных ALTER TABLE ... ADD COLUMN иметь значения по умолчанию не NULL. Эта способность была добавлена в version 3.1.4 2005-03-11.
  4. 4 заставляет SQLite понимать ключевое слово DESC на декларациях индекса. Ключевое слово DESC проигнорировано в индексах для форматов 1, 2 и 3. Формат 4 также добавляет две новых булевых значения типа записи (serial types 8 и 9). Поддержка формата 4 была добавлена в SQLite 3.3.0 2006-01-10.

Новые файлы базы данных, созданные SQLite, используют формат 4 по умолчанию. legacy_file_format pragma может использоваться, чтобы заставить SQLite создавать новые файлы базы данных, используя формат 1. Номер версии формата может быть сделан умолчанием 1 вместо 4, установив во время компиляции SQLITE_DEFAULT_FILE_FORMAT=1.

1.3.11. Предложенный размер кэша

4-байтовое целое число со знаком с обратным порядком байтов в смещении 48 является предложенным размером кэша на страницах для файла базы данных. Значение это только предложение, SQLite не обязан его соблюдать. Абсолютное значение целого числа используется в качестве предложенного размера. Предложенный размер кэша может быть установлен, используя default_cache_size pragma.

1.3.12. Возрастающие вакуумные параметры настройки

Два 4-байтовых целых числа с обратным порядком байтов в смещениях 52 и 64 используются, чтобы управлять auto_vacuum и incremental_vacuum. Если целое число в смещении 52 является нолем тогда, карта указателей (ptrmap) страницы опущена от файла базы данных, и ни auto_vacuum, ни incremental_vacuum не поддерживаются. Если целое число в смещении 52 отличное от нуля тогда, это номер страницы самой большой страницы корня в файле базы данных, файл базы данных будет содержать страницы ptrmap, и режим должен быть или auto_vacuum или incremental_vacuum. В этом последнем случае целое число в смещении 64 true для incremental_vacuum и false для auto_vacuum. Если целое число в смещении 52 является нолем, тогда целое число в смещении 64 должно также быть нолем.

1.3.13. Текстовое кодирование

4-байтовое целое число с обратным порядком байтов в смещении 56 определяет кодирование, используемое для всех текстовых строк, сохраненных в базе данных. 1 = UTF-8. 2 = UTF-16le. 3 = UTF-16be. Никакие другие значения не позволены. Заголовочный файл sqlite3.h определяет макрос C-препроцессора SQLITE_UTF8 как 1, SQLITE_UTF16LE как 2 и SQLITE_UTF16BE как 3, чтобы использовать вместо цифровых кодов для текстового кодирования.

1.3.14. Пользовательский номер версии

4-байтовое целое число с обратным порядком байтов в смещении 60 является пользовательской версией, которая установлена и запрошена user_version pragma. Пользовательская версия не используется SQLite.

1.3.15. Application ID

4-байтовым целым числом с обратным порядком байтов в смещении 68 является "Идентификатор приложения", который может быть установлен PRAGMA application_id, чтобы идентифицировать базу данных как принадлежащую или связанную с конкретным применением. Идентификатор приложения предназначается для файлов базы данных, используемых в качестве прикладного формата файла. Идентификатор приложения может использоваться утилитами, такими как file(1), чтобы определить определенный тип файла вместо того, чтобы просто сообщить "о Базе данных SQLite3". Список назначенных идентификаторов приложений может быть найден в файле magic.txt в исходном хранилище SQLite.

1.3.16. Запись номера версии библиотеки и числа version-valid-for

4-байтовое целое число с обратным порядком байтов в смещении 96 хранит SQLITE_VERSION_NUMBER для библиотеки SQLite, которая последний раз изменила файл базы данных. 4-байтовое целое число с обратным порядком байтов в смещении 92 является счетчиком изменений, когда номер версии был сохранен. Целое число в смещении 92 указывает, для какой транзакции номер версии действителен и иногда называется "version-valid-for".

1.3.17. Пространство заголовка, зарезервированное для расширения

Все другие байты заголовка файла базы данных резервируются для будущего расширения и должны быть установлены в ноль.

1.4. Страница Lock-Byte

Страница Lock-Byte это единственная страница файла базы данных, которая содержит байты между 1073741824 и 1073742335, включительно. Файл базы данных, который меньше или равен 1073741824 байтам в размере, не содержит этой страницы. Файл базы данных, больше 1073741824, содержит точно страницу.

Страница Lock-Byte обойдена для использования определенным операционной системой внедрением VFS в осуществлении примитивов блокировки файла базы данных. SQLite не использует страницу lock-byte. Ядро SQLite никогда не будет читать или писать страницу lock-byte, хотя VFS может прочитать или пишет байты на этой странице согласно потребностям и склонностям основной системы. Unix и win32 VFS, которые встроены в SQLite, не пишут эту страницу, но сторонние внедрения VFS для других операционных систем могли бы.

Страница Lock-Byte явилась результатом потребности поддержать Win95, которая была преобладающей операционной системой, когда этот формат файла был разработан и которая поддерживала только обязательную блокировку файла. Все современные операционные системы, что мы знаем поддерживают консультативную блокировку файла, и таким образом, страница lock-byte больше не действительно необходима, но сохраняется для совместимости.

1.5. Freelist

Файл базы данных мог бы содержать одну или более страниц, которые не находятся в активном использовании. Неиспользованные страницы могут появиться, например, когда информация удалена из базы данных. Неиспользованные страницы сохранены в freelist и снова используются, когда дополнительные страницы требуются.

freelist организован как связанный список freelist страниц ствола с каждой страницей ствола, содержащей номера страниц для ноля или большего количества freelist страниц листа.

freelist страница ствола состоит из множества 4-байтовых целых чисел с обратным порядком байтов. Размер множества это столько целых чисел, сколько поместится в применимое пространство страницы. Минимальное применимое пространство составляет 480 байтов, таким образом, множество всегда будет по крайней мере 120 записями в длине. Первое целое число на freelist странице ствола это номер страницы следующей freelist страницы ствола в списке или ноль, если это последняя freelist страница ствола. Второе целое число на freelist странице ствола это количество указателей страницы листа. Назовите второе целое число на freelist странице ствола L. Если L больше, чем ноль тогда, целые числа с индексами массива между 2 и L+1 включительно содержат номера страниц для freelist страниц листа.

Страницы листа Freelist не содержат информации. SQLite избегает читать или писать freelist страницы листа, чтобы уменьшить дисковый I/O.

Ошибка в версиях SQLite до 3.6.0 (2008-07-16) заставила базу данных считаться поврежденной, если какие-либо из последних 6 записей на freelist странице ствола имеют ненулевые значения. У более новых версий SQLite нет этой проблемы. Однако более новые версии SQLite все еще избегают использования последних шести записей в множестве freelist страниц ствола, чтобы файлы базы данных, созданные более новыми версиями SQLite, могли быть прочитаны более старыми версиями SQLite.

Число freelist страниц сохранено как 4-байтовое целое число с обратным порядком байтов в заголовке базы данных в смещении 36 с начала файла. Заголовок базы данных также хранит номер страницы первой freelist страницы ствола как 4-байтовое целое число с обратным порядком байтов в смещении 32 с начала файла.

1.6. Страницы B-tree

Алгоритм b-дерева предоставляет ключу/хранению данных уникальные и упорядоченные ключи, ориентированные на страницу устройства хранения данных. Для справочной информации о b-деревьях посмотрите Knuth, The Art Of Computer Programming, Volume 3 "Sorting and Searching", pages 471-479. Два варианта b-деревьев используются SQLite. "B-деревья таблицы" используют 64-битный ключ целого числа со знаком и хранят все данные в листьях. "B-деревья индекса" используют произвольные ключи и не хранят данных вообще.

Страница b-дерева это внутренняя страница или страница листа. Страница листа содержит ключи, и в случае b-дерева таблицы каждый ключ связан с данными. Внутренняя страница содержит ключи K вместе с указателями K+1 на дочерние страницы b-дерева. "Указатель" на внутренней странице b-дерева это просто 32-битный целый без знака номер страницы дочерней страницы.

Количество ключей на внутренней странице b-дерева, K, является почти всегда по крайней мере 2 и обычно является намного больше чем 2. Единственное исключение это когда страница 1 внутренняя страница b-дерева. Страница 1 имеет в наличии на 100 меньше байтов пространства памяти из-за присутствия заголовка базы данных в начале той страницы, поэтому иногда (редко), если страница 1 внутренняя страница b-дерева, это может хранить единственный ключ. Во всех других случаях K равняется 2 или больше. Верхняя граница на K это столько ключей, сколько будет соответствовать на странице. Большие ключи на b-деревьях индекса разделены на страницы переполнения так, чтобы никакой единственный ключ не использовал больше чем одну четверть доступного пространства памяти на странице, и следовательно каждая внутренняя страница в состоянии сохранить по крайней мере 4 ключа. Ключи целого числа b-деревьев таблицы никогда не достаточно большие, чтобы потребовать переполнения, таким образом, переполнение происходит только на b-деревьях индекса.

Определите глубину b-дерева листа в 1 и глубину любого внутреннего b-дерева на 1 больше, чем максимальная глубина любой дочерней страницы. В правильно построенной базе данных у всех потомков внутреннего b-дерева есть та же самая глубина.

На внутренней странице b-дерева указатели и ключи логически чередуются с указателем на обоих концах. Предыдущее предложение должно быть понято концептуально: фактическое расположение ключей и указателей в странице более сложно и будет описано в продолжении. Все ключи в той же самой странице уникальны и логически организованы в порядке возрастания слева направо. Снова, этот порядок логичный, не физический. Фактическое местоположение ключей в странице произвольно. Для любого ключа X, указатели налево от X относятся к страницам b-дерева, на которых все ключи меньше или равны X. Указатели направо от X относятся к страницам, где все ключи больше X.

Во внутренней странице b-дерева каждый ключ и указатель на ее оставленное непосредственное объединены в структуру, названную "клеткой". Самый правый указатель проводится отдельно. У страницы b-дерева листа нет указателей, но она все еще использует структуру клетки, чтобы держать под контролем для b-деревьев индекса или ключами и содержанием для b-деревьев таблицы. Данные также содержатся в клетке.

У каждой страницы b-дерева есть самое большее одна родительская страница b-дерева. Страницу b-дерева без родителя называют страницей корня. Страница b-дерева корня вместе с ее потомками формирует полное b-дерево. Это возможно (и на самом деле довольно распространено) иметь полное b-дерево, которое состоит из единственной страницы, которая является листом и корнем сразу. Поскольку есть указатели от родителей детям, каждая страница полного b-дерева может быть расположена, если только страница корня известна. Следовательно, b-деревья определяются их номером страницы корня.

Страница b-дерева это страница b-дерева таблицы или страница b-дерева индекса. Все страницы в каждом полном b-дереве имеют тот же самый тип: таблица или индекс. Есть b-деревья таблицы в файле базы данных для каждой rowid-таблицы в схеме базы данных, включая системные таблицы, такие как sqlite_schema. Есть одно b-дерево индекса в файле базы данных для каждого индекса в схеме, включая подразумеваемые индексы, созданные ограничениями уникальности. Нет никаких b-деревьев, связанных с виртуальными таблицами. Определенные виртуальные внедрения таблицы могли бы использовать теневые таблицы для хранения, но у тех теневых таблиц будут отдельные записи в схеме базы данных. Таблицы WITHOUT ROWID используют b-деревья индекса, а не табличные b-деревья, таким образом, есть одно b-дерево индекса в файле базы данных для каждой таблицы WITHOUT ROWID. B-дерево, соответствующее таблице sqlite_schema, всегда является b-деревом таблицы и всегда имеет страницу корня 1. Таблица sqlite_schema содержит номер страницы корня для любой таблицы и индекса в файле базы данных.

Каждый вход в b-дереве таблицы состоит из 64-битного ключа целого числа со знаком и до 2147483647 байтов произвольных данных. Ключ b-дерева таблицы соответствует rowid таблицы SQL, которую осуществляет b-дерево. Внутренние b-деревья таблицы содержат только ключи и указатели на потомков. Все данные содержатся в листьях b-дерева таблицы.

Каждый вход в b-дереве индекса состоит из произвольного ключа до 2147483647 байтов в длине и не содержит никаких данных.

Определите "полезную нагрузка" клетки, чтобы быть разделом произвольной длины клетки. Для b-дерева индекса ключ всегда произволен в длине, и следовательно полезная нагрузка это ключ. Нет никаких элементов произвольной длины в клетках внутренних страниц b-дерева таблицы и таким образом, у тех клеток нет полезной нагрузки. Страницы листа b-дерева таблицы содержат содержание произвольной длины, значит для клеток на тех страницах, полезная нагрузка это содержание.

Когда размер полезной нагрузки для клетки превышает определенный порог (определен позже), тогда только первые несколько байтов полезной нагрузки сохранены на странице b-дерева, и баланс сохранен в связанном списке страниц переполнения.

Страница b-дерева разделена на регионы в следующем порядке:

  1. 100-байтовый заголовок файла базы данных (только на странице 1)
  2. 8 или 12-байтовый верхний колонтитул страницы b-дерева
  3. Множество указателей на клетки
  4. Неассигнованное пространство
  5. Предметная область клетки
  6. Зарезервированный регион

100-байтовый заголовок файла базы данных есть только на странице 1, которая всегда является страницей b-дерева таблицы. Все другие страницы b-дерева в файле базы данных опускают этот 100-байтовый заголовок.

Зарезервированный регион это область неиспользуемого места в конце каждой страницы (кроме страницы блокировок), которое расширения могут использовать, чтобы хранить информацию для страницы. Размер зарезервированного региона определяется однобайтовым целым без знака, найденным в смещении 20 в заголовке файла базы данных. Размер зарезервированного региона обычно ноль.

Верхний колонтитул страницы b-дерева составляет 8 байт в размере для страниц листа и 12 байт для внутренних страниц. Все мультибайтные значения в верхнем колонтитуле страницы имеют обратный порядок байтов. Верхний колонтитул страницы b-дерева состоит из следующих областей:

Формат верхнего колонтитула страницы B-дерева
СмещениеРазмерОписание
0 1 Однобайтовый флаг в смещении 0 указывает на тип страницы b-дерева.
  • 2 (0x02) означает, что это внутренняя страница b-дерева индекса.
  • 5 (0x05) означает, что это внутренняя страница b-дерева таблицы.
  • 10 (0x0a) означает, что это страница b-дерева индекса листа.
  • 13 (0x0d) означает, что это страница листа b-дерева таблицы.
Любое другое значение для типа страницы b-дерева это ошибка.
1 2 Двухбайтовое целое число в смещении 1 дает начало первого freeblock на странице или является нолем, при отсутствии freeblocks.
3 2 Двухбайтовое целое число в смещении 3 дает количество клеток на странице.
5 2 Двухбайтовое целое число в смещении 5 определяет начало предметной области клетки. Нулевое значение для этого целого числа интерпретируется как 65536.
7 1 Однобайтовое целое число в смещении 7 дает число фрагментированных свободных байтов в предметной области клетки.
8 4 Четырехбайтовый номер страницы в смещении 8 является самым правым указателем. Эта значение появляется только в заголовке внутренних страниц b-дерева и пропущено на всех других страницах.

Множество указателя ячейки страницы b-дерева следует немедленно за верхним колонтитулом страницы b-дерева. Позвольте K быть количеством клеток в btree. Множество указателя ячейки состоит из 2-байтовых смещений целого числа K к содержанию клетки. Указатели ячейки устроены в ключевом порядке с крайней левой клеткой (клетка с самым маленьким ключом) первой и самой правой клеткой (клетка с самым большим ключом) последней.

Содержание клетки сохранено в регионе содержания клетки страницы b-дерева. SQLite стремится поместить клетки настолько далеко к концу страницы b-дерева, как может, чтобы оставить пространство для будущего роста множества указателя ячейки. Областью между последней записью массива указателя ячейки и началом первой клетки является неассигнованный регион.

Если страница не будет содержать клеток (это возможно только для страницы корня таблицы, которая не содержит строк), смещение предметной области клетки будет равняться размеру страницы минус байты зарезервированного пространства. Если база данных использует 65536-байтовый размер страницы, а зарезервированное пространство ноль (обычное значение для зарезервированного пространства), смещение содержания клетки пустой страницы хочет быть 65536. Однако, то целое число слишком большое, чтобы быть сохраненным в 2-байтовом целом без знака, таким образом, значение 0 используется в этом месте.

freeblock это структура, используемая, чтобы определить неассигнованное пространство в странице b-дерева. Freeblock организованы как цепь. Первые 2 байта freeblock это целое число с обратным порядком байтов, которое является смещением на странице b-дерева следующего freeblock в цепи или нолем, если freeblock является последним в цепи. Третьи и четвертые байты каждого freeblock формируют целое число с обратным порядком байтов, которое является размером freeblock в байтах, включая 4-байтовый заголовок. Freeblock всегда связываются в порядке увеличения смещения. Вторая область верхнего колонтитула страницы b-дерева это смещение первого freeblock или ноль при отсутствии freeblock на странице. На правильно построенной странице b-дерева перед первым freeblock всегда будет по крайней мере одна клетка.

freeblock требует по крайней мере 4 байта пространства. Если есть изолированная группа 1, 2 или 3 неиспользованных байта в предметной области клетки, те байты включают фрагмент. Общее количество байтов во всех фрагментах сохранено в пятой области верхнего колонтитула страницы b-дерева. На правильно построенной странице b-дерева общее количество байтов во фрагментах не может превысить 60.

Общая сумма свободного пространства на странице b-дерева состоит из размера неассигнованного региона плюс полный размер всего freeblock плюс число фрагментированных свободных байтов. SQLite может время от времени реорганизовывать страницу b-дерева так, чтобы не было никакого freeblock или байтов фрагмента, все неиспользованные байты содержатся в неассигнованном регионе, и все клетки упакованы плотно в конце страницы. Это называют "дефрагментацией" страницы b-дерева.

Целое число переменной длины или "varint" это статическое кодирование Хафмана 64-битных дополнительных пар целых чисел, которое использует меньше пространства для маленьких положительных значений. varint между 1 и 9 байтами в длине. varint состоит из ноля или из большего количества байтов, которые имеют установленный старший бит, сопровождаемые одним байтом с обнуленным старшим битом, или девять байтов, смотря, что короче. Младшие семь битов каждого из первых восьми байтов и все 8 битов девятого байта используются, чтобы хранить 64-битное дополненнок парами целое число. Varints имеет обратный порядок байтов: биты, взятые от более раннего байта varint, более значительные, чем биты, взятые от более поздних байтов.

Формат клетки зависит от того, на которой странице b-tree клетка появляется. Следующая таблица показывает элементы клетки в порядке появления для различных типов страницы b-дерева.

Лепестковый элемент B-дерева таблицы (заголовок 0x0d):

  • varint, который является общим количеством байтов полезной нагрузки, включая любое переполнение
  • varint, который является ключом целого числа, иначе "rowid"
  • Начальная часть полезной нагрузки, которая не переходит на страницы переполнения.
  • 4-байтовый номер страницы целого числа с обратным порядком байтов для первой страницы списка страницы переполнения, пропущено, если вся полезная нагрузка размещена на странице b-дерева.

Клетка интерьера B-дерева таблицы (заголовок 0x05):

  • 4-байтовый номер страницы с обратным порядком байтов, который является левым указателем на подчиненный элемент.
  • varint, который является ключом целого числа

Лепестковый элемент B-дерева индекса (заголовок 0x0a):

  • varint, который является общим количеством байтов ключа полезной нагрузки, включая любое переполнение
  • Начальная часть полезной нагрузки, которая не переходит на страницы переполнения.
  • 4-байтовый номер страницы целого числа с обратным порядком байтов для первой страницы списка страницы переполнения. Пропущено, если вся полезная нагрузка находится на странице b-дерева.

Клетка интерьера B-дерева индекса (заголовок 0x02):

  • 4-байтовый номер страницы с обратным порядком байтов, который является левым указателем на подчиненный элемент.
  • varint, который является общим количеством байтов ключа полезной нагрузки, включая любое переполнение.
  • Начальная часть полезной нагрузки, которая не переходит на страницы переполнения.
  • 4-байтовый номер страницы целого числа с обратным порядком байтов для первой страницы списка страницы переполнения. Пропущено, если вся полезная нагрузка находится на странице b-дерева.

Информация выше может быть переделана в формат таблицы следующим образом:

Формат ячеек B-дерева
Тип данных Появляется в Описание
Лист таблицы (0x0d) Интерьер таблицы (0x05) Лист индекса (0x0a) Интерьер индекса (0x02)
4-byte integer     Номер страницы левого потомка
varint   Число байтов полезной нагрузки
varint     Rowid
byte array   Полезная нагрузка
4-byte integer   Номер страницы первой страницы переполнения

Сумма полезной нагрузки, которая переходит на страницы переполнения, также зависит от типа страницы. Для следующих вычислений позвольте U быть применимым размером страницы базы данных, полный размер страницы меньше на зарезервированное пространство в конце каждой страницы. И позвольте P быть размером полезной нагрузки. Далее X представляет максимальную сумму полезной нагрузки, которая может быть сохранена непосредственно на странице b-дерева без перехода на страницу переполнения, а M представляет минимальное количество полезной нагрузки, которое должно быть сохранено на btree-странице, прежде чем будет позволено переполнение.

Лепестковый элемент B-дерева таблицы:

Пусть X = U-35. Если размер полезной нагрузки P меньше или равен X, вся полезная нагрузка сохранена на странице листа b-дерева. Пусть M = ((U-12)*32/255)-23, K = M+((P-M)%(U-4)). Если P больше X, то число байтов, сохраненных на странице листа b-дерева таблицы, является K, если K меньше или равен X. Иначе M. Число байтов, сохраненных на странице листа, никогда не меньше M.

Клетка интерьера B-дерева таблицы:

У внутренних страниц b-деревьев таблицы нет полезной нагрузки и таким образом, никогда нет никакого полезной нагрузки для переполнения.

Лист B-дерева индекса или внутренняя клетка:

Пусть X = ((U-12)*64/255)-23. Если размер полезной нагрузки P меньше или равен X, вся полезная нагрузка сохранена на странице b-дерева. Пусть M = ((U-12)*32/255)-23, K = M+((P-M)%(U-4)). Если P больше X, число байтов, сохраненных на странице b-дерева индекса, является K, если K меньше или равен X. Иначе M. Число байт, сохраненных на индексной странице, никогда не меньше M.

Вот альтернативное описание того же самого вычисления:

  • X = U-35 для таблицы btree страницы листа или ((U-12)*64/255)-23 для индексных страниц.
  • M всегда ((U-12)*32/255)-23.
  • Пусть K = M+((P-M)%(U-4)).
  • Если P<=X, все P байт полезной нагрузки сохранены непосредственно на btree-странице без переполнения.
  • Если P>X и K<=X первые байты K байт блока P сохранены на btree-странице, и остающиеся байты P-K сохранены на страницах переполнения.
  • Если P>X и K>X, первые M байт блока P сохранены на btree-странице, а остающиеся байты P-M сохранены на страницах переполнения.

Пороги переполнения разработаны, чтобы дать минимальное разветвление 4 для b-деревьев индекса и удостовериться, что достаточно полезной нагрузки находится на странице b-дерева, чтобы к заголовку записи можно обычно получить доступ, не консультируясь со страницей переполнения. В ретроспективе, проектировщик логики b-дерева SQLite понял, что эти пороги, возможно, были сделаны намного более простыми. Однако, вычисления не могут быть изменены, не приводя к несовместимому формату файла. И текущие вычисления работают хорошо, даже если они немного сложнее, чем надо.

1.7. Страницы переполнения полезной нагрузки клетки

Когда полезная нагрузка клетки b-дерева слишком большая для страницы b-дерева, излишек перенесен на страницы переполнения. Страницы переполнения формируют связанный список. Первые четыре байта каждой страницы переполнения это целое число с обратным порядком байтов, которое является номером страницы следующей страницы в цепи или нолем для заключительной страницы в цепи. Байты с пятого до конца используемых используются, чтобы хранить содержание переполнения.

1.8. Карта указателей или страницы Ptrmap

Карта указателей или ptrmap-страницы это дополнительные страницы, вставленные в базу данных, чтобы сделать операции auto_vacuum и incremental_vacuum более эффективными. У других типов страницы в базе данных, как правило, есть указатели от родителя потомку. Например, внутренняя страница b-дерева содержит указатели на свои дочерние страницы b-дерева, и у цепи переполнения есть указатель от ранних до более поздних связей в цепи. ptrmap-страница содержит информацию о связи в противоположном направлении, от потомка к родителю.

Страницы Ptrmap должны существовать в любом файле базы данных, у которого есть самое большое значение страницы b-дерева корня, отличное от нуля, в смещении 52 в заголовке базы данных. Если самое большое значение страницы b-дерева корня это ноль, то база данных не должна содержать ptrmap-страницы.

В базе данных с ptrmap-страницами первая ptrmap-страница это страница 2. ptrmap-страница состоит из множества 5-байтовых записей. Пусть J это количество 5-байтовых записей, которые поместятся в применимое пространство страницы. Другими словами, J=U/5. Первая ptrmap-страница будет содержать информацию об обратном указателе для страниц с 3 по J+2 включительно. Вторая страница карты указателей будет на странице J+3, и ptrmap-страница предоставит информацию об обратном указателе для страниц с J+4 по 2*J+3 включительно. И т.д для всего файла базы данных.

В базе данных, которая использует ptrmap-страницы, все страницы в местоположениях, определенных вычислением в предыдущем параграфе, должны быть ptrmap-страницами, и никакая другая страница не может быть ptrmap-страницей. Кроме того, если страница byte-lock падает на тот же самый номер страницы как ptrmap-страница, то ptrmap перемещена на следующую страницу для этого случая.

Каждый 5-байтовый вход на ptrmap-странице предоставляет информацию об обратной ссылке об одной из страниц, которые немедленно следуют за картой указателей. Если страница B это ptrmap-страница, информация об обратной ссылке о странице B+1 предоставляется первым входом на карте указателей. Информация о странице B+2 предоставляется вторым входом. И т.д.

Каждый 5-байтовый вход ptrmap состоит из одного байта "page type", сопровождаемого 4-байтовым номером страницы с обратным порядком байтов. 5 типов страницы доступны:

  1. Страница корня b-дерева. Номер страницы должен быть нолем.
  2. freelist-страница. Номер страницы должен быть нолем.
  3. Первая страница цепи переполнения полезной нагрузки клетки. Номер страницы это страница b-дерева, которая содержит клетку, содержание которой переполнилось.
  4. Страница в цепи переполнения кроме первой страницы. Номер страницы это предшествующая страница цепи переполнения.
  5. Некорневая страница b-дерева. Номер страницы это родительская страница b-дерева.

В любом файле базы данных, который содержит ptrmap-страницы, все страницы корня b-дерева должны быть перед любой некорневой страницей b-дерева, страницей переполнения полезной нагрузки клетки или freelist-страницей. Это ограничение гарантирует, что страница корня никогда не будет перемещаться во время автовакуума или возрастающего вакуума. Автовакуумная логика не знает, как обновить поле root_page в таблице sqlite_schema и таким образом, необходимо препятствовать тому, чтобы страницы корня были перемещены во время автовакуума, чтобы сохранить целостность таблицы sqlite_schema. Страницы корня перемещены в начало файла базы данных операциями CREATE TABLE, CREATE INDEX, DROP TABLE и DROP INDEX.

2. Слой схемы

Предшествующий текст описывает аспекты низкого уровня формата файла SQLite. Механизм b-дерева обеспечивает сильные и действенные средства доступа к большому набору данных. Эта секция опишет, как слой b-дерева низкого уровня используется, чтобы осуществить высокоуровневые возможности SQL.

2.1. Формат записи

Данные для страницы листа b-дерева таблицы и ключа индекса страницы b-дерева характеризовались выше как произвольная последовательность байтов. Предшествующее обсуждение упомянуло один ключ, являющийся меньше, чем другой, но не определило то, что такое "меньше, чем". Текущая секция обратится к этим упущениям.

Полезная нагрузка, то есть данные b-дерева таблицы или ключи индекса b-дерева всегда находится в "формате записи". Формат записи определяет последовательность значений, соответствующих колонкам в таблице или индексе. Формат записи определяет количество колонок, тип данных каждой колонки и содержание каждой колонки.

Формат записи делает широкое применение variable-length integer или varint, представление 64-битных целых чисел со знаком.

Запись содержит заголовок и тело, именно в этом порядке. Заголовок начинается с единственного varint, который определяет общее количество байтов в заголовке. Значение varint это размер заголовка в байтах, включая размер самого varint. После размера varint идет один или несколько дополнительных varint, один для каждого столбца. Эти дополнительные varint называют номерами "последовательного типа", они определяют тип данных каждой колонки, согласно следующей диаграмме:

Последовательные коды типа формата записи
Serial TypeРазмер содержимогоСмысл
0 0Значение NULL.
1 1 8-битное дополненное парами целое число.
2 2 16-битное дополненное парами целое число с обратным порядком байтов.
3 3 24-битное дополненное парами целое число с обратным порядком байтов.
4 4 32-битное дополненное парами целое число с обратным порядком байтов.
5 6 48-битное дополненное парами целое число с обратным порядком байтов.
6 8 64-битное дополненное парами целое число с обратным порядком байтов.
7 8 IEEE 754-2008 с обратным порядком байтов 64-битное число с плавающей точкой.
8 0integer 0. Только для schema format 4 и выше.
9 0integer 1. Только для schema format 4 и выше.
10,11 variable Зарезервировано для внутреннего пользования. Эти последовательные коды типа никогда не будут появляться в правильно построенном файле базы данных, но они могли бы использоваться в переходных и временных файлах базы данных, которые SQLite иногда производит для его собственного использования. Значения этих кодов могут меняться от одного выпуска SQLite к следующему.
N≥12 and even (N-12)/2 BLOB длиной (N-12)/2 байт.
N≥13 and odd (N-13)/2 Последовательность в текстовом кодировании длиной (N-13)/2 байт. nul-терминатор не сохранен.

Размер заголовка varint и последовательный тип varint будут обычно состоять из единственного байта. Последовательный тип varint для больших последовательностей и BLOB мог бы распространиться на два или три байта varint, но это исключение, а не правило. Формат varint очень эффективен при кодировании заголовка записи.

Значения для каждой колонки в записи немедленно следуют за заголовком. Для последовательных типов 0, 8, 9, 12 и 13, значение это 0 байт в длину. Если все колонки имеют эти типы, часть тела записи пуста.

У записи могло бы быть меньше значений, чем количество колонок в соответствующей таблице. Это может произойти, например, после того, как SQL-оператор ALTER TABLE ... ADD COLUMN увеличил число колонок в схеме таблицы, не изменяя существующие ранее строки в таблице. Отсутствующие значения в конце записи заполнены значениями по умолчанию для соответствующих колонок, определенных в схеме таблицы.

2.2. Порядок сортировки записей

Порядок ключей в b-дереве индекса определен порядком сортировки записей, что ключи представляют. Сравнение записей выполняется по колонкам. Колонки записи исследованы слева направо. Первая пара колонок, которые не равны, определяет относительный порядок двух записей. Порядок сортировки отдельных столбцов следующий:

  1. Значения NULL (serial type 0) отсортированы первыми.
  2. Числовые значения (последовательные типы с 1 по 9) отсортированы после NULL в числовом порядке.
  3. Текстовые значения (последовательные типы 13 и больше) отсортированы после числовых значений в порядке функций сопоставления колонок.
  4. BLOB (последовательные типы 12 и больше) отсортированы последними в порядке, определенном memcmp().

Функция сопоставления для каждой колонки необходима, чтобы вычислить порядок текстовых полей. SQLite определяет три встроенных функции сопоставления:

BINARY Встроенное сопоставление BINARY сравнивает строки побайтно, используя функцию memcmp() из стандартной библиотеки C.
NOCASE Сопоставление NOCASE похоже на BINARY за исключением того, что заглавные знаки ASCII (от 'A' до 'Z') свернуты в их строчные эквиваленты до сравнения. Только знаки ASCII свернуты. NOCASE не осуществляет общую цель сравнения unicode caseless.
RTRIM RTRIM подобен BINARY за исключением того, что дополнительные пробелы в конце любой последовательности не изменяют результат. Другими словами, последовательности выдержат сравнение равенства друг другу, пока они отличаются только по количеству пробелов в конце.

Дополнительные специализированные функции сопоставления могут быть добавлены к SQLite использованием sqlite3_create_collation().

Функция сопоставления по умолчанию для всех последовательностей BINARY. Альтернативные функции сопоставления для столбцов таблицы могут быть определены в CREATE TABLE, используя параметр COLLATE в определении столбца. Когда колонка внесена в указатель, та же самая функция сопоставления, определенная в CREATE TABLE, используется для колонки в индексе, по умолчанию, хотя это может быть отвергнуто, используя пункт COLLATE в CREATE INDEX.

2.3. Представление таблиц SQL

Каждая обычная таблица SQL в схеме базы данных представляется на диске b-деревом таблицы. Каждый вход в b-дереве таблицы соответствует строке таблицы SQL. rowid SQL-таблицы это 64-битный ключ целого числа со знаком для каждого входа в b-дереве таблицы.

Содержание каждой строки таблицы SQL сохранено в файле базы данных первым объединением значений в различных колонках в массив байтов в формате записи, затем храня этот массив байтов как полезную нагрузку во входе в b-дереве таблицы. Порядок значений в записи совпадает с порядком колонок в определении таблицы SQL. Когда таблица SQL включает INTEGER PRIMARY KEY (псевдоним для rowid), колонка появляется в записи как NULL. SQLite будет всегда использовать ключ b-дерева таблицы, а не NULL, ссылаясь на столбец INTEGER PRIMARY KEY.

Если близость колонки REAL, и та колонка содержит значение, которое может быть преобразовано в целое число без потери информации (если значение не содержит дробной части и не слишком большое, чтобы быть представленным как целое число), колонка может быть сохранена в записи как целое число. SQLite преобразует значение назад в плавающую точку, извлекая его из записи.

2.4. Представление таблиц WITHOUT ROWID

Если таблица SQL составлена, используя "WITHOUT ROWID" в CREATE TABLE, то таблица WITHOUT ROWID и использует иное представление на диске. Таблица WITHOUT ROWID использует b-дерево индекса, а не b-дерево таблицы для хранения. Ключ для каждого входа в b-дерево WITHOUT ROWID является записью из колонок PRIMARY KEY, сопровождаемых всеми остальными столбцами таблицы. Колонки первичного ключа появляются в порядке, в котором они были объявлены в PRIMARY KEY, остальные столбцы появляются в порядке, в котором они указаны в CREATE TABLE.

Следовательно, кодирование контента для таблицы WITHOUT ROWID совпадает с кодированием для обычной таблицы rowid за исключением того, что порядок колонок перестроен так, чтобы колонки PRIMARY KEY были на первом месте, и содержание, используется в качестве ключа в b-дереве индекса, а не в качестве данных в b-дереве таблицы. Специальные правила кодирования для колонок с близостью REAL относятся к WITHOUT ROWID, как и к rowid-таблицам.

2.4.1. Подавление избыточных колонок в PRIMARY KEY в WITHOUT ROWID

Если PRIMARY KEY в таблице WITHOUT ROWID используют те же самые колонки с той же самой последовательностью сопоставления несколько раз, то вторые и последующие случаи той колонки в определении PRIMARY KEY проигнорированы. Например, следующие CREATE TABLE определяют ту же самую таблицу, у которой будет то же самое представление на диске:

CREATE TABLE t1(a,b,c,d,PRIMARY KEY(a,c)) WITHOUT ROWID;
CREATE TABLE t1(a,b,c,d,PRIMARY KEY(a,c,a,c)) WITHOUT ROWID;
CREATE TABLE t1(a,b,c,d,PRIMARY KEY(a,A,a,C)) WITHOUT ROWID;
CREATE TABLE t1(a,b,c,d,PRIMARY KEY(a,a,a,a,c)) WITHOUT ROWID;

Первый пример выше это предпочтительное определение таблицы, конечно. Все примеры создают таблицу WITHOUT ROWID с PRIMARY KEY "a" и "c", в этом порядке, с двумя столбцами данных "b" и "d", тоже в этом порядке.

2.5. Представление индексов SQL

Каждый индекс SQL, неважно: заявлен он явно через CREATE INDEX или реализован ограничением UNIQUE или PRIMARY KEY, соответствует b-дереву индекса в файле базы данных. Каждый вход в b-дереве индекса соответствует единственной строке в связанной таблице SQL. Ключ к b-дереву индекса это запись, состоящая из колонок, которые вносятся в указатель сопровождаемые ключом соответствующей строки таблицы. Для обычных таблиц ключ строки это ее rowid, для таблиц WITHOUT ROWID ключ строки это PRIMARY KEY. Поскольку у каждой строки в таблице есть уникальный ключ, все ключи в индексе уникальны.

В нормальном индексе есть непосредственное отображение между строками в таблице и записи в каждом индексе, связанном с таблицей. Однако, в частичном индексе b-дерево индекса только содержит записи, соответствующие строкам таблицы, для которых выражение оператора Where в CREATE INDEX = true. Соответствующие строки в индексе и b-деревьях таблицы разделяют тот же самый rowid или значения первичного ключа и содержат то же самое значение для всех индексированных столбцов.

2.5.1. Подавление избыточных колонок во вторичных индексах WITHOUT ROWID

В индексе на таблице WITHOUT ROWID, если колонка PRIMARY KEY также колонка в индексе и имеет последовательность сопоставления соответствия, то индексированный столбец не повторяется в ключевом для таблицы суффиксе на конце записи индекса. Как пример, рассмотрите следующий SQL:

CREATE TABLE ex25(a,b,c,d,e,PRIMARY KEY(d,c,a)) WITHOUT rowid;
CREATE INDEX ex25ce ON ex25(c,e);
CREATE INDEX ex25acde ON ex25(a,c,d,e);
CREATE INDEX ex25ae ON ex25(a COLLATE nocase,e);

Каждая строка в индексе ex25ce это запись с этими колонками: c, e, d, a. Первые две колонки это внесенные в указатель колонки, c и e. Остальные столбцы это первичный ключ соответствующей строки таблицы. Обычно первичный ключ был бы колонками d, c и a, но потому что колонка c уже появляется ранее в индексе, это опущено в ключевом суффиксе.

В крайнем случае, где колонки, внесенные в указатель покрывают все колонки PRIMARY KEY, индекс будет состоять только из внесенных в указатель колонок. Пример ex25acde выше демонстрирует это. Каждый вход в индексе ex25acde состоит только из колонок a, c, d, и e, именно в этом порядке.

Каждая строка в ex25ae содержит пять колонок: a, e, d, c, a. "a" повторяется, так как у первого возникновения "a" есть функция сопоставления "nocase", а у второго есть последовательность сопоставления "binary". Если колонка "a" не повторяется и если таблица содержит две или больше записей с тем же самым значением "e", где "a" отличается только регистром, то все те записи таблицы соответствовали бы одному входу в индексе, что сломает взаимно-однозначное соответствие между таблицей и индексом.

Подавление избыточных колонок в ключевом суффиксе элемента индекса происходит только в таблицах WITHOUT ROWID. В обычной таблице rowid элемент индекса всегда заканчивается rowid, даже если INTEGER PRIMARY KEY является одной из внесенных в указатель колонок.

2.6. Хранение схемы базы данных SQL

Страница 1 файла базы данных это страница корня b-дерева таблицы, которое хранит специальную таблицу "sqlite_schema". Это b-дерево известно как "таблица схемы", так как это хранит полную схему базы данных. Структура таблицы sqlite_schema:

CREATE TABLE sqlite_schema(type text, name text, tbl_name text,
                           rootpage integer, sql text);

Таблица sqlite_schema содержит одну строку для каждой таблицы, индекса, обзора и триггера (коллективно "объекты") в схеме базы данных, нет никакого входа для самой таблицы sqlite_schema. Таблица sqlite_schema содержит записи для внутренних объектов схемы в дополнение к определенным программистами объектам.

Столбец sqlite_schema.type будет одной из следующих текстовых строк: 'table', 'index', 'view' или 'trigger', согласно типу объекта. Строка 'table' используется для обычных и для виртуальных таблиц.

Столбец sqlite_schema.name хранит название объекта. Ограничения UNIQUE и PRIMARY KEY заставляют SQLite создавать внутренние индексы с именами вида "sqlite_autoindex_TABLE_N", где TABLE заменяется названием таблицы, которая содержит ограничение, а N целое число, начинающееся с 1 и увеличивающееся на 1 с каждым ограничением в определении таблицы. В таблице WITHOUT ROWID нет никакого входа sqlite_schema для PRIMARY KEY, но имя "sqlite_autoindex_TABLE_N" обойдено для PRIMARY KEY, как будто sqlite_schema вход действительно существовал. Это затронет нумерацию последующих ограничений UNIQUE. Имя "sqlite_autoindex_TABLE_N" никогда не ассигнуется для INTEGER PRIMARY KEY в таблицах.

Столбец sqlite_schema.tbl_name хранит название таблицы или обзора, с которым объект связан. Для таблицы или представления колонка tbl_name это копия колонки имени. Для индекса tbl_name это название таблицы, которая внесена в указатель. Для триггера tbl_name хранит название таблицы или обзора, который заставляет триггер работать.

Столбец sqlite_schema.rootpage хранит номер страницы страницы b-дерева корня для таблиц и индексов. Для строк, которые определяют обзоры, триггеры и виртуальные таблицы, столбец rootpage 0 или NULL.

Столбец sqlite_schema.sql хранит код на SQL, который описывает объект. Этот код на SQL CREATE TABLE, CREATE VIRTUAL TABLE, CREATE INDEX, CREATE VIEW или CREATE TRIGGER, который, если оценен для файла базы данных, когда это главная база данных соединения с базой данных, воссоздал бы объект. Текст обычно копия оригинального запроса, используемого, чтобы создать объект, но с нормализацией, примененной так, чтобы текст соответствовал следующим правилам:

  • CREATE, TABLE, VIEW, TRIGGER и INDEX в начале запроса преобразовываются во все прописные буквы.
  • TEMP или TEMPORARY удалены, если они после начального ключевого слова CREATE.
  • Любой определитель имени базы данных, который происходит до названия создаваемого объекта, удален.
  • Удалены ведущие пробелы.
  • Все пробелы после первых двух ключевых слов преобразовываются в одиночный пробел.

Текст в столбце sqlite_schema.sql это копия оригинального текста инструкции CREATE, которая создала объект, кроме нормализованного, как описано выше и, измененного последующими ALTER TABLE. sqlite_schema.sql = NULL для внутренних индексов, которые автоматически создаются ограничениями UNIQUE или PRIMARY KEY.

2.6.1. Альтернативные названия таблицы схемы

Имя "sqlite_schema" не появляется нигде в формате файла. То имя просто соглашение, используемое внедрением базы данных. Из-за исторических и эксплуатационных соображений, таблица "sqlite_schema" может также иногда называться одним из следующих псевдонимов:

  1. sqlite_master
  2. sqlite_temp_schema
  3. sqlite_temp_master

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

2.6.2. Внутренние объекты схемы

В дополнение к таблицам, индексам, обзорам и триггерам, созданным применением и/или разработчиком, используя CREATE, таблица sqlite_schema может содержать ноль или больше записей для внутренних объектов схемы, которые создаются SQLite для его собственного внутреннего пользования. Названия внутренних объектов схемы всегда начинаются с "sqlite_" и любая таблица, индекс, представление или триггер, имя которого начинается с "sqlite_", являются внутренним объектом схемы. SQLite мешает создать объекты, имена которых начинаются с "sqlite_".

Внутренние объекты схемы, используемые SQLite, могут включать следующее:

  • Индексы с названиями формы "sqlite_autoindex_TABLE_N", которые используются, чтобы осуществить ограничения UNIQUE и PRIMARY KEY на обычных таблицах.

  • Таблица с именем "sqlite_sequence", которая используется, чтобы отслеживать максимальный исторический INTEGER PRIMARY KEY для таблицы, используя AUTOINCREMENT.

  • Таблицы с названиями формы "sqlite_statN", где N это integer. Такие таблицы хранят статистику базы данных, собранную командой ANALYZE и используемую планировщиком запроса, чтобы помочь определить лучший алгоритм для каждого запроса.

Новые внутренние имена объектов схемы, всегда начинаются с "sqlite_" и могут быть добавлены к формату файла SQLite в будущих выпусках.

2.6.3. Таблица sqlite_sequence

Таблица sqlite_sequence это внутренняя таблица, используемая, чтобы помочь осуществить AUTOINCREMENT. sqlite_sequence составлена автоматически каждый раз, когда любая обычная таблица с первичным ключом целого числа AUTOINCREMENT составлена. После того, как создана, sqlite_sequence существует в таблице sqlite_schema всегда, это не может быть удалено. Схема для таблицы sqlite_sequence:

CREATE TABLE sqlite_sequence(name,seq);

Есть единственная строка в sqlite_sequence для каждой обычной таблицы, которая использует AUTOINCREMENT. Название таблицы (как это появляется в sqlite_schema.name) находится в поле sqlite_sequence.name и самый большой INTEGER PRIMARY KEY, когда-либо вставленный в таблицу, находится в sqlite_sequence.seq. Новые автоматически произведенные первичные ключи целого числа для таблиц AUTOINCREMENT, как гарантируют, будут больше, чем sqlite_sequence.seq для той таблицы. Если поле sqlite_sequence.seq таблицы AUTOINCREMENT уже в самом большом целочисленном значении (9223372036854775807), попытки добавить новые строки к той таблице с автоматически произведенным основным целым числом потерпят неудачу с ошибкой SQLITE_FULL. Поле sqlite_sequence.seq автоматически обновляется при необходимости, когда новые записи вставляются в таблицу AUTOINCREMENT. Строка sqlite_sequence для таблиц AUTOINCREMENT автоматически удалена, когда таблица удалена. Если строка sqlite_sequence для таблицы AUTOINCREMENT не существует, когда таблица AUTOINCREMENT обновляется, то новая строка sqlite_sequence создана. Если значение sqlite_sequence.seq для таблицы AUTOINCREMENT вручную установлено к чему-то другому, чем целое число и есть последующая попытка вставить или обновить таблицу AUTOINCREMENT, то поведение не определено.

Коду приложения позволяют изменить таблицу sqlite_sequence, чтобы добавить новые строки, удалить или изменить существующие строки. Однако, код приложения не может составить таблицу sqlite_sequence, если она еще не существует. Код приложения может удалить все записи из sqlite_sequence, но не саму таблицу.

2.6.4. Таблица sqlite_stat1

Таблица sqlite_stat1 это внутренняя таблица, составленная командой ANALYZE, используемая, чтобы содержать дополнительную информацию о таблицах и индексах, которые планировщик запроса может использовать, чтобы помочь найти лучшие способы выполнить запросы. Приложения могут обновить, удалить из, вставить в или удалить всю таблицу sqlite_stat1, но не могут создать или изменить sqlite_stat1. Схема таблицы sqlite_stat1 следующая:

CREATE TABLE sqlite_stat1(tbl,idx,stat);

Обычно есть одна строка на индекс с индексом, определенным именем в колонке sqlite_stat1.idx. Столбец sqlite_stat1.tbl это название таблицы, которой принадлежит индекс. В каждой такой строке колонка sqlite_stat.stat будет последовательностью, состоящей из списка целых чисел, сопровождаемых нолем или большим количеством аргументов. Первое целое число в этом списке задает приблизительное количество строк в индексе. Количество строк в индексе совпадает с количеством строк в таблице, за исключением частичных индексов. Второе целое число это приблизительное количество строк в индексе, у которых есть то же самое значение в первой колонке индекса. Третье целое число это количество строк в индексе, у которых есть то же самое значение для первых двух колонок. Энное целое число (для N> 1) это предполагаемое среднее количество строк в индексе, у которых есть то же самое значение для первых N-1 колонок. Для индекса колонки K в колонке статистики будут целые числа K+1. Если индекс будет уникален, то последнее целое число будет 1.

Список целых чисел в колонке статистики может произвольно сопровождаться аргументами, каждый из которых является последовательностью непробелов. Всем аргументам предшествует одиночный пробел. Непризнанные аргументы тихо проигнорированы.

Если аргумент "unordered" присутствует, то планировщик запроса предполагает, что индекс не упорядочен и не будет использовать индекс для запроса диапазона или для сортировки.

Аргумент "sz=NNN" (NNN представляет последовательность из 1 или более цифр) означает, что средний размер строки по всем записям таблицы или индекса NNN байт на строку. Планировщик запроса SQLite мог бы использовать предполагаемую информацию о размере строки, предоставленную "sz=NNN", чтобы помочь ему выбрать меньшие таблицы и индексы, которые требуют меньшего количества I/O диска.

Присутствие "noskipscan" в поле sqlite_stat1.stat индекса препятствует тому, чтобы тот индекс использовался с оптимизацией skip-scan.

Новые текстовые символы могут быть добавлены до конца колонки статистики в будущих улучшениях SQLite. Для совместимости тихо проигнорированы непризнанные символы в конце колонки статистики.

Если sqlite_stat1.idx = NULL, sqlite_stat1.stat содержит единственное целое число, которое является приблизительным количеством строк в таблице, определенной sqlite_stat1.tbl. Если sqlite_stat1.idx совпадает с sqlite_stat1.tbl, то таблица WITHOUT ROWID и поле sqlite_stat1.stat содержат информацию об индексе btree, который осуществляет таблицу WITHOUT ROWID.

2.6.5. Таблица sqlite_stat2

Таблица sqlite_stat2 создается и используется только если SQLite собран с SQLITE_ENABLE_STAT2 и если номер версии SQLite между 3.6.18 (2009-09-11) и 3.7.8 (2011-09-19). Таблица sqlite_stat2 не прочитана и не написана любой версией SQLite прежде 3.6.18 и после 3.7.8. sqlite_stat2 содержит дополнительную информацию о распределении ключей в индексе. Схема sqlite_stat2 таблицы следующая:

CREATE TABLE sqlite_stat2(tbl,idx,sampleno,sample);

Столбцы sqlite_stat2.idx и sqlite_stat2.tbl в каждой строке таблицы sqlite_stat2 определяют индекс, описанный той строкой. Обычно есть 10 строк в таблице sqlite_stat2 для каждого индекса.

Записи sqlite_stat2 для индекса, у которых есть sqlite_stat2.sampleno между 0 и 9, являются образцами крайнего левого значения ключа в индексе, взятом в равномерно расположенных пунктах вдоль индекса. Позвольте C быть количеством строк в индексе. Тогда выбранными строками будут:

rownumber = (i*C*2 + C)/20

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

Формат для sqlite_stat2 зарегистрирован здесь для справки. Последние версии SQLite больше не поддерживают sqlite_stat2, таблица sqlite_stat2, если существует, просто проигнорирована.

2.6.6. Таблица sqlite_stat3

Таблица sqlite_stat3 используется только если SQLite собран с SQLITE_ENABLE_STAT3 или SQLITE_ENABLE_STAT4 и если номер версии SQLite 3.7.9 (2011-11-01) или больше. sqlite_stat3 не прочитана и не написана любой версией SQLite прежде 3.7.9. Если выбор времени компиляции SQLITE_ENABLE_STAT4 используется, и номер версии SQLite 3.8.1 (2013-10-17) или больше, то sqlite_stat3 могла бы быть прочитана, но не написана. sqlite_stat3 содержит дополнительную информацию о распределении ключей в индексе, которую планировщик запроса может использовать, чтобы создать лучшие и более быстрые алгоритмы запроса. Схема sqlite_stat3 таблицы следующая:

CREATE TABLE sqlite_stat3(tbl,idx,nEq,nLt,nDLt,sample);

Есть обычно многократные входы в sqlite_stat3 для каждого индекса. Колонка sqlite_stat3.sample считает значение крайней левой области индекса определенной sqlite_stat3.idx и sqlite_stat3.tbl. Колонка sqlite_stat3.nEq хранит приблизительное количество записей в индексе, крайний левый столбец которого точно соответствует образцу. sqlite_stat3.nLt хранит приблизительное количество записей в индексе, крайний левый столбец которого меньше, чем образец. sqlite_stat3.nDLt хранит приблизительное количество отличных крайних левых записей в индексе, которые меньше, чем образец.

Может быть произвольное число sqlite_stat3 записей на индекс. Команда ANALYZE будет, как правило, производить таблицы sqlite_stat3, которые содержат между 10 и 40 образцами, которые распределяются по ключевому пространству и с большими значениями nEq.

В правильно построенной таблице sqlite_stat3 образцы для любого единственного индекса должны появиться в том же самом порядке, в каком они происходят в индексе. Другими словами, если вход с крайним левым столбцом S1 находится ранее в b-дереве индекса, чем вход с крайним левым столбцом S2, то в таблице sqlite_stat3 у образца S1 должен быть меньший rowid, чем у образца S2.

2.6.7. Таблица sqlite_stat4

Таблица sqlite_stat4 создается и используется только если SQLite собран с SQLITE_ENABLE_STAT4 и номер версии SQLite 3.8.1 (2013-10-17) или больше. sqlite_stat4 не прочитана и нн написана любой версией SQLite прежде 3.8.1. sqlite_stat4 содержит дополнительную информацию о распределении ключей в индексе или распределении ключей в первичном ключе таблицы WITHOUT ROWID . Планировщик запроса может иногда использовать дополнительную информацию в sqlite_stat4, чтобы создать лучшие и более быстрые алгоритмы запроса. Схема таблицы sqlite_stat4 следующая:

CREATE TABLE sqlite_stat4(tbl,idx,nEq,nLt,nDLt,sample);

Как правило, есть от 10 до 40 записей в таблице sqlite_stat4 для каждого индекса, для которого статистические данные доступны, однако эти пределы не жесткие границы. Значения колонок в sqlite_stat4:

tbl: Столбец sqlite_stat4.tbl хранит название таблицы, которая владеет индексом, который описывает строка.
idx: Столбец sqlite_stat4.idx хранит название индекса, который строка описывает, или в случае входа sqlite_stat4 для таблицы WITHOUT ROWID, название самой таблицы.
sample: Столбец sqlite_stat4.sample хранит BLOB в формате записи, который кодирует индексированные столбцы, сопровождаемые rowid для rowid-таблицы или колонками первичного ключа для таблицы WITHOUT ROWID. sqlite_stat4.sample BLOB для таблицы WITHOUT ROWID содержит просто колонки первичного ключа. Позвольте количеству колонок, закодированных sqlite_stat4.sample blob, быть N. Для индексов на обычной rowid-таблице N будет на 1 больше, чем количество внесенных в указатель колонок. Для индексов на таблице WITHOUT ROWID N будет количеством колонок, внесенных в указатель плюс количество колонок в первичном ключе. Для таблицы WITHOUT ROWID N будет количеством колонок в первичном ключе.
nEq: Столбец sqlite_stat4.nEq хранит список целых чисел N, где K-е целое число приблизительное количество записей в индексе, чьи крайние левые колонки K точно соответствуют крайним левым столбцам образца K.
nLt: Столбец sqlite_stat4.nLt хранит список целых чисел N, где K-е целое число приблизительное количество записей в индексе, чьи крайние левые столбцы K коллективно меньше, чем крайние левые столбцы образца K.
nDLt: Столбец sqlite_stat4.nDLt хранит список целых чисел N, где K-е целое число приблизительное количество записей в индексе, которые отличны в первых колонках K и где крайние левые колонки K коллективно меньше, чем крайние левые колонки образца K.

sqlite_stat4 это обобщение sqlite_stat3. sqlite_stat3 предоставляет информацию о крайнем левом столбце индекса, тогда как sqlite_stat4 предоставляет информацию обо всех колонках индекса.

Может быть произвольное число записей sqlite_stat4 на индекс. ANALYZE будет, как правило, производить таблицы sqlite_stat4, которые содержат между 10 и 40 образцами, которые распределяются по ключевому пространству и с большими значениями nEq.

В правильно построенной таблице sqlite_stat4 образцы для любого единственного индекса должны появиться в том же самом порядке, в каком они происходят в индексе. Другими словами, если вход S1 находится ранее в b-дереве индекса, чем вход S2, то в таблице sqlite_stat4 у образца S1 должен быть меньший rowid, чем у S2.

3. Журнал обратной перемотки

Журнал обратной перемотки это файл, связанный с каждым файлом базы данных SQLite, который содержит информацию, используемую, чтобы вернуть файл базы данных к его начальному состоянию в ходе транзакции. Файл журнала обратной перемотки всегда располагается в том же самом каталоге, как файл базы данных и имеет то же самое имя, как файл базы данных, но с последовательностью "-journal". Может быть только единственный журнал обратной перемотки, связанный с каждой базой данных и следовательно может быть только одна транзакция записи, открытая для базы данных.

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

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

  1. Файл журнала обратной перемотки может быть удален.
  2. Файл журнала обратной перемотки может быть усечен к нулевой длине.
  3. Заголовок журнала обратной перемотки может быть переписан недействительным текстом заголовка (например, все ноли).

Эти три способа передать транзакцию соответствуют параметрам DELETE, TRUNCATE и PERSIST в journal_mode pragma.

Действительный журнал обратной перемотки начинается с заголовка в следующем формате:

Формат заголовка журнала обратной перемотки
СмещениеРазмерОписание
0 8 Строка заголовка: 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7
8 4 "Page Count" число страниц в следующем сегменте журнала или -1, чтобы обозначить все содержание до конца файла
12 4 Случайный ключ для контрольной суммы
16 4 Начальный размер базы данных в страницах
20 4 Размер сектора диска, принятого процессом, который написал этот журнал
24 4 Размер страниц в этом журнале

Заголовок журнала обратной перемотки дополнен нолями к размеру единственного сектора (как определено целым числом размера сектора в смещении 20). Заголовок находится в секторе отдельно, чтобы, если потеря питания произошла, информация, которая следует за заголовком, была (надо надеяться) не повреждена.

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

Позвольте размеру страницы базы данных (значение целого числа в смещении 24 в заголовке журнала) быть N. Тогда формат записи страницы следующий:

Формат записи страницы журнала обратной перемотки
СмещениеРазмерОписание
0 4 Номер страницы в файле базы данных
4 N Оригинальное содержание страницы до начала транзакции
N+4 4 Checksum

checksum это unsigned 32-bit integer, вычисляемый так:

  1. Инициализируйте контрольную сумму значением данного случая контрольной суммы, найденной в заголовке журнала в смещении 12.
  2. Инициализируйте индекс X, чтобы быть N-200, где N = размер страницы базы данных в байтах.
  3. Интерпретируйте байт в смещении X на странице как 8-битное целое без знака и добавьте значение этого целого числа к контрольной сумме.
  4. Вычтите 200 из X.
  5. Если X больше или равен нолю, вернитесь к шагу 3.

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

Позвольте значения количества страницы в смещении 8 в заголовке журнала быть M. Если M больше 0, тогда после того, как M записей страницы файл журнала могут быть дополнены нолями, к следующему кратному числу размера сектора, и другой заголовок журнала может быть вставлен. Все заголовки журнала в рамках того же самого журнала должны содержать тот же самый размер страницы и сектора базы данных.

Если M = -1 в начальном заголовке журнала, то количество записей страницы, которые следуют, вычисляется, считая, сколько записей страницы поместится в свободное место остатка файла журнала.

4. Журнал с упреждающей записью

Начиная с version 3.7.0 (2010-07-21), SQLite поддерживает новый механизм управления транзакциями, названный "журналом с упреждающей записью" или "WAL". Когда база данных находится в режиме WAL, все связи с той базой данных должны использовать WAL. Конкретная база данных будет использовать журнал обратной перемотки или WAL, но не обоих в то же время. WAL всегда располагается в том же самом каталоге, как файл базы данных и имеет то же самое имя, как файл базы данных, но с последовательностью "-wal".

4.1. Формат файла WAL

Файл WAL состоит из заголовка, сопровождаемого нолем или большим количеством "структур". Каждая структура делает запись пересмотренного содержания единственной страницы от файла базы данных. Все изменения базы данных зарегистрированы, записывая структуры в WAL. Транзакции передают, когда структура написана, которая содержит маркер передачи. Единственный WAL может и обычно делает запись многократных транзакций. Периодически содержание WAL возвращено в файл базы данных в операции, названной "checkpoint".

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

Заголовок WAL имеет 32 байта в размере и состоит из следующих восьми 32-битных значений целого без знака с обратным порядком байтов:

Формат заголовка WAL
СмещениеРазмерОписание
0 4 Магическое число. 0x377f0682 или 0x377f0683
4 4 Версия формата файла. Сейчас 3007000.
8 4 Размер страницы базы данных. Пример: 1024
12 4 Порядковый номер контрольной точки
16 4 Salt-1: случайный integer, увеличен с каждой контрольной точкой
20 4 Salt-2: другое случайное число для каждой контрольной точки
24 4 Checksum-1: Первая часть контрольной суммы в первых 24 байтах заголовка
28 4 Checksum-2: Вторая часть контрольной суммы в первых 24 байтах заголовка

Немедленно после wal-заголовка хранится ноль или больше структур. Каждая структура состоит из 24-байтового заголовка структуры, сопровождаемого < i>размером страницы байт данных о странице. Заголовок структуры это шесть 32-битных значений целого без знака с обратным порядком байтов:

Формат заголовка тела WAL
СмещениеРазмерОписание
0 4 Номер страницы
4 4 Для записей коммитов размер файла базы данных в страницах после передачи. Для всех других записей 0
8 4 Salt-1 от заголовка WAL
12 4 Salt-2 от заголовка WAL
16 4 Checksum-1: Совокупная контрольная сумма через эту страницу, включая ее
20 4 Checksum-2: Вторая половина совокупной контрольной суммы

Структуру считают действительной, если и только если следующие условия верны:

  1. salt-1 и salt-2 в заголовке структуры соответствуют salt в заголовке wal

  2. checksum в заключительных 8 байтах заголовка структуры точно соответствуют контрольной сумме, вычисленной последовательно на первых 24 байтах заголовка WAL и первых 8 байтах и содержании всех структур до и включая текущую структуру.

4.2. Алгоритм контрольной суммы

Контрольная сумма вычисляется, интерпретируя вход как четное количество 32-битных целых чисел без знака с x(0) по x(N). 32-bit integer имеют обратный порядок байтов, если магическое число в первых 4 байтах заголовка WAL 0x377f0683 и прямой порядок байтов, если магическое число 0x377f0682. Значения контрольной суммы всегда хранятся в заголовке структуры в формате с обратным порядком байтов, независимо от того, какой порядок байтов используется, чтобы вычислить контрольную сумму.

Алгоритм контрольной суммы только работает на содержании, которое является кратным числу 8 байтов в длину. Другими словами, если входы с x(0) по x(N), N четное. Алгоритм контрольной суммы:

s0 = s1 = 0
for i from 0 to n-1 step 2:
  s0 += x(i) + s1;
  s1 += x(i+1) + s0;
endfor
# result in s0 and s1

Вывод s0 и s1 оба являются контрольными суммами, используя веса Fibonacci в обратном порядке. Самый большой вес Фибоначчи возникает на первом элементе суммируемой последовательности. Значение s1 охватывает все 32-битные integer последовательности, тогда как s0 опускает заключительное.

4.3. Алгоритм контрольной точки

При checkpoint WAL сначала сбрасывается к постоянному хранению, используя метод xSync VFS. Тогда действительное содержание WAL передается в файл базы данных. Наконец, база данных сбрасывается к постоянному хранению, используя другой вызов метода xSync. Операции xSync работают как барьеры записи: все записи, начатые прежде, чем xSync вызван, должны будут закончиться прежде, чем вызван следующий xSync.

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

4.4. Сброс WAL

После полной контрольной точки, если никакие другие связи не находятся в транзакциях, которые используют WAL, последующие транзакции записи могут переписать файл WAL с начала. Это называют "сбросом WAL". В начале первой новой транзакции значение salt-1 в заголовке WAL увеличено и salt-2 рандомизировано. Эти изменения лишают законной силы старые структуры в WAL, которые уже были отработаны, но еще не переписаны, и препятствует тому, чтобы они были сохранены снова.

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

4.5. Алгоритм чтения

Чтобы прочитать страницу от базы данных (называют ее по номеру страницы P), читатель сначала проверяет WAL, чтобы видеть, содержит ли это страницу P. Если так, тогда последний действительный случай страницы P, который сопровождается структурой передачи или является самой структурой передачи, становится прочитанным значением. Если WAL не содержит копий страницы P, которые действительны и являются структурой передачи или сопровождаются структурой передачи, страница P прочитана из файла базы данных.

Чтобы начать транзакцию чтения, читатель делает запись значения количества структур в WAL как "mxFrame". Читатель использует эту запись значения mxFrame для всех последующих операций чтения. Новые транзакции могут быть добавлены к WAL, но пока читатель использует первоначальное значение mxFrame и игнорирует впоследствии добавленное содержание, читатель будет видеть последовательный образ базы данных от единственного момента времени. Эта техника позволяет многократным параллельным читателям рассматривать различные версии содержания базы данных одновременно.

Алгоритм читателя в предыдущих параграфах работает правильно, но потому что структуры для страницы P могут появиться где угодно в WAL, читатель должен просмотреть весь WAL в поисках структур страницы P. Если WAL большой (многократные мегабайты типично), просмотр может быть медленным, производительность чтения страдает. Чтобы преодолеть эту проблему, отдельная структура данных, названная wal-индексом, сохраняется, чтобы ускорить поиск структур конкретной страницы.

4.6. Формат индекса WAL

Концептуально, wal-индекс это общая память, хотя текущие внедрения VFS используют файл с отображенной памятью для мобильности операционной системы. Файл с отображенной памятью находится в том же самом каталоге, как база данных и имеет то же самое имя, как база данных с суффиксом "-shm". Поскольку wal-индекс это общая память, SQLite не поддерживает journal_mode=WAL в сетевой файловой системе, когда клиенты находятся на различных машинах, поскольку все клиенты базы данных должны быть в состоянии разделить ту же самую память.

Цель wal-индекса состоит в том, чтобы ответить на этот запрос быстро:

Учитывая номер страницы P и максимальный индекс M структуры WAL, возвратите самый большой индекс структуры WAL для страницы P, которая не превышает M, или NULL при отсутствии структур для страницы P, которые не превышают M.

Значение M в предыдущем параграфе это значение "mxFrame", определенное в главе 4.4, которое прочитано в начале транзакции и определяет максимальную структуру из WAL, которую будет использовать читатель.

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

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