Статьи Новости Контакты
MySQL (0)

26.03.2008
Даниил Буров

SQLite: Передача одиночного файла

описание принципов работы SQLite

Начальное состояние системы

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

Начальное состояние

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

Соединение считывания

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

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

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

Чтение информации из базы данных

Считывание из БД

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

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

Зарезервированное соединение

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

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

Создание файла журнала восстановления

Создание файла восстановления

До проведения изменений SQLite сначала создает отдельный файл с журналом восстановления (rollback journal file), куда записывает первоначальное содержимое тех страниц, которые подлежат изменению. Идея такого журнала заключается в том, что он содержит всю необходимую информацию, чтобы была возможность восстановить базу данных в ее первоначальном состоянии.

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

Когда создается новый файл, большинство операционных систем (windows, linux, macOSX) фактически ничего на диск не записывают. Новый фал создается только в буфере операционной системы. Файл не создается на запоминающем устройстве, пока система не сэкономит для этого время. Благодаря этому, у пользователя создается впечатление, что ввод-вывод производится намного быстрее. Быстрее, чем это возможно на самом деле, когда это действительно делается на диск. Идея иллюстрируется на рисунке, где показывается, как новый журнал восстановления появляется только в буфере операционной системы, но не на жестком диске.

Изменение страниц БД в пространстве пользователя

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

Запись на диск файлов журнала восстановления

Запись файлов восстановления

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

Данный этап обычно является более сложным, чем простое сбрасывание на диск журнала с файлами восстановления. На большинстве платформ требуются две отдельные flush (или fsync()) операции. Первая flush-операция выписывает основное содержание журнала восстановления. Затем заголовок журнала восстановления изменяется, чтобы он отображал количество страниц в этом журнале. После этого заголовок записывается на жесткий диск. Детали того, зачем производится это изменение заголовка и дополнительная flush-операция, приведены далее.

Получение исключительной блокировки

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

"Ожидающая обработки" блокировка позволяет работать уже образованным открытым соединениям, но блокирует подключение к БД новых
Ожидающее обработки соединение позволяет другим процессам, которые уже имеют общедоступное соединение, продолжать считывание из файла базы данных. Но оно препятствует установлению новых общедоступных соединений. Идея подобной блокировки заключается в том, чтобы предотвратить зависание программы записи, вызванное большим количеством читающих запросов. Может быть множество, целые сотни, других процессов, пытающихся прочитать файл базы данных. Каждый процесс получает общедоступное соединение прежде, чем начинает считывать информацию. Затем он производит считывание, после чего общедоступное соединение пропадает. Если при этом имеется много различных процессов, считывающих информацию из одной базы данных, то может случиться так, что новый процесс получить общедоступное соединение до того, как предыдущий процесс его потеряет. Таким образом, может не быть момента, чтобы файл базы данных не был занят каким-либо общедоступным соединением, а следовательно, программа записи не сможет установить исключительное соединение. Ожидающая обработки блокировка разработана, чтобы предотвратить этот цикл, позволяя действовать существующим открытым соединениям, но препятствуя установлению новых. В конечном счете, все общедоступные блокировки будут сняты, и ожидающее обработки соединение получит возможность перерасти в исключительное.

Запись изменений в файл базы данных

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

Запись изменений на запоминающее устройство

Запись изменений на диск

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

Удаления журнала восстановления

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

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

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

Снятие блокировки

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




Скоро на сайте

  • Wordpress

    Серия статей о плагинах к движку WordPrress
  • AJAX

    Проекты и продукты, ориентированные на AJAX
  • Новые сервисы Google

    Обзор новых сервисов Google
 

Copyright © 2003—2018 Все права защищены

При использовании материалов сайта ссылка на hostinfo.ru обязательна

  • хостинг от .masterhost
  • Rambler's Top100