System.Data.SQLite不支持多个事务

15

我正在使用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
非常仔细地查看您的代码...在第一个using语句中,您新建了command1,但是您引用了command - JonH
5个回答

16

OP正在两个连接上启动事务,这就是问题的起源,而不是多个事务本身。

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());

4

SQLite被设计为轻量级数据库,用于浏览器中的书签或照片目录程序中的照片等。SQLite具有非常细粒度的锁定系统,适用于具有许多读者或单个读者/写者线程的场景。因此,它在并发写入方面已知存在性能问题 - 它根本不是为具有许多并发写入者的应用程序而设计的。您可以进行并发写入,但它不会很好地扩展。

在这种情况下,问题在于您试图进行并发模式更改 - 如果您改为执行多个SELECT或多个INSERT语句,则此操作将成功(正如sixfeetsix的答案所示)。

如果您的应用程序有许多读者而没有多少编写者,则SQLite可能很好,但是如果您有一个具有许多并发读者和编写者的应用程序,则可能会发现完整的数据库服务器更合适。

有关更多详细信息,请参见SQLite版本3中的文件锁定和并发性


第三个回答就像那样… SQLite 确实支持多个事务。 - user610650
1
是的,我有点收回之前说的话,因为现在我更好地理解了你的观点,但我的看法仅仅是,如果我把自己放在OP的位置上,我会更喜欢社区直接解决我的问题,即坚持告诉我多个写连接是问题所在,然后额外谈论一下为什么坚持只使用一个事务可能更好。 - user610650
+1,因为这正是我想做的——多个连接,每个连接都有一个单独的事务保存到数据库中——但SQLite不支持这种操作。 - Katie Kilian

1

尝试使用:

((SQLiteConnection)connection).BeginTransaction(true)-

布尔参数告诉我们关于deferredLock。但是,请记住只有在第一次事务提交后才应调用ExecuteNonScalar。

-1

就我所知,许多数据提供程序(我确定包括ODP.NET,可能还有其他的)都不支持在单个连接上进行多个事务。

解决方法之一是为每个独立的使用单独的。

--编辑---

哎呀!我刚才意识到你确实有多个连接。对不起。


1
SQLite支持多个事务。 - user610650

-1

SQLite不支持多个事务 - 当处于事务中时,它会锁定整个数据库(请参见www.sqlite.org)。

编辑: 支持多个事务,但在一个以上的事务中使用DDL时不支持。


当你比较Oracle和SQLite中的多个事务支持时,你会发现SQLite只是“模拟”它的支持...在你的例子和原始问题之间有一个区别,你使用DML而他在两个事务中都使用DDL...我相应地调整了我的答案。 - Yahia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接