C#: SQLite数据库始终被锁定

4
我编写了一个简单的应用程序(称其为app1),它读取SQLite数据库并在网格视图中显示内容。我有一个单独的C#控制台应用程序(app2),需要写入相同的数据库。问题是,app2会出现“数据库已锁定”的错误。我发现只要启动app1就会创建一个userdb-journal文件。我认为问题是app1打开了数据库但没有释放它?这是我在app1中为填充绑定到网格的表所拥有的代码。
    public DataTable GetAllPeople()
    {
        var connectionString = "Data Source=" + dbPath + ";Version=3";

        using (SQLiteDataAdapter sqlDataAdapter =
            new SQLiteDataAdapter("SELECT id,FirstName,LastName,Address FROM Users",
                                  connectionString))
        {
            using (DataTable dataTable = new DataTable())
            {
                sqlDataAdapter.Fill(dataTable);
                // code to add some new columns here

                return dataTable;
            }
        }
    }

这是填充网格视图的代码:

    private void Form1_Load(object sender, EventArgs e)
    {
        UserDatabase db = new UserDatabase();
        db.Initialize();
        dataGridView1.DataSource = db.GetAllPeople();

    }

我该如何解决让 app2 在 app1 运行时也能读写数据库的问题? 编辑 看起来日志文件只有 app2 创建。当 app1 也在运行时,我只注意到了数据库锁定错误,但 app1 可能是一个红鲱鱼。App2 是多线程的。也许我应该开一个新的问题,专注于 app2 和多线程访问? 编辑 感谢所有评论。我已经在所有数据库访问周围加了锁,并将所有内容包装在 usings 中。现在似乎一切都正常了。

SQLite日志是在写入数据库时创建的,而不是读取它。而GetAllPeople仅从数据库中读取数据,因此它可能只会短暂地获取一个共享锁,然后返回。您确定您的app1没有向数据库中写入数据吗? - vgru
你是正确的。我错了。看起来那个日志文件只有app2创建。当app1运行时,我只注意到了数据库锁定错误,但也许app1是一个误导。App2是多线程的。也许我应该发起一个新的问题,重点关注app2和多线程访问? - RogerS
@RogerS:你应该集中精力在写入数据库的部分,那是它被锁定的部分。检查配置的读取超时时间,并确保正确处理连接的释放。很可能你的代码中有一个执行耗时插入或更新操作并锁定文件时间过长的部分。 - vgru
@Groo - 我在数据库访问方面使用锁和using语句进行了优化,目前一切似乎都正常。谢谢。 - RogerS
我不确定“在所有数据库访问周围放置锁”是什么意思。然而,对我来说听起来有问题。当您进行数据库访问时,您将执行锁定,然后SQLite将进行第二次查找?这是一种不必要的性能惩罚和维护挑战。 - ravenspoint
2个回答

3
这里是代码,可以在连接字符串生成器上轻松设置参数,并根据它构建SQLiteConnection。
 SQLiteConnectionStringBuilder connBuilder = new SQLiteConnectionStringBuilder();
        connBuilder.DataSource = filePath;
        connBuilder.Version = 3;
        connBuilder.CacheSize = 4000;            
        connBuilder.DefaultTimeout = 100;
        connBuilder.Password = "mypass";


        using(SQLiteConnection conn = new SQLiteConnection(connBuilder.ToString()))
        {
            //...
        }

敬启,


1

你是否让SQLITE等待并在数据库被锁定时重试?以下是如何在C语言中实现:

 // set SQLite to wait and retry for up to 100ms if database locked
    sqlite3_busy_timeout( db, 100 );

重点是当访问数据库时,SQLITE会短暂地锁定它。如果另一个线程或进程在被阻塞时访问它,SQLITE默认会返回错误。但是你可以使用上述调用使其自动等待并再次尝试。这解决了许多此类问题。

谢谢,看起来非常有用。你知道怎么用C#实现这个吗? - RogerS
@RogerS 我不太常用C#。我的猜测是:在你的连接字符串中添加“busy_timeout=100”。 - ravenspoint
有点晚了,但以防万一有人需要:如果你正在使用 System.Data.SQLite.dll,你可以使用 SQLiteConnectionStringBuilder 类及其 DefaultTimeout 属性来定义超时。 - D.Rosado
2
@D.Rosado,我建议您发布一个带有一些示例代码的答案,展示如何完成此操作。谁知道呢,楼主可能会回来并接受您的答案。 - ravenspoint

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