Подтвердить что ты не робот

Как использовать SQLite в многопоточном приложении?

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

Мой прецедент: в базе данных есть одна таблица, позвольте ей "A", которая имеет разные группы строк (на основе одного из своих столбцов). У меня есть "основной поток" приложения, который читает содержимое из таблицы A. Кроме того, я иногда решает обновить определенную группу строк. Для этого я хочу создать новый поток, удалить все строки группы и повторно вставить их (это единственный способ сделать это в контексте моего приложения). Это может произойти с разными группами одновременно, поэтому у меня могут быть 2+ темы, пытающиеся обновить базу данных.

Я использую разные транзакции из каждого потока, I.E. в начале каждого цикла обновления потока у меня есть начало. Фактически, каждый поток фактически выполняет вызов "BEGIN", удаляет из базы данных все строки, необходимые для "обновления", и вставляет их снова с новыми значениями (это так, как это должно быть сделано в контексте моего приложение).

Теперь я пытаюсь понять, как я это делаю. Я пробовал читать (другие ответы на Stack Overflow, сайт SQLite), но я не нашел ответы на все вопросы. Вот некоторые вещи, о которых мне интересно:

  • Нужно ли мне "открывать" и создавать новую структуру sqlite из каждого потока?
  • Нужно ли мне добавлять какой-либо специальный код для всего этого или достаточно, чтобы порождать разные потоки, обновлять строки, и это прекрасно (поскольку я использую разные транзакции)?
  • Я видел что-то, говоря о различных типах блокировок, и о том, что я мог бы получить "SQLite занят" от вызова определенных API, но, честно говоря, я не видел ссылок, которые полностью объяснялись, когда мне нужно было все это в учетную запись. Нужно ли мне?

Если кто-то может ответить на вопросы/указать мне в сторону хорошего ресурса, я был бы очень благодарен.

ОБНОВЛЕНИЕ 1: Из всего, что я читал до сих пор, похоже, что у вас не может быть двух потоков, которые все равно будут записывать в файл базы данных.

Смотрите: http://www.sqlite.org/lockingv3.html. В разделе 3.0: ЗАБРОНИРОВАННАЯ блокировка означает, что процесс планирует записать в файл базы данных в какой-то момент в будущем, но в настоящее время он просто считывает из файла. За один раз может быть активен только один замок RESERVED, хотя несколько замков SHARED могут сосуществовать с одной блокировкой RESERVED.

Означает ли это, что я могу только порождать один поток, чтобы обновлять группу строк каждый раз? У меня есть какой-то поток poller, который решает, что мне нужно обновить некоторые из строк, а затем создает новый поток для этого, но не более одного за раз? Так как это похоже на то, что любой другой поток, который я создаю, просто получит SQLITE_BUSY до тех пор, пока не закончится первый поток.

Я правильно понял вещи?

Кстати, спасибо за ответы до сих пор, они очень помогли.

4b9b3361

Ответ 1

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

  • Убедитесь, что вы компилируете SQLite с -DTHREADSAFE = 1.

  • Убедитесь, что каждый поток открывает файл базы данных и сохраняет свою собственную структуру sqlite.

  • Убедитесь, что вы обрабатываете вероятную возможность столкновения одного или нескольких потоков при одновременном доступе к файлу db: соответствующим образом обрабатывайте SQLITE_BUSY.

  • Убедитесь, что вы заключили в транзакции команды, которые изменяют файл базы данных, например INSERT, UPDATE, DELETE и другие.

Ответ 2

Некоторые шаги при запуске с SQLlite для многопоточного использования:

  • Убедитесь, что sqlite скомпилирован с помощью многопоточного флага.
  • Вы должны открыть open в своем файле sqlite, чтобы создать соединение в каждом потоке, не разделять соединения между потоками.
  • SQLite имеет очень консервативную модель потоков, когда вы выполняете операцию записи, которая включает в себя открытие транзакций, которые собираются выполнить INSERT/UPDATE/DELETE, другие потоки будут заблокированы до завершения этой операции.
  • Если вы не используете транзакцию, транзакции неявны, поэтому, если вы запустите INSERT/DELETE/UPDATE, sqlite попытается получить эксклюзивную блокировку и завершить операцию до ее выпуска.
  • Если вы выполняете оператор BEGIN EXCLUSIVE, он будет приобретать эксклюзивную блокировку перед выполнением операций в этой транзакции. COMMIT или ROLLBACK освободят блокировку.
  • Ваши sqlite3_step, sqlite3_prepare и некоторые другие вызовы могут возвращать SQLITE_BUSY или SQLITE_LOCKED. SQLITE_BUSY обычно означает, что sqlite необходимо получить блокировку. Самая большая разница между двумя возвращаемыми значениями:
    • SQLITE_LOCKED: если вы получаете это из инструкции sqlite3_step, вы ДОЛЖНЫ вызывать sqlite3_reset в дескрипторе оператора. Вы должны получить это только при первом вызове sqlite3_step, поэтому после вызова reset вы можете "повторить" ваш вызов sqlite3_step. В других операциях он аналогичен SQLITE_BUSY
    • SQLITE_BUSY: нет необходимости вызывать sqlite3_reset, просто повторите операцию после ожидания бит для блокировки, которая будет выпущена.

Ответ 3

Я понимаю, что это старый поток, и ответы хорошие, но я изучал это недавно и наткнулся на интересный анализ некоторых различных реализаций. В основном это касается сильных и слабых сторон соединения, обмена сообщениями, потоков-локальных подключений и пула соединений. Взгляните на это здесь: http://dev.yorhel.nl/doc/sqlaccess

Ответ 5

В современных версиях SQLite по умолчанию включена защита потоков. SQLITE_THREADSAFE флаг компиляции определяет, включен или нет код в SQLite, чтобы он мог безопасно работать в многопоточной среде. Значение по умолчанию SQLITE_THREADSAFE=1. Это означает Сериализованный режим. В этом режиме:

В этом режиме (который по умолчанию используется при компиляции SQLite с SQLITE_THREADSAFE = 1), библиотека SQLite сама будет сериализовать доступ к соединениям с базой данных и подготовленным операциям, чтобы приложение могло использовать одно и то же соединение с базой данных или тот же подготовленный оператор в разных потоках одновременно.

Используйте функцию sqlite3_threadsafe(), чтобы проверить флаг компиляции Sqlite library SQLITE_THREADSAFE.

Поведение безопасности нити библиотеки по умолчанию можно изменить с помощью sqlite3_config(). Используйте флажки SQLITE_OPEN_NOMUTEX и SQLITE_OPEN_FULLMUTEX в sqlite3_open_v2(), чтобы настроить режим потоковой передачи отдельных подключений к базе данных.

Ответ 6

Резюме

Транзакции в SQLite являются SERIALIZABLE.

Изменения, сделанные в одном соединении с базой данных, невидимы для всех других соединений с базой данных до фиксации.

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

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

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

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


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

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