SQLite Параллельный доступ - PullRequest
       3

SQLite Параллельный доступ

153 голосов
/ 31 октября 2010

Безопасно ли SQLite3 обрабатывает одновременный доступ нескольких процессов чтения / записи из одной и той же БД?Есть ли какие-либо исключения для платформы?

Ответы [ 7 ]

100 голосов
/ 31 октября 2010

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

Для большинства приложений для настольных ПК / ноутбуков / планшетов / телефонов SQLite достаточно быстр, поскольку не хватает параллелизма. (Firefox широко использует SQLite для закладок, истории и т. Д.)

Что касается серверных приложений, кто-то некоторое время назад сказал, что все, что меньше 100К просмотров страниц в день, может прекрасно обрабатываться базой данных SQLite в типичных сценариях (например, в блогах, на форумах), и мне еще предстоит увидеть какие-либо доказательства обратного , Фактически, с современными дисками и процессорами, 95% веб-сайтов и веб-сервисов будут прекрасно работать с SQLite.

Если вы хотите действительно быстрый доступ для чтения / записи, используйте базу данных SQLite в памяти. Оперативная память на несколько порядков быстрее дисковой.

36 голосов
/ 31 октября 2010

Да, SQLite хорошо справляется с параллелизмом, но он не самый лучший с точки зрения производительности.Из того, что я могу сказать, нет никаких исключений.Подробности на сайте SQLite: https://www.sqlite.org/lockingv3.html

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

28 голосов
/ 28 июня 2014

Кажется, никто не упомянул режим WAL (Write Ahead Log). Убедитесь, что транзакции правильно организованы и при включенном режиме WAL нет необходимости держать базу данных заблокированной, пока люди читают что-то во время обновления.

Единственная проблема заключается в том, что в какой-то момент WAL необходимо повторно включить в основную базу данных, и это происходит при закрытии последнего соединения с базой данных. На очень загруженном сайте может потребоваться несколько секунд, чтобы все соединения были закрыты, но 100 тыс. Обращений в день не должно быть проблемой.

20 голосов
/ 27 января 2018

Да, это так. Давайте выясним, почему

SQLite - транзакционный

Все изменения внутри одной транзакции в SQLite либо происходят полностью или не совсем

Такая поддержка ACID, а также одновременное чтение / запись предоставляются двумя способами - с использованием так называемого журналирования (назовем это « old way ») или запись с опережением записи (назовем это « по-новому »)

Журналирование (Old Way)

В этом режиме SQLite использует DATABASE-LEVEL блокировка . Это важный момент для понимания.

Это означает, что всякий раз, когда ему нужно что-то прочитать / записать, он сначала получает блокировку для файла базы данных ENTIRE . Несколько читателей могут сосуществовать и читать что-то параллельно

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

Вот почему здесь говорят, что SQlite реализует сериализуемые транзакции

Смутное

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

Rollbacks / Отключение

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

Запись в режиме записи вперед или WAL (новый способ)

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

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

Предостережения

SQlite сильно зависит от базовой функциональности блокировки файловой системы, поэтому его следует использовать с осторожностью, более подробную информацию здесь

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

9 голосов
/ 01 февраля 2019

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

"PRAGMA journal_mode = wal2"

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

НАЧАТЬ КОНКУРЕНТ - ссылка на подробный документ

Усовершенствование BEGIN CONCURRENT позволяет нескольким записчикам обрабатывать транзакции записи одновременно, если база данных находится в режиме "wal" или "wal2", хотя система все еще работаетсериализует команды COMMIT.

Когда транзакция записи открывается с помощью «BEGIN CONCURRENT», фактическая блокировка базы данных откладывается до выполнения COMMIT.Это означает, что любое количество транзакций, запущенных с BEGIN CONCURRENT, может выполняться одновременно.Система использует оптимистическую блокировку на уровне страниц для предотвращения фиксации конфликтующих параллельных транзакций.

Вместе они присутствуют в begin-concurrent-wal2 или в каждой отдельной филиал .

6 голосов
/ 30 января 2018

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

Это ясно в DOC.

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

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

Реализовать детали для экземпляра два, напишите:

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

Исходное состояние «UNLOCKED», и в этом состоянии соединение еще не имеет доступа к базе данных. Когда база данных подключена к базе данных и даже транзакция была запущена с BEGIN, соединение все еще находится в состоянии «UNLOCKED».

Следующим состоянием разблокированного состояния является состояние SHARED. Чтобы иметь возможность считывать (не записывать) данные из базы данных, соединение должно сначала войти в состояние SHARED, то есть сначала получить блокировку SHARED. Несколько соединений могут одновременно получать и поддерживать блокировки SHARED, то есть несколько соединений могут одновременно считывать данные из одной и той же базы данных. Но даже если не была снята только одна блокировка SHARED, она не позволяет любому соединению записывать базу данных.

Если соединение хочет записать базу данных, оно должно сначала получить зарезервированную блокировку.

Одновременно может быть активна только одна зарезервированная блокировка, хотя несколько блокировок SHARED могут сосуществовать с одной зарезервированной блокировкой. RESERVED отличается от PENDING тем, что новые замки SHARED могут быть получены при наличии зарезервированного замка.

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

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

ЭКСКЛЮЗИВНАЯ блокировка необходима для записи в файл базы данных. Только одна ИСКЛЮЧИТЕЛЬНАЯ блокировка разрешена для файла, и никакие другие блокировки любого рода не могут сосуществовать с ИСКЛЮЧИТЕЛЬНОЙ блокировкой. Чтобы максимизировать параллелизм, SQLite работает, чтобы минимизировать время, в течение которого удерживаются ИСКЛЮЧИТЕЛЬНЫЕ блокировки.

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

5 голосов
/ 22 января 2014

Эта ветка старая, но я думаю, что было бы неплохо поделиться результатами моих тестов, выполненных на sqlite: Я запустил 2 экземпляра программы Python (разные процессы одной и той же программы), выполняющих команды SELECT и UPDATE sql в транзакции с блокировкой EXCLUSIVE и тайм-аутом, установленным на 10 секунд, чтобы получить блокировку, и результат разочаровывает. Каждый экземпляр делал в шаговом цикле 10000:

  • подключение к БД с эксклюзивным замком
  • выберите в одной строке, чтобы прочитать счетчик
  • обновить строку новым значением, равным счетчику, увеличенному на 1
  • закрыть соединение с БД

Даже если sqlite предоставил эксклюзивную блокировку для транзакции, общее количество реально выполненных циклов было не равно 20 000, а меньше (общее число итераций по одному счетчику для обоих процессов). Программа на Python почти не выдает ни одного исключения (только один раз во время выбора для 20 исполнений). Редакция sqlite на момент тестирования составляла 3.6.20, а python v3.3 - CentOS 6.5. По моему мнению, лучше найти более надежный продукт для такой работы или ограничить записи в sqlite одним уникальным процессом / потоком.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...