问题
So I am having an interesting issue with System.Data.SQLite and using multiple transactions. Basically I have the following code which fails:
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();
}
From what I've read it seems that System.Data.SQLite should support nested and by extension sequential transactions. The code fails on line 7 (where the second transaction is declared) with the following exception:
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()
Does anyone know what the issue is or how to get around this? I feel having concurrent transactions is essential for any database system so there must be some way to do this.
Thanks!
回答1:
OP is initiating transactions on 2 connections, that's where problems start, not multiple transactions per se.
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 is designed as a lightweight database for things like the bookmarks in a browser, or the photos in a photo catalog program. SQLite has a very granular locking system which is optimised for scenarios with either many readers or a single reader / writer thread. As such it is known to have performance problems with concurrent writes - it is simply not designed for use in applications with many concurent writers. You can do concurrent writes, but it won't scale well.
In this case the problem is because you are attempting to make concurrent schema changes - if you instead did multiple SELECT
or multiple INSERT
statements then this will work successfully (as sixfeetsix's answer shows).
If your application has many readers and not many writers then SQLite may well be fine, however if you have an application with many concurrent readers and writers then you may find that a fully fledged database server is more suitable.
See File Locking And Concurrency In SQLite Version 3 for more detail.
回答3:
Try to use:
((SQLiteConnection)connection).BeginTransaction(true)-
The bool parameter tells about the deferredLock. But, remember the ExecuteNonScalar should be called only after the first transaction is commited.
回答4:
For what it's worth, not supporting multiple transactions per single connection seems to be common for data providers (I know for sure ODP.NET, probably others).
One way to work around it is to have a separate IDbConnection
for each separate IDbTransaction
.
-- EDIT ---
Doh! I just realized you do have multiple connections. Sorry.
回答5:
SQLite does not support multiple transaction - it locks the complete database when in a transaction (see www.sqlite.org).
EDIT: Multiple transactions are supported but not when using DDL in more than one transaction.
来源:https://stackoverflow.com/questions/6749270/system-data-sqlite-not-supporting-multiple-transactions