Безопасно ли обрабатывает SQLite3 одновременный доступ нескольких процессов чтения / записи из одной и той же БД? Есть ли какие-либо исключения для платформы?
Безопасно ли обрабатывает SQLite3 одновременный доступ нескольких процессов чтения / записи из одной и той же БД? Есть ли какие-либо исключения для платформы?
Ответы:
Если большинство из этих одновременных обращений - чтение (например, SELECT), SQLite может справиться с ними очень хорошо. Но если вы начнете писать одновременно, конфликт блокировки может стать проблемой. Многое будет зависеть от скорости вашей файловой системы, так как сам механизм SQLite чрезвычайно быстр и имеет много умных оптимизаций для минимизации конфликтов. Особенно SQLite 3.
Для большинства приложений для настольных ПК / ноутбуков / планшетов / телефонов SQLite достаточно быстр, поскольку не хватает параллелизма. (Firefox широко использует SQLite для закладок, истории и т. Д.)
Что касается серверных приложений, кто-то некоторое время назад сказал, что все, что меньше 100К просмотров страниц в день, может отлично обрабатываться базой данных SQLite в типичных сценариях (например, в блогах, на форумах), и я пока не вижу никаких доказательств обратного. Фактически, с современными дисками и процессорами, 95% веб-сайтов и веб-сервисов будут прекрасно работать с SQLite.
Если вы хотите действительно быстрый доступ для чтения / записи, используйте базу данных SQLite в памяти . Оперативная память на несколько порядков быстрее дисковой.
Да, SQLite хорошо справляется с параллелизмом, но он не самый лучший с точки зрения производительности. Из того, что я могу сказать, нет никаких исключений. Подробности на сайте SQLite: https://www.sqlite.org/lockingv3.html
Это утверждение представляет интерес: «Модуль пейджера следит за тем, чтобы изменения происходили одновременно, чтобы все изменения произошли или ни один из них не произошел, чтобы два или более процесса не пытались одновременно обращаться к базе данных несовместимыми способами»
Да, это так. Давайте выясним, почему
Все изменения в пределах одной транзакции в SQLite происходят полностью или не происходят вообще
Такая поддержка ACID, а также одновременное чтение / запись предоставляются двумя способами - с использованием так называемого ведения журнала (давайте назовем это « старым способом ») или ведения записи с опережением записи (давайте назовем это « новым способом »)
В этом режиме SQLite использует блокировку DATABASE-LEVEL . Это важный момент для понимания.
Это означает, что всякий раз, когда ему нужно что-то прочитать / записать, он сначала получает блокировку на ВСЁ файле базы данных. Несколько читателей могут сосуществовать и читать что-то параллельно
Во время записи он гарантирует, что получена исключительная блокировка, и никакой другой процесс не читает / пишет одновременно, и, следовательно, запись безопасна.
Вот почему здесь говорится, что SQlite реализует сериализуемые транзакции
Поскольку необходимо каждый раз блокировать всю базу данных, и все ожидают, что процесс, обрабатывающий запись, страдает от параллелизма, и такие параллельные операции записи / чтения имеют довольно низкую производительность.
Перед записью чего-либо в файл базы данных SQLite сначала сохраняет чанк, подлежащий изменению, во временном файле. Если что-то падает во время записи в файл базы данных, он подхватывает этот временный файл и отменяет изменения из него.
В этом случае все записи добавляются во временный файл ( журнал предварительной записи ), и этот файл периодически объединяется с исходной базой данных. Когда SQLite что-то ищет, он сначала проверит этот временный файл, а если ничего не будет найдено, перейдите к основному файлу базы данных.
В результате, читатели не конкурируют с писателями, и производительность намного лучше по сравнению со старым путем.
SQlite сильно зависит от базовой функциональности блокировки файловой системы, поэтому его следует использовать с осторожностью, более подробно здесь
Вы также можете столкнуться с ошибкой заблокированной базы данных , особенно в журнальном режиме, поэтому ваше приложение должно быть спроектировано с учетом этой ошибки.
Никто, кажется, не упомянул режим WAL (Write Ahead Log). Убедитесь, что транзакции правильно организованы и при включенном режиме WAL нет необходимости держать базу данных заблокированной, пока люди читают что-то во время обновления.
Единственная проблема заключается в том, что в какой-то момент WAL необходимо повторно включить в основную базу данных, и он делает это, когда закрывается последнее соединение с базой данных. На очень загруженном сайте вы можете обнаружить, что все соединения закрываются за несколько секунд, но 100 000 обращений в день не должно быть проблемой.
database is locked
ошибка будет
В 2019 году появилось два новых варианта одновременной записи, которые еще не выпущены, но доступны в отдельных ветках.
Преимущество этого режима журнала по сравнению с обычным режимом "wal" состоит в том, что авторы могут продолжать запись в один файл wal, в то время как другой является контрольной точкой.
BEGIN CONCURRENT - ссылка на подробный документ
Усовершенствование BEGIN CONCURRENT позволяет нескольким авторам обрабатывать транзакции записи одновременно, если база данных находится в режиме «wal» или «wal2», хотя система все еще сериализует команды COMMIT.
Когда транзакция записи открывается с «BEGIN CONCURRENT», фактическая блокировка базы данных откладывается до тех пор, пока не будет выполнен COMMIT. Это означает, что любое количество транзакций, запущенных с BEGIN CONCURRENT, может выполняться одновременно. Система использует оптимистическую блокировку на уровне страниц, чтобы предотвратить фиксацию конфликтующих параллельных транзакций.
Вместе они присутствуют в begin-concurrent-wal2 или каждый в отдельной собственной ветке .
begin concurrent
.
SQLite имеет блокировку чтения-записи на уровне базы данных. Несколько соединений (возможно, принадлежащих разным процессам) могут считывать данные из одной и той же базы данных одновременно, но только одно может записывать в базу данных.
SQLite поддерживает неограниченное количество одновременных читателей, но в любой момент времени он может использовать только одного писателя. Для многих ситуаций это не проблема. Писатель в очереди. Каждое приложение выполняет свою работу с базой данных быстро и движется дальше, и никакая блокировка не длится более нескольких десятков миллисекунд. Но есть некоторые приложения, которые требуют большего параллелизма, и эти приложения, возможно, должны искать другое решение. - правильное использование для SQLite @ SQLite.org
Блокировка чтения-записи позволяет выполнять независимую обработку транзакций и реализована с использованием эксклюзивных и общих блокировок на уровне базы данных.
Исключительная блокировка должна быть получена до того, как соединение выполнит операцию записи в базу данных. После получения эксклюзивной блокировки операции чтения и записи из других подключений блокируются до тех пор, пока блокировка не будет снята снова.
SQLite имеет таблицу блокировок, которая помогает блокировать базу данных как можно позже во время операции записи, чтобы обеспечить максимальный параллелизм.
Исходное состояние UNLOCKED, и в этом состоянии соединение еще не обращалось к базе данных. Когда процесс подключен к базе данных и даже транзакция была запущена с BEGIN, соединение все еще находится в состоянии UNLOCKED.
После состояния UNLOCKED следующее состояние - это состояние SHARED. Чтобы иметь возможность читать (не записывать) данные из базы данных, соединение должно сначала войти в состояние SHARED, получив блокировку SHARED. Несколько соединений могут получать и поддерживать блокировки SHARED одновременно, поэтому несколько соединений могут одновременно считывать данные из одной и той же базы данных. Но до тех пор, пока не освобождена даже одна блокировка SHARED, ни одно соединение не сможет успешно завершить запись в базу данных.
Если соединение хочет выполнить запись в базу данных, оно должно сначала получить зарезервированную блокировку.
Только одна зарезервированная блокировка может быть активной одновременно, хотя несколько блокировок SHARED могут сосуществовать с одной зарезервированной блокировкой. RESERVED отличается от PENDING тем, что новые замки SHARED могут быть получены при наличии зарезервированного замка. - Блокировка файлов и параллелизм в SQLite версии 3 @ SQLite.org
Как только соединение получает зарезервированную блокировку, оно может начать обработку операций модификации базы данных, хотя эти изменения могут быть сделаны только в буфере, а не фактически записаны на диск. Изменения, внесенные в содержимое считывания, сохраняются в буфере памяти. Когда соединение хочет отправить изменение (или транзакцию), необходимо обновить зарезервированную блокировку до ИСКЛЮЧИТЕЛЬНОЙ блокировки. Для того, чтобы получить замок, вы должны сначала поднять замок в ожидании блокировки.
Блокировка PENDING означает, что процесс, удерживающий блокировку, хочет выполнить запись в базу данных как можно скорее и просто ожидает от всех текущих блокировок SHARED, чтобы очистить ее, чтобы получить блокировку EXCLUSIVE. Новые блокировки SHARED не разрешены для базы данных, если активна блокировка PENDING, хотя существующие блокировки SHARED могут продолжаться.
ЭКСКЛЮЗИВНАЯ блокировка необходима для записи в файл базы данных. Для файла допускается только одна ИСКЛЮЧИТЕЛЬНАЯ блокировка, и никакие другие блокировки не могут сосуществовать с ИСКЛЮЧИТЕЛЬНОЙ блокировкой. Чтобы максимизировать параллелизм, SQLite работает, чтобы минимизировать время, в течение которого удерживаются ИСКЛЮЧИТЕЛЬНЫЕ блокировки. - Блокировка файлов и параллелизм в SQLite версии 3 @ SQLite.org
Таким образом, вы можете сказать, что SQLite безопасно обрабатывает одновременный доступ нескольких процессов, записывающих в одну и ту же БД, просто потому, что он не поддерживает его! Вы получите SQLITE_BUSY
или SQLITE_LOCKED
для второго автора, когда он достигнет ограничения повторных попыток.
Этот поток старый, но я думаю, что было бы хорошо поделиться результатами моих тестов, выполненных на sqlite: я выполнил 2 экземпляра программы на python (разные процессы одной и той же программы), выполнив операторы SELECT и UPDATE sql в транзакциях с блокировкой EXCLUSIVE и таймаутом, установленным в 10 секунд, чтобы получить блокировку, и результат был разочаровывающим. Каждый экземпляр делал в шаге 10000:
Даже если sqlite предоставил монопольную блокировку транзакции, общее количество реально выполненных циклов было не равно 20 000, а меньше (общее количество итераций по одному счетчику для обоих процессов). Программа на Python почти не выдает ни одного исключения (только один раз во время выбора для 20 выполнений). Редакция sqlite на момент тестирования составляла 3.6.20, а python v3.3 - CentOS 6.5. По моему мнению, лучше найти более надежный продукт для такой работы или ограничить записи в sqlite одним уникальным процессом / потоком.
with con
достаточно.