通过System.Data.SQLite和c#多次访问单个SQLite数据库文件

bai*_*rog 7 c# database sqlite locking system.data.sqlite

正如我可以从SQLite FAQ中读到的,它支持多个进程读取(SELECT),并且在任何时刻都只支持一个进程写入(INSERT,UPDATE,DELETE)数据库:

SQLite使用读取器/写入器锁来控制对数据库的访问.当任何进程想要写入时,它必须在更新期间锁定整个数据库文件.但这通常只需要几毫秒.其他流程只是等待作者完成然后继续他们的业务

我通过c#使用System.Data.SQLite适配器.

有人可以告诉我PLZ,这个过程到底是怎么回事?

这个过程是否会自动运行,编写SQLiteCommand只会等待另一个SQLiteCommand已经在同一个数据库上执行?

或者它可能会抛出异常?什么样的?

抱歉,但我没有找到有关此机制的信息:)

谢谢.

更新:

我发现帖子说异常会引发一个特定的错误代码

这句话是否正确?

bai*_*rog 13

我自己调查了一下:

我创建了一个示例SQLite数据库c:\123.db,其中一个表Categories包含两个字段:ID(uniqueidentifier)和Name(nvarchar).

然后我编写了一些多线程代码来模拟对数据库的多次写访问(System.Data.SQLite如果使用此代码,请不要忘记添加对项目的引用):

using System;
using System.Data.SQLite;
using System.Threading.Tasks;

namespace SQLiteTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var tasks = new Task[100];

            for (int i = 0; i < 100; i++)
            {
                tasks[i] = new Task(new Program().WriteToDB);
                tasks[i].Start();
            }

            foreach (var task in tasks)
                task.Wait();
        }

        public void WriteToDB()
        {
            try
            {
                using (SQLiteConnection myconnection = new SQLiteConnection(@"Data Source=c:\123.db"))
                {
                    myconnection.Open();
                    using (SQLiteTransaction mytransaction = myconnection.BeginTransaction())
                    {
                        using (SQLiteCommand mycommand = new SQLiteCommand(myconnection))
                        {
                            Guid id = Guid.NewGuid();

                            mycommand.CommandText = "INSERT INTO Categories(ID, Name) VALUES ('" + id.ToString() + "', '111')";
                            mycommand.ExecuteNonQuery();

                            mycommand.CommandText = "UPDATE Categories SET Name='222' WHERE ID='" + id.ToString() + "'";
                            mycommand.ExecuteNonQuery();

                            mycommand.CommandText = "DELETE FROM Categories WHERE ID='" + id.ToString() + "'";
                            mycommand.ExecuteNonQuery();
                        }
                        mytransaction.Commit();
                    }
                }
            }
            catch (SQLiteException ex)
            {
                if (ex.ReturnCode == SQLiteErrorCode.Busy)
                    Console.WriteLine("Database is locked by another process!");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的Core2Duo E7500的结果是永远不会出现异常!

看起来SQLite已根据我的需求进行了优化(锁定/解锁非常快,通常只需几毫秒,如SQLite FAQ告诉我们的那样) - 太棒了!

请注意,不需要检索整数ErrorCode SQLiteException- 您可以使用特殊的枚举ReturnCode字段.这里描述所有代码.

希望这些信息对某人有所帮助.

  • 好主意!我刚刚使用这种方法进行了测试,它确实有效 使用SQLite,多进程访问可以正常工作. (3认同)
  • 您的代码证明*单个*进程中的多个线程可以无缝访问 SQLite 数据库。您是否能够测试多个不同的进程是否可以访问同一个数据库? (2认同)
  • @JamesEby SQlite使用内部日记功能,因此当由于块状态或其他原因而导致某项失败时,它将重试直到完成。因此,这不是内部竞争,当然您的插入内容也不会丢失。实际上,如果您看到此消息,则发生错误,然后稍后重试。使用线程插入时,这种“锁定”错误很常见。除此之外,如果您要从线程中读取大量数据,则可以使用WAL日志模型,因此,写入和读取均位于单独的线程中,因此从线程中进行写入不会影响可能的数千次并发读取。 (2认同)