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

Small. Fast. Reliable.
Choose any three.

Асинхронный модуль I/O для SQLite


Примечание: WAL mode с PRAGMA synchronous = NORMAL избегает вызова fsync() во время передачи транзакции и вызывает fsync() только во время операции checkpoint. Использование WAL mode в основном устраняет потребность в этом асинхронном модуле I/O. Следовательно, этот модуль больше не поддерживается. Исходный код продолжает существовать в исходном дереве SQLite, но это не часть никакого стандарта сборки и больше не сохраняется. Эта документация сохраняется для исторической справки.


Обычно, когда SQLite пишет файл базы данных, он ждет, пока операция записи не будет закончена перед возвращением контроля вызывающему приложению. Запись файловой системы обычно очень медлена по сравнению со связанными действиями CPU, это может быть исполнительным узким местом. Асинхронный бэкенд I/O это расширение, которое заставляет SQLite выполнять все пишущие запросы, используя отдельный поток в фоновом режиме. Хотя это не уменьшает полные системные ресурсы (CPU, дисковая пропускная способность и т.д.), это действительно позволяет SQLite возвращать контроль при записи базы данных.

1.0. ФУНКЦИОНАЛЬНОСТЬ

С асинхронным I/O запросы записи обработаны отдельным потоком в фоновом режиме. Это означает, что поток, который начинает запись базы данных, не должен ждать (иногда медленного) дискового I/O. Запись, кажется, происходит очень быстро, хотя в действительности это происходит в своем обычном медленном темпе в фоновом режиме.

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

Вы теряете Durability с асинхронным I/O, но вы все еще сохраняете другие части ACID: Atomic, Consistent и Isolated. Много приложений прожиут прекрасно без Durability.

1.1. Как это работает

Асинхронный I/O работает, создавая объект SQLite VFS и регистрируя его в sqlite3_vfs_register(). Когда файлы, открытые через этот VFS, написаны (использованием метода vfs xWrite()), данные не написаны непосредственно на диск, а помещаются в очередь записи, чтобы быть обработанными фоновым потоком.

Когда файлы, открытые с асинхронным VFS, прочитаны (использованием метода vfs xRead()), данные прочитаны из файла на диске и очереди записи так, чтобы с точки зрения читателя xWrite() уже закончил работу.

Асинхронный I/O VFS зарегистрирован вызовом API sqlite3async_initialize() и sqlite3async_shutdown().

1.2. Ограничения

Чтобы приобрести опыт с главными идеями, окружающими асинхронный IO, это внедрение сознательно сохранено простым. Дополнительные возможности могут быть добавлены в будущем.

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

1.3. Захват и параллелизм

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

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

  • Когда связь, используя асинхронный IO начинает транзакцию базы данных, база данных немедленно блокирована. Однако, блокировка не выпущена, пока все соответствующие операции в очереди записи не сброшены на диск. Это означает (например), что база данных может остаться блокированной в течение некоторого времени после "COMMIT" или "ROLLBACK".

  • Если приложение, используя асинхронный IO выполняет транзакции в быстрой последовательности, другие пользователи базы данных могут быть эффективно блокированы. Это вызвано тем, что когда выполняется BEGIN, блокировка базы данных немедленно устанавливается. Но когда выполнен соответствующий COMMIT или ROLLBACK, блокировка не выпущена, пока соответствующая часть очереди записи не сброшена. В результате, если COMMIT сопровождается BEGIN, прежде чем очередь записи сбросится, базу данных никогда не открывают, препятствуя тому, чтобы другие процессы получили доступ к базе данных.

Захват файла может быть отключен во время выполнения, используя sqlite3async_control() API. Это может улучшить работу, когда применена NFS или другая сетевая файловая система. Однако, если многократные связи пытаются получить доступ к тому же самому файлу базы данных, когда захват файла отключен, сбои приложения и повреждения базы данных это вероятный результат.

2.0. КОМПИЛЯЦИЯ И ИСПОЛЬЗОВАНИЕ

Асинхронное расширение IO состоит из единственного файла кода C (sqlite3async.c) и заголовочного файла (sqlite3async.h), находящихся в подкаталоге ext/async дерева исходных текстов SQLite, которое определяет C API для приложений, чтобы активировать и управлять функциональностью модулей.

Чтобы использовать асинхронное расширение IO, соберите sqlite3async.c как часть приложения, которое использует SQLite. Затем используйте API, определенный в sqlite3async.h, чтобы инициализировать и формировать модуль.

Асинхронный IO VFS API описан подробно в комментариях в sqlite3async.h. Использование API обычно состоит из следующих шагов:

  1. Зарегистрируйте асинхронный IO VFS в SQLite вызовом sqlite3async_initialize().

  2. Создайте фоновый поток, чтобы выполнить операции записи и вызовите sqlite3async_run().

  3. Используйте нормальный API SQLite, чтобы читать и писать базы данных через асинхронный IO VFS.

Обратитесь к комментариям в файле заголовка sqlite3async.h за подробностями.

3.0. ПОРТИРОВАНИЕ

В настоящее время асинхронное расширение IO совместимо с win32 и системами, которые поддерживают интерфейс pthreads, включая Mac OS X, Linux и другие варианты Unix.

Чтобы портировать асинхронное расширение IO на другую платформу, пользователь должен осуществить mutex и примитивы условной переменной для новой платформы. В настоящее время нет никакого внешне доступного интерфейса, чтобы позволить это, но изменение кода в sqlite3async.c, чтобы включать новые примитивы параллелизма платформ относительно легко. Ищите в sqlite3async.c строку комментария "PORTING FUNCTIONS". Затем осуществите новые версии следующего:

static void async_mutex_enter(int eMutex);
static void async_mutex_leave(int eMutex);
static void async_cond_wait(int eCond, int eMutex);
static void async_cond_signal(int eCond);
static void async_sched_yield(void);

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