Как выполнить запрос SQLite с помощью устройства чтения данных без блокировки базы данных?

Я использую систему.Данные.Sqlite для доступа к базе данных SQLite в C#. У меня есть запрос, который должен читать строки в таблице. При итерации по строкам и при открытом считывателе необходимо выполнить определенные обновления SQL. Я сталкиваюсь с исключением "база данных заблокирована".

на документация SQLite гласит:

когда процесс хочет читать из файла базы данных, он следует следующей последовательности шагов:

  1. откройте файл базы данных и получите общую блокировку.

в документации далее говорится о" общей " блокировке:

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

в часто задаваемые вопросы гласит:

несколько процессов могут одновременно открывать одну и ту же базу данных. Несколько процессов могут делать выбор одновременно. Однако только один процесс может вносить изменения в базу данных в любой момент времени.

книги окончательное руководство по SQLite гласит:

...соединение может выбрать иметь read-uncommitted уровень изоляции с помощью read_uncommited ПРАГМА. Если установлено значение правда, тогда соединение не будет помещать блокировки чтения в таблицы, которые оно читает. Таким образом, другой писатель может фактически изменить таблицу, так как соединение в режиме чтения не может ни блокировать, ни быть заблокировано другими соединениями.

Я попытался установить pragma для чтения uncommitted в инструкции команды SQL query следующим образом:

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

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

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

обновление:

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

2 ответов


использовать WAL режим.


Я не смог заставить это работать с помощью поставщика данных с открытым исходным кодом от здесь. Тем не менее, я смог заставить это работать, используя бесплатную стандартную версию dotConnect следующим образом:

создайте приведенный ниже импорт DLL, чтобы мы могли включить общий кэш для SQLite.

[DllImport("sqlite3.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int sqlite3_enable_shared_cache(int enable);

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

sqlite3_enable_shared_cache(1);

затем префикс оператора SQL query, используемого устройством чтения данных, с оператором pragma следующим образом:

PRAGMA read_uncommitted = 1;
SELECT Column1, Column2 FROM MyTable

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

обновление:

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

Devart.Data.SQLite.SQLiteConnection.EnableSharedCache();

можно настроить незафиксированные чтения в строку подключения, например, следующим образом:

Devart.Data.SQLite.SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
builder.ReadUncommitted = true;
builder.DateTimeFormat = Devart.Data.SQLite.SQLiteDateFormats.Ticks;
builder.DataSource = DatabaseFilePath;
builder.DefaultCommandTimeout = 300;
builder.MinPoolSize = 0;
builder.MaxPoolSize = 100;
builder.Pooling = true;
builder.FailIfMissing = false;
builder.LegacyFileFormat = false;
builder.JournalMode = JournalMode.Default;
string connectionString = builder.ToString();