【问题标题】:Mono SQLite fails to open databaseMono SQLite 无法打开数据库
【发布时间】:2012-11-26 19:58:37
【问题描述】:

我编写了一个简单的 Mono C# 应用程序,用于使用 Mono.Data.Sqlite 包中的 Mono 实现写入 SQLite 数据库:

using System;
using Mono.Data.Sqlite;

class MainClass
{
    public static void Main (string[] args)
    {
        using (var dbConnection = new SqliteConnection (@"Data Source=/var/log/gmblog;Version=3;"))
        {
            dbConnection.Open();

            string sql = @"INSERT INTO ""queue"" (""data"") VALUES(""Test"")";
            using (var insertCommand = new SqliteCommand (sql, dbConnection))
            {
                insertCommand.ExecuteNonQuery();
            }
        }
    }
}

这工作正常,直到我从另一个应用程序(例如 sqlite3)插入并保持该应用程序运行:

sqlite> insert into queue ("data") VALUES("test2");

现在 C# 程序挂起,直到出现以下错误:

Unhandled Exception: Mono.Data.Sqlite.SqliteException: The database file is locked

我从sqlite3 的其他实例或我创建的 C++ 应用程序写入表没有任何问题。

如果我关闭 sqlite3 实例,那么 C# 应用程序将再次运行。

执行lsof /var/log/gmblog 表明sqlite3 在执行INSERT 后获得了读者锁:

sqlite3 15578  cup    3ur  REG   8,17    13312 4988505 /var/log/gmblog

在 INSERT 之前它没有这个锁:

sqlite3 15578  cup    3u   REG   8,17    13312 4988505 /var/log/gmblog

但正如我指出的,当其他应用程序正在使用数据库时,其他应用程序不会有任何问题写入表。

对我的 C# 代码有什么问题有任何想法吗?它是 SQLite 的 Mono 实现中的错误吗?

25/11 更新

请注意,导致数据库锁定错误的是dbConnection.Open();,而不是insertCommand.ExecuteNonQuery();。 IE。以下代码也不起作用:

using System;
using Mono.Data.Sqlite;

class MainClass
{
    public static void Main (string[] args)
    {
        using (var dbConnection = new SqliteConnection (@"Data Source=/var/log/gmblog;Version=3;"))
        {
            dbConnection.Open();
        }
    }
}

【问题讨论】:

  • 如果您执行“提交事务”会发生什么;在插入后的sqlite3中? sqlite3 中的未完成锁定将阻止您的应用。
  • 我在尝试提交时收到以下错误:sqlite> commit transaction; Error: cannot commit - no transaction is active
  • 我很确定问题不是活动事务,因为其他 sqlite3 实例能够进行写入就好了。

标签: c# sqlite mono locking


【解决方案1】:

请参阅SQLiteConnection documentation:“如果 SQLiteConnection 超出范围,则不会关闭。因此,您必须通过调用 Close 显式关闭连接。”。该示例显示连接在 finally 块中关闭。我在您的代码示例中看不到您关闭连接的任何地方。

您还应该能够使用“使用”块,它会在对象超出范围时自动调用关闭:

using (var dbConnection = new SqliteConnection (@"..."))
{
    dbConnection.Open();

    string sql = @"INSERT INTO ""queue"" (""data"") VALUES(""Test"")";
    using (var insertCommand = new SqliteCommand (sql, dbConnection))
    {
        insertCommand.ExecuteNonQuery();
    }
}

这样可以确保所有内容都能及时正确地发布。

【讨论】:

  • 当然,为了简单起见,请将 SqliteConnection 的构造函数包装在 using 块中。
  • 我添加了 Close() 调用,但这并不能解决问题。在调用 ExecuteNonQuery() 期间抛出异常,即在调用 Close() 之前。
  • 同时关闭任何其他可能在数据库上有未完成锁定的应用程序/进程,然后再次尝试您的程序。您可能有以前运行应用程序时遗留下来的连接。
  • 预计其他应用程序将同时使用数据库/表(它是一个消息队列)。问题是C#实现无法获取写锁,而其他实现没有这个问题。
【解决方案2】:

我在 sqlite 的多线程网站上找到了类似的discussion。我知道您在那里不是多线程,而是试图获得写锁 - 但讨论仍然有一些有用的提示可能会有所帮助。

然后每个线程继续插入许多记录,假设是 1000。您将遇到的问题如下:一个线程将通过对文件设置锁定来控制数据库。这很好,但是当锁处于活动状态时,其余线程将继续为每次尝试的 INSERT 失败。

Solution

测试 SQLITE_BUSY,我最初没有这样做。这里有一些 用于说明解决方案的伪代码:

  while (continueTrying) {
    retval = sqlite_exec(db, sqlQuery, callback, 0, &msg);
    switch (retval) {
      case SQLITE_BUSY:
        Log("[%s] SQLITE_BUSY: sleeping fow a while...", threadName);
        sleep a bit... (use something like sleep(), for example)
        break;
      case SQLITE_OK:
        continueTrying = NO; // We're done
        break;
      default:
        Log("[%s] Can't execute \"%s\": %s\n", threadName, sqlQuery, msg);
        continueTrying = NO;
        break;
    }
  }

您可能还想尝试连接字符串上的busy_timeout参数,如herehere所示。

busy_timeout 参数被实现为调用 sqlite(3)_busy_timeout。默认值为0,表示抛出一个 如果数据库被锁定,则立即出现 SqliteBusyException。

【讨论】:

  • 我试图将 .Open() 放入一个循环中,但没有运气。 code.google.com/p/csharp-sqlite 具有您上面提到的 C++ 接口的一对一实现。进一步看这个库,它似乎不太支持linux中的锁定。
猜你喜欢
  • 1970-01-01
  • 2015-07-18
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 2014-11-11
  • 2014-08-30
  • 2015-06-22
  • 2019-12-03
相关资源
最近更新 更多