System.Data.SQLite не поддерживает несколько транзакций
Итак, у меня есть интересная проблема с System.Data.SQLite и использование нескольких транзакций. В основном у меня есть следующий код, который не выполняется:
using (IDbConnection connection1 = new SQLiteConnection("connectionstring"), connection2 = new SQLiteConnection("connectionstring"))
{
connection1.Open();
connection2.Open();
IDbTransaction transaction1 = connection1.BeginTransaction();
IDbTransaction transaction2 = connection2.BeginTransaction(); // Fails!
using(IDbCommand command = new SQLiteCommand())
{
command.Text = "CREATE TABLE artist(artistid int, artistname text);";
command.CommandType = CommandType.Text;
command.Connection = connection1;
command.ExecuteNonQuery();
}
using (IDbCommand command = new SQLiteCommand())
{
command.Text = "CREATE TABLE track(trackid int, trackname text);";
command.CommandType = CommandType.Text;
command.Connection = connection2;
command.ExecuteNonQuery();
}
transaction1.Commit();
transaction2.Commit();
}
Из того, что я прочитал, кажется, что System.Data.SQLite должен поддерживать вложенные и последовательные транзакции расширения. Код не работает в строке 7 (где объявлена вторая транзакция) со следующим исключением:
System.Data.SQLite.SQLiteException: The database file is locked
System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
System.Data.SQLite.SQLiteDataReader.NextResult()
System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock)
System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction()
Кто-нибудь знает, в чем проблема или как обойти это? Я чувствую, что параллельные транзакции необходимы для любой системы баз данных, поэтому должен быть какой-то способ сделать это.
Спасибо!
Ответы
Ответ 1
OP инициирует транзакции по 2 соединениям, где возникают проблемы, а не несколько транзакций как таковые.
SQLiteConnection conn = new SQLiteConnection("data source=:memory:");
conn.Open();
var command = conn.CreateCommand();
command.CommandText = "create table a (b integer primary key autoincrement, c text)";
command.ExecuteNonQuery();
var tran1 = conn.BeginTransaction();
var tran2 = conn.BeginTransaction();
var command1 = conn.CreateCommand();
var command2 = conn.CreateCommand();
command1.Transaction = tran1;
command2.Transaction = tran2;
command1.CommandText = "insert into a VALUES (NULL, 'bla1')";
command2.CommandText = "insert into a VALUES (NULL, 'bla2')";
command1.ExecuteNonQuery();
command2.ExecuteNonQuery();
tran1.Commit();
tran2.Commit();
command.CommandText = "select count(*) from a";
Console.WriteLine(command.ExecuteScalar());
Ответ 2
SQLite разработан как легкая база данных для таких вещей, как закладки в браузере, или фотографии в программе каталога фотографий. SQLite имеет очень гранулированную систему блокировки, которая оптимизирована для сценариев с множеством считывателей или с одним потоком чтения/записи. Как известно, у него есть проблемы с производительностью при одновременной записи - он просто не предназначен для использования в приложениях со многими авторскими писателями. Вы можете выполнять одновременную запись, но она не будет хорошо масштабироваться.
В этом случае проблема заключается в том, что вы пытаетесь сделать параллельные изменения схемы - если вместо этого вы выполняли несколько SELECT
или несколько операторов INSERT
, то это будет работать успешно (как показано на экране с шестью ответами).
Если у вашего приложения много читателей и не много писателей, тогда SQLite вполне может быть в порядке, однако если у вас есть приложение со многими параллельными читателями и писателями, вы можете обнаружить, что полноценный сервер базы данных более подходящим.
Подробнее см. Блокировка файлов и Concurrency В SQLite версии 3.
Ответ 3
Попробуйте использовать:
((SQLiteConnection)connection).BeginTransaction(true)-
Параметр bool сообщает об отложенномлоке. Но, помните, ExecuteNonScalar следует вызывать только после совершения первой транзакции.
Ответ 4
Для того, что стоит, не поддерживая несколько транзакций на одно соединение, похоже, является общим для поставщиков данных (я точно знаю ODP.NET, возможно, другие).
Один из способов обойти это - иметь отдельный IDbConnection
для каждого отдельного IDbTransaction
.
- EDIT ---
Doh! Я просто понял, что у вас do есть несколько соединений. К сожалению.
Ответ 5
SQLite не поддерживает несколько транзакций - он блокирует полную базу данных при транзакции (см. www.sqlite.org).
EDIT:
Поддерживается несколько транзакций, но не при использовании DDL в нескольких транзакциях.